This provider is designed to use flagd's evaluation protocol, or locally evaluate flags defined in a flagd flag definition.
<dependency>
<groupId>dev.openfeature.contrib.providers</groupId>
<artifactId>flagd</artifactId>
<version>0.10.2</version>
</dependency>
The flagd provider can operate in two modes: RPC (evaluation takes place in flagd, via gRPC calls) or in-process (evaluation takes place in-process, with the provider getting a ruleset from a compliant sync-source).
This is the default mode of operation of the provider.
In this mode, FlagdProvider
communicates with flagd via the gRPC protocol.
Flag evaluations take place remotely at the connected flagd instance.
Instantiate a new FlagdProvider instance and configure the OpenFeature SDK to use it:
// Create a flagd instance with default options
FlagdProvider flagd = new FlagdProvider();
// Set flagd as the OpenFeature Provider
OpenFeatureAPI.getInstance().setProvider(flagd);
This mode performs flag evaluations locally (in-process). Flag configurations for evaluation are obtained via gRPC protocol using sync protobuf schema service definition.
Consider the following example to create a FlagdProvider
with in-process evaluations,
FlagdProvider flagdProvider = new FlagdProvider(
FlagdOptions.builder()
.resolverType(Config.Resolver.IN_PROCESS)
.build());
In the above example, in-process handlers attempt to connect to a sync service on address localhost:8013
to obtain flag definitions.
To support the injection of contextual data configured in flagd for in-process evaluation, the provider exposes a getSyncMetadata
accessor which provides the most recent value returned by the GetMetadata RPC.
The value is updated with every (re)connection to the sync implementation.
This can be used to enrich evaluations with such data.
If the in-process
mode is not used, and before the provider is ready, the getSyncMetadata
returns an empty map.
In-process resolvers can also work in an offline mode.
To enable this mode, you should provide a valid flag configuration file with the option offlineFlagSourcePath
.
FlagdProvider flagdProvider = new FlagdProvider(
FlagdOptions.builder()
.resolverType(Config.Resolver.IN_PROCESS)
.offlineFlagSourcePath("PATH")
.build());
Provider will attempt to detect file changes using polling. Polling happens at 5 second intervals and this is currently unconfigurable. This mode is useful for local development, tests and offline applications.
You can include a custom connector as a configuration option to customize how the in-process resolver fetches flags. The custom connector must implement the Connector interface.
Connector myCustomConnector = new MyCustomConnector();
FlagdOptions options =
FlagdOptions.builder()
.resolverType(Config.Resolver.IN_PROCESS)
.customConnector(myCustomConnector)
.build();
FlagdProvider flagdProvider = new FlagdProvider(options);
Important
Note that the in-process resolver can only use a single flag source. If multiple sources are configured then only one would be selected based on the following order of preference:
- Custom Connector
- Offline file
- gRPC
Most options can be defined in the constructor or as environment variables, with constructor options having the highest
precedence.
Default options can be overridden through a FlagdOptions
based constructor or set to be picked up from the environment
variables.
Given below are the supported configurations:
Option name | Environment variable name | Type & Values | Default | Compatible resolver |
---|---|---|---|---|
resolver | FLAGD_RESOLVER | String - rpc, in-process | rpc | |
host | FLAGD_HOST | String | localhost | rpc & in-process |
port | FLAGD_PORT | int | 8013 | rpc & in-process |
targetUri | FLAGD_TARGET_URI | string | null | rpc & in-process |
tls | FLAGD_TLS | boolean | false | rpc & in-process |
socketPath | FLAGD_SOCKET_PATH | String | null | rpc & in-process |
certPath | FLAGD_SERVER_CERT_PATH | String | null | rpc & in-process |
deadline | FLAGD_DEADLINE_MS | int | 500 | rpc & in-process |
streamDeadlineMs | FLAGD_STREAM_DEADLINE_MS | int | 600000 | rpc & in-process |
keepAliveTime | FLAGD_KEEP_ALIVE_TIME_MS | long | 0 | rpc & in-process |
selector | FLAGD_SOURCE_SELECTOR | String | null | in-process |
cache | FLAGD_CACHE | String - lru, disabled | lru | rpc |
maxCacheSize | FLAGD_MAX_CACHE_SIZE | int | 1000 | rpc |
maxEventStreamRetries | FLAGD_MAX_EVENT_STREAM_RETRIES | int | 5 | rpc |
retryBackoffMs | FLAGD_RETRY_BACKOFF_MS | int | 1000 | rpc |
offlineFlagSourcePath | FLAGD_OFFLINE_FLAG_SOURCE_PATH | String | null | in-process |
Note
Some configurations are only applicable for RPC resolver.
Unix socket communication with flagd is facilitated by usaging of the linux-native epoll
library on linux-x86_64
only (ARM support is pending the release of netty-transport-native-epoll
v5). Unix sockets are not supported on other
platforms or architectures.
Reconnection is supported by the underlying gRPC connections. If the connection to flagd is lost, it will reconnect automatically. A failure to connect will result in an error event from the provider, though it will attempt to reconnect indefinitely.
Deadlines are used to define how long the provider waits to complete initialization or flag evaluations. They behave differently based on the resolver type.
The deadline for an individual flag evaluation can be configured by calling setDeadline(myDeadlineMillis)
.
If the remote evaluation call is not completed within this deadline, the gRPC call is terminated with the error DEADLINE_EXCEEDED
and the evaluation will default.
In-process resolver with remote evaluation uses the deadline
for synchronous gRPC calls to fetch metadata from flagd as part of its initialization process.
If fetching metadata fails within this deadline, the provider will try to reconnect.
The streamDeadlineMs
defines a deadline for the streaming connection that listens to flag configuration updates from
flagd. After the deadline is exceeded, the provider closes the gRPC stream and will attempt to reconnect.
In-process resolver with offline evaluation uses the deadline
for file reads to fetch flag definitions.
If the provider cannot open and read the file within this deadline, the provider will default the evaluation.
TLS is available in situations where flagd is running on another host. You may optionally supply an X.509 certificate in PEM format. Otherwise, the default certificate store will be used.
FlagdProvider flagdProvider = new FlagdProvider(
FlagdOptions.builder()
.host("myflagdhost")
.tls(true) // use TLS
.certPath("etc/cert/ca.crt") // PEM cert
.build());
Warning
There's a vulnerability in netty, a transitive dependency of the underlying gRPC libraries used in the flagd-provider that fails to correctly validate certificates. This will be addressed in netty v5.
Note
The in-process resolver does not benefit from caching since all evaluations are done locally and do not involve I/O.
The provider attempts to establish a connection to flagd's event stream (up to 5 times by default).
If the connection is successful and caching is enabled, each flag returned with the reason STATIC
is cached until an event is received
concerning the cached flag (at which point it is removed from the cache).
On invocation of a flag evaluation (if caching is available), an attempt is made to retrieve the entry from the cache, if
found the flag is returned with the reason CACHED
.
By default, the provider is configured to use least recently used (lru) caching with up to 1000 entries.
The contextEnricher
option is a function which provides a context to be added to each evaluation.
This function runs on the initial provider connection and every reconnection, and is passed the sync-metadata.
By default, a simple implementation which uses the sync-metadata payload in its entirety is used.
flagd provider support OpenTelemetry traces for gRPC-backed remote evaluations.
There are two ways you can configure OpenTelemetry for the provider,
When using automatic instrumentation, traces for gRPC will be automatically added by the OpenTelemetry Java library. These traces, however will not include extra attributes added when using manual instrumentation.
When using manual instrumentation, you have two options to construct flagd provider to enable traces.
The first(preferred) option is to construct the provider with an OpenTelemetry instance,
FlagdOptions options =
FlagdOptions.builder()
.openTelemetry(openTelemetry)
.build();
FlagdProvider flagdProvider = new FlagdProvider(options);
The second option is useful if you have set up a GlobalOpenTelemetry in your runtime.
You can allow flagd to derive the OpenTelemetry instance by enabling withGlobalTelemetry
option.
FlagdOptions options =
FlagdOptions.builder()
.withGlobalTelemetry(true)
.build();
FlagdProvider flagdProvider = new FlagdProvider(options);
Please refer OpenTelemetry example for best practice guidelines.
Provider telemetry combined with flagd OpenTelemetry allows you to have distributed traces.
The targetUri
is meant for gRPC custom name resolution (default is dns
), this allows users to use different
resolution method e.g. xds
. Currently, we are supporting all core resolver
and one custom resolver for envoy
proxy resolution. For more details, please refer the
RFC document.
FlagdOptions options = FlagdOptions.builder()
.targetUri("envoy://localhost:9211/flag-source.service")
.resolverType(Config.Resolver.IN_PROCESS)
.build();