description |
---|
flagd RPC provider specification |
By default, flagd is a remote service that is accessed via grpc by a client application to retrieve feature flags. Depending on the environment, flagd therefore is usually deployed as a standalone service, e.g. as a Kubernetes Deployment, or injected as a sidecar container into the pod running the client application, as it is done in the OpenFeature Operator.
Prerequisites:
- Understanding of general provider concepts
- Proficiency in the chosen programming language (check the language isn't already covered by the existing providers)
Fundamentally, RPC providers use the evaluation schema to connect to flagd, initiate evaluation RPCs, and listen for changes in the flag definitions. In order to do this, you must generate the gRPC primitives (message types and client) using the protobuf code generation mechanisms available in your language. If you are unable to use gRPC code generation, you can also use REST (via the connect protocol) to communicate with flagd, though in this case, you will not be able to open a stream to listen for changes.
Protobuf schemas define the contract between the flagd evaluation API and a client.
Leverage the buf CLI or protoc to generate a flagd-proxy
client in the chosen technology:
Add the open-feature schema repository as a submodule
git submodule add --force https://github.com/open-feature/schemas.git
Create a buf.gen.{chosen language}.yaml
for the chosen language in schemas/protobuf
(if it doesn't already exist) using one of the other files as a template (find a plugin for the chosen language here) and create a pull request with this file.
Generate the code (this step ought to be automated in the build process for the chosen technology so that the generated code is never committed)
cd schemas/protobuf
buf generate --template buf.gen.{chosen language}.yaml
As an alternative to buf, use the .proto file directly along with whatever protoc-related tools or plugins available for your language.
Move the generated code (following convention for the chosen language) and add its location to .gitignore
Note that for the in-process provider only the schema
package will be relevant, since RPC providers communicate directly to flagd.
With the release of the v0.6.0 spec, OpenFeature now outlines a lifecycle for in-process flagd provider initialization and shutdown.
In-process flagd providers should do the following to make use of OpenFeature v0.6.0 features:
- start in a
NOT_READY
state - fetch the flag definition specified in the sync provider sources and set
state
toREADY
orERROR
in theinitialization
function- note that the SDK will automatically emit
PROVIDER_READY
/PROVIDER_ERROR
according to the termination of theinitialization
function
- note that the SDK will automatically emit
- throw an exception or terminate abnormally if a connection cannot be established during
initialization
- For gRPC based sources (i.e. flagd-proxy), attempt to restore the streaming connection to flagd-proxy (if the connection cannot be established or is broken):
- If flag definition have been retrieved previously, go into
STALE
state to indicate that flag resolution responses are based on potentially outdated Flag definition. - reconnection should be attempted with an exponential back-off, with a max-delay of
maxSyncRetryInterval
(see configuration) - reconnection should be attempted up to
maxSyncRetryDelay
times (see configuration) PROVIDER_READY
andPROVIDER_CONFIGURATION_CHANGED
should be emitted, in that order, after successful reconnection
- If flag definition have been retrieved previously, go into
- For Kubernetes sync sources, retry to retrieve the FlagConfiguration resource, using an exponential back-off strategy, with a max-delay of
maxSyncRetryInterval
(see configuration) - emit
PROVIDER_CONFIGURATION_CHANGED
event and update ruleset when aconfiguration_change
message is received on the streaming connection - close the streaming connection in the
shutdown
function
stateDiagram-v2
[*] --> NOT_READY
NOT_READY --> READY: initialize(), connection to flagd established, stream connected
NOT_READY --> ERROR: initialize(), unable to connect or establish stream(retry)
READY --> ERROR: stream or connection disconnected
READY --> READY: configuration_change (emit changed*, invalidate cache)
ERROR --> READY: reconnect successful (emit ready*, changed*, invalidate cache)
ERROR --> ERROR: maxSyncRetries reached
ERROR --> [*]: shutdown(), stream disconnected
* ready=PROVIDER_READY
, changed=PROVIDER_CONFIGURATION_CHANGED
, stale=PROVIDER_STALE
, error=PROVIDER_ERROR
flagd
has a caching strategy implementable by RPC providers that support server-to-client streaming.
flagd
sets the reason
of a flag evaluation as STATIC
when no targeting rules are configured for the flag.
A client can safely store the result of a static evaluation in its cache indefinitely (until the configuration of the flag changes, see cache invalidation).
Put simply in pseudocode:
if reason == "STATIC" {
isFlagCacheable = true
}
flagd
emits events to the server-to-client stream, among these is the configuration_change
event.
The structure of this event is as such:
{
"type": "delete", // ENUM:["delete","write","update"]
"source": "/flag-configuration.json", // the source of the flag configuration
"flagKey": "foo"
}
A client should invalidate the cache of any flag found in a configuration_change
event to prevent stale data.
If the connection drops all cache values must be cleared (any number of events may have been missed).
Expose means to configure the provider aligned with the following priority system (highest to lowest).
flowchart LR
constructor-parameters -->|highest priority| environment-variables -->|lowest priority| defaults
This takes the form of parameters to the provider's constructor, it has the highest priority.
Read environment variables with sensible defaults (before applying the values explicitly declared to the constructor).
Option name | Environment variable name | Type & Values | Default |
---|---|---|---|
host | FLAGD_HOST | String | localhost |
port | FLAGD_PORT | int | 8013 |
tls | FLAGD_TLS | boolean | false |
socketPath | FLAGD_SOCKET_PATH | String | null |
certPath | FLAGD_SERVER_CERT_PATH | String | null |
deadline | FLAGD_DEADLINE_MS | int | 500 |
cache | FLAGD_CACHE | String - lru, disabled | lru |
maxCacheSize | FLAGD_MAX_CACHE_SIZE | int | 1000 |
maxEventStreamRetries | FLAGD_MAX_EVENT_STREAM_RETRIES | int | 5 |
retryBackoffMs | FLAGD_RETRY_BACKOFF_MS | int | 1000 |
Handle flag evaluation errors by using the error constructors exported by the SDK (e.g. openfeature.NewProviderNotReadyResolutionError(ConnectionError)
), thereby allowing the SDK to parse and handle the error appropriately.
The following steps will extend the reach of the newly created provider to other developers of the chosen technology.
Create an issue in openfeature.dev here. This will ensure the provider is added to OpenFeature's website.
An RPC provider should serialize the OpenFeature context for use in the evaluation.proto
.
It should map the targeting-key into a top level property of the context, with the key "targetingKey"