Skip to content

Java provider

Installation

<dependency>
  <groupId>dev.openfeature.contrib.providers</groupId>
  <artifactId>flagd</artifactId>
  <version>0.10.0</version>
</dependency>

Configuration and Usage

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).

Remote resolver (RPC)

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);

In-process resolver

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.

Sync-metadata

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.

Offline mode

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.

Custom Connector

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: 1. Custom Connector 2. Offline file 3. gRPC

Configuration options

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 support

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

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

Deadlines are used to define how long the provider waits to complete initialization or flag evaluations. They behave differently based on the resolver type.

Deadlines with Remote resolver (RPC)

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.

Deadlines with In-process resolver

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

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.

Caching (RPC only)

[!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.

Context enrichment

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.

OpenTelemetry tracing (RPC only)

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.

Target URI Support (gRPC name resolution)

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();