Using a persistent feature store
Read time: 4 minutes
Last edited: Mar 05, 2020
This topic explains how to use a persistent feature store to keep flag data.
In their default configuration, LaunchDarkly's server-side SDKs:
- connect to LaunchDarkly and receive feature flag data
- store the flags in memory
- update the in-memory state if LaunchDarkly sends updates
Flag evaluations always refer to the last known state in memory. This collection of last known flag data is the "feature store."
Alternatively, you can use a database for the feature store. Most of the server-side SDKs support Redis, DynamoDB, and Consul for this purpose.
Whichever database you use, there are two ways to use it:
- Exactly like the default configuration, except substituting a database for the in-memory store, or
- Using only the database as a source of flag data, without connecting to LaunchDarkly.
In this configuration, the SDK receives feature flag data from LaunchDarkly and puts it in the feature store. The only difference is that the store is in a database.
When flags are evaluated, the SDK checks the database to get the latest flag state, usually with some form of in-memory caching to improve performance.
The main reason to do this is to accelerate flag updates when your application has to restart, and after restarting, it takes longer to establish a connection to LaunchDarkly than you want. If you have a persistent feature store that has already been populated, the SDK can still evaluate flags using the last known flag state from the store until newer data is available from LaunchDarkly.
To set up this configuration, most people create some kind of object for the specific type of database and put it in the client configuration's feature store property. In PHP, this property is called the "feature requester".
An example configuration is below:
1import ld "gopkg.in/launchdarkly/go-server-sdk.v4"23store, err := examplepackage.NewExampleFeatureStore(storeOptions)45config := ld.DefaultConfig6config.FeatureStore = store7client := ld.MakeCustomClient(sdkKey, config, waitTime)
If there are multiple instances of your application configured in this way with the same database, the same data gets written to the database multiple times, because each instance receives feature flags from LaunchDarkly.
This is harmless, but it is inefficient, so you may want to use a persistent feature store without connecting to LaunchDarkly, as described below.
This is similar to the previous configuration, except that the SDK does not connect to LaunchDarkly at all. Instead, it relies on some other process which does have a LaunchDarkly connection to write the latest flag data to the database, where the SDK will then read it.
The other process could be the LaunchDarkly Relay Proxy, or any other application that creates a SDK client with the same persistent store. To learn more about the Relay Proxy, read The LaunchDarkly Relay Proxy.
Since the Relay Proxy is also known as the LaunchDarkly Daemon, some versionf of the SDKs refer to this mode as "LDD mode." Creating the client is the same as above in terms of specifying the persistent store, but you must also add an option to make the SDK not connect to LaunchDarkly.
1import ld "gopkg.in/launchdarkly/go-server-sdk.v4"23store, err := examplepackage.NewExampleFeatureStore(storeOptions)45config := ld.DefaultConfig6config.FeatureStore = store7config.UseLdd = true // <-- the extra option8client := ld.MakeCustomClient(sdkKey, config, waitTime)
All of the LaunchDarkly server-side SDKs support Redis. This feature is built into the main SDK distribution in all cases except .NET, where it requires a separate package.
The available options are slightly different in each language, but you can always specify the following:
The Redis host address (defaults to
A prefix string to add to all keys used by the store, to avoid collisions in case the database is also being used for some other purpose
The length of time that recently read or updated data should be cached in memory
In the following examples, the Redis feature store is set to use a host address of
my-redis:6379, a prefix string of
"my-key-prefix", and a cache TTL of 30 seconds.
1import (2 ld "gopkg.in/launchdarkly/go-server-sdk.v4"3 ldredis "gopkg.in/launchdarkly/go-server-sdk.v4/redis"4)567store, err := ldredis.NewRedisFeatureStoreWithDefaults(8 ldredis.HostAndPort("my-redis", 6379),9 ldredis.Prefix("my-key-prefix"),10 ldredis.CacheTTL(30 * time.Second))111213config := ld.DefaultConfig14config.FeatureStore = store15client := ld.MakeCustomClient(sdkKey, config, waitTime)
All of the current versions of the server-side SDKs except C support DynamoDB. DynamoDB is a particularly useful solution if you are running code in AWS Lambda, since it can be accessed from Lambda without needing access to any VPC resource.
In the Go SDK (as of version 4.5.1), the Ruby SDK (as of version 5.5.1), the Python SDK (as of version 6.7.0), and the PHP SDK (as of version 3.5.0), this feature is built into the main SDK distribution.
The other SDKs require an additional package:
In your application code, the only required parameter is the table name, although you can also specify any other options supported by AWS; by default, the DynamoDB driver will expect to get your AWS credentials and region from environment variables or local configuration files, as described in the AWS SDK documentation.
The table must already exist before your application starts. It must have a partition key called
"namespace", and a sort key called
"key". (The SDK does not create the table automatically because it would not know what values to use for other properties such as permissions and throughput.)
DynamoDB imposes a limit of 400KB on the total size of any database item. In this implementation, each feature flag or user segment is a single item, so the feature store will not be able to persist any flag or segment whose JSON representation is larger than that limit.
In the following examples, the DynamoDB feature store is set to use a table called
"my-table" and a cache TTL of 30 seconds. The DynamoDB feature store does support using a key prefix, as shown in the Redis examples, but it is uncommon for one DynamoDB table to be shared by multiple applications.
1import (2 ld "gopkg.in/launchdarkly/go-server-sdk.v4"3 "gopkg.in/launchdarkly/go-server-sdk.v4/lddynamodb"4)567store, err := ldredis.NewDynamoDBFeatureStore("my-table",8 lddynamodb.CacheTTL(30 * time.Second))91011config := ld.DefaultConfig12config.FeatureStore = store13client := ld.MakeCustomClient(sdkKey, config, waitTime)
All of the current versions of the server-side SDKs except C support Consul. In the Go SDK (as of version 4.5.0), the Ruby SDK (as of version 5.5.1), the Python SDK (as of version 6.8.1), and the PHP SDK (as of version 3.5.0), this feature is built into the main SDK distribution. The other SDKs require an additional package: Java, .NET, Node.js.
In the following examples, the Consul feature store is set to use a host address of
my-consul:8100, a prefix string of
"my-key-prefix", and a cache TTL of 30 seconds.
1import (2 ld "gopkg.in/launchdarkly/go-server-sdk.v4"3 "gopkg.in/launchdarkly/go-server-sdk.v4/ldconsul"4)567store, err := ldconsul.NewConsulFeatureStore(8 ldconsul.Address("http://my-consul:8100"),9 ldconsul.Prefix("my-key-prefix"),10 ldconsul.CacheTTL(30 * time.Second))111213config := ld.DefaultConfig14config.FeatureStore = store15client := ld.MakeCustomClient(sdkKey, config, waitTime)