Context-Based Flow Version Selection

A ContextBasedVersionSelector is a component whose sole function is to determine which version a specific flow should be initiated with when the caller did not specify a version on the InitiationInput. In that scenario, the generated input handling code in your Domain will call the configured ContextBasedVersionSelector with the current propagated context. If the propagated context has not been set on the input explicitly, the context from ContextPropagation will be used to select the version. If the selected version cannot be resolved to a concrete flow version, the latest flow version will be used (the default when version selection is disabled).

Among other things, the ipf-write-starter-core module comes bundled with a default implementation of ContextBasedVersionSelector. To use the default version selector, just wire a ContextBasedVersionSelector bean as a dependency into your Domain:

@Bean
public QuickStartModelDomain initialiseDomain(ActorSystem<?> actorSystem,
    // the dependency on a bean provided by ipf-write-starter-core
    ContextBasedVersionSelector versionSelector) {
    return new QuickStartModelDomain.Builder(actorSystem)
                .withVersionSelector(versionSelector)
                .build();
}

Read on to learn more about the default version selector and how to configure it.

The Default Version Selector Overview

To help it determine the version of a flow to select, the default version selector relies on a set of routing rules provided via a pluggable rule store.

Version Selection Rules

A rule is provided using the following three pieces of information:

  • the key that has to be present in the evaluated context

  • the value that the context key must have

  • the version if the key with the given value

The rules in the store can be defined on two levels:

  • per a specific flow, in which case they will apply only when selecting versions for that particular flow, or

  • globally, in which case they will apply to all flows

Rules defined on a per-flow basis take precedence over rules that are defined globally.

While the routing rules do not prohibit you from using different context keys on a global and per-flow level, using the same context key on all levels will result in more maintainable and less error-prone rules.

Rule Stores

There are two types of rule stores available out of the box:

  • HOCON store, enabled by default, allows you to specify your routing rules via configuration properties and does not allow any modification to the rules after application startup

  • Distributed Data store, which uses the Akka Distributed Data library to store and replicate the rules to all the nodes in the cluster and persist them on disk. The rules can be modified at any point and through an HTTP API. On application startup, the rules are loaded from disk and will default to the HOCON rules if no rules can be found in DData. Distributed Data store is enabled by setting ipf.behaviour.version-selection.route-store-type = ddata. Please note that doing so will require you to add the following JVM flags to your run command --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED due to Akka’s use of LMDB for persistent storage (for more details see the official docs)

Configuring the Version Selection Rules

Default Version Selection Rules

The following rule configuration is provided by default:

ipf.behaviour.version-selection {

  // The global flow routing configuration to use when DData store is disabled or empty
  global-rules = { (1)
    // The defaults use the propagated `ipf_version_selection` key to drive version selection
    key-name = ipf_version_selection (2)
    version-rules = [{ (3)
      active-from = "1970-01-01T00:00:00.00Z" (4)
      // The default rules only allow you to pick the oldest or latest versions of flows
      values-to-versions { (5)
        OLDEST = OLDEST
        LATEST = LATEST
        STABLE = OLDEST
        CANARY = LATEST
      }
    }]
  }

  // The per-flow routing configuration to use when DData store is disabled or empty
  per-flow-rules { (6)
    // Example structure your per-flow rule configuration should take:
    //"Flow" = {
    //  key-name = ipf_version_selection
    //  version-rules = [{
    //   active-from = "1970-01-01T00:00:00.00Z"
    //    // The default rules only allow you to pick the oldest or latest versions of flows
    //    values-to-versions {
    //      VALUE1 = V1
    //      VALUE2 = V2
    //    }
    //  }]
    //}
  }
}
1 The global-rules config block is effectively a single context key rule block.
2 A context key rule applies to a single key and its possible values and the key-name property defines the name of the context key which triggers the rule. By default, the global rules provide support only for ipf_version_selection key, which is an IPF-wide propagated context key. Propagated context keys will be automatically detected in transport message headers and included in the current context by receive connectors.
3 The version-rules block provides a list of mappings between a particular values the ipf_version_selection context key may take and the flow version we want to select when that value is encountered in the evaluated context.
4 Each entry in the version-rules list of mappings needs to have an activation date assigned to it — this allows future-dated rules to be added. When the same value is present in multiple mapping entries, the one with the latest active-from timestamp wins.
5 The context key value-version mappings are defined within a values-to-versions block. Since global rules are flow-agnostic, concrete flow versions will generally be missing from them but OLDEST and LATEST placeholder versions can be used. By default, whenever ipf_version_selection takes the value of OLDEST or STABLE, the oldest flow version of a particular flow will be used, whereas LATEST and CANARY will pick the latest version of a flow.
6 No per flow rules are provided out of the box, but you can tell from the example configuration that per-flow rules use the same context key structure in the form of "Flow" = <context-key-config-block>.

Providing Custom Version Selection Rules

There are two ways to customize your version selection rules:

  1. By providing configuration overrides via ipf.conf (or a .conf file higher in the hierarchy). Configuration overrides are the only way to customize HOCON rule stores and are also needed to customize the defaults for Distributed Data rule stores.

  2. By using the version selection API to modify the configuration. The version API is only available for the Distributed Data rule stores.

The API details are covered in the API documentation page and the rest of this section will be dealing with HOCON only.

Supporting a Custom Context Key in Global Rules

To support your own custom context key instead of the IPF-default ipf_version_selection key, simply overwrite the whole ipf.behaviour.version-selection.global-rules.key-name property:

ipf.behaviour.version-selection.global-rules.key-name = my_custom_context_key

You will also have to mark the new header as propagated:

ipf.context-propagation.propagated-supporting-context-keys += my_custom_context_key

Overriding the Default Global Rules

To disable the default global rules, simply provide an empty config block:

ipf.behaviour.version-selection.global-rules = {}

To use different value-version mappings in global rules, simply overwrite the version-rules block with your own mappings:

ipf.behaviour.version-selection.global-rules.version-rules = [{
  active-from = "2024-01-01T00:00:00.00Z"
  values-to-versions {
    // new value mappings
    MY_VALUE_1 = OLDEST
    MY_VALUE_2 = LATEST
    // keeps some of the old ones, too
    STABLE = OLDEST
    CANARY = LATEST
  }
}]

Adding Per-Flow Rules

ipf.behaviour.version-selection.per-flow-rules = { (1)
  "Flow" = { (2)
    key-name = ipf_version_selection (3)
    version-rules = [{ (4)
      active-from = "1970-01-01T00:00:00.00Z"
      values-to-versions {
        VALUE1 = V1 (5)
        VALUE2 = V2
      }
    }]
  }
}
1 You add your per-flow rules under the appropriately named ipf.behaviour.version-selection.per-flow-rules config block.
2 The config block consists of a "<FlowName>" = <context-key-rule-block> key-value pairs. A context key rule block applies to a single key.
3 As with the global rules, the key-name property defines the name of the context key which triggers the rule. In this example we are using the same key we use for the global rules, which is the suggested approach.
4 Again exactly like the global rules, the version-rules block provides a list of mappings between a particular values the context key may take and the flow version we want to select when that value is encountered in the evaluated context.
5 Unlike the global rules, concrete flow versions are used here but this is not surprising — being able to select a specific version of a flow is the main reason to specify per-flow rules.

Version Selection API

SwaggerUI