Skip to content

feat!: Replace FeatureStoreCore and CachingStoreWrapper with PersistentDataStore - #483

Merged
keelerm84 merged 1 commit into
feat/9.xfrom
mk/SDK-3454/persistent-data-store
Nov 4, 2026
Merged

keelerm84 merged 1 commit into
feat/9.xfrom
mk/SDK-3454/persistent-data-store

Conversation

@keelerm84

@keelerm84 keelerm84 commented Nov 4, 2026 •

Copy link
Copy Markdown
Member

A database integration now implements LaunchDarkly::Interfaces::PersistentDataStore and wraps it in LaunchDarkly::Integrations::Util::PersistentDataStoreWrapper. This matches the persistent data store design of the Go, Java, .NET and js-core SDKs, and lets an integration in its own gem use only public SDK types.

The store receives and returns SerializedItemDescriptor values. The wrapper serializes and deserializes items with the public data kinds, so a store no longer calls LaunchDarkly::Impl::Model. The store methods are init, get, get_all, upsert, initialized?, available? and stop. upsert takes the key and the serialized item, does the version check, and returns true only if it wrote the item. The wrapper treats any other result as false.

The wrapper behaves as CachingStoreWrapper did, with the cache rules of the Go SDK's wrapper: it caches misses, latches initialized?, re-reads an item into the cache after a write that the store did not apply, keeps its cache entry if that read fails, and clears the cache if init fails.

PersistentDataStore only documents the methods. A stub method in an included module hides a method that the store inherits from a superclass, so the module defines none, and a missing method raises NoMethodError.

The Redis, DynamoDB and Consul stores implement the new interface. Their on-disk format does not change. The Redis store no longer has the RedisFeatureStore facade class; Redis.new_feature_store returns the wrapper, as the DynamoDB and Consul factories do.

FeatureStoreCore and CachingStoreWrapper are removed. A custom store core must move to the new interface.


Note

Overview
Breaking refactor of how Redis, DynamoDB, and Consul persistent feature stores plug into the SDK: FeatureStoreCore / CachingStoreWrapper are removed in favor of the public LaunchDarkly::Interfaces::PersistentDataStore plus PersistentDataStoreWrapper, aligned with other LaunchDarkly SDKs.

Database cores now speak SerializedItemDescriptor (read/write serialized_item, version checks in upsert(kind, key, item)) instead of using internal Model serialize/deserialize on flag hashes. The wrapper owns serialization, deleted-item filtering, optional in-memory cache, and disable_cache for FDv2. Cache rules are tightened (e.g. cache misses, latch initialized?, clear cache on failed init, re-read store after rejected upsert).

Redis drops the RedisFeatureStore facade; Redis.new_feature_store builds core + wrapper like Consul/DynamoDB. On-disk formats are unchanged. Custom store authors must implement PersistentDataStore instead of FeatureStoreCore.

Reviewed by Cursor Bugbot for commit a8750a4. Bugbot is set up for automated code reviews on this repo. Configure here.

…ntDataStore

A database integration now implements LaunchDarkly::Interfaces::PersistentDataStore and wraps it in LaunchDarkly::Integrations::Util::PersistentDataStoreWrapper. This matches the persistent data store design of the Go, Java, .NET and js-core SDKs, and lets an integration in its own gem use only public SDK types.

The store receives and returns SerializedItemDescriptor values. The wrapper serializes and deserializes items with the public data kinds, so a store no longer calls LaunchDarkly::Impl::Model. The store methods are init, get, get_all, upsert, initialized?, available? and stop. upsert takes the key and the serialized item, does the version check, and returns true only if it wrote the item. The wrapper treats any other result as false.

The wrapper behaves as CachingStoreWrapper did, with the cache rules of the Go SDK's wrapper: it caches misses, latches initialized?, re-reads an item into the cache after a write that the store did not apply, keeps its cache entry if that read fails, and clears the cache if init fails.

PersistentDataStore only documents the methods. A stub method in an included module hides a method that the store inherits from a superclass, so the module defines none, and a missing method raises NoMethodError.

The Redis, DynamoDB and Consul stores implement the new interface. Their on-disk format does not change. The Redis store no longer has the RedisFeatureStore facade class; Redis.new_feature_store returns the wrapper, as the DynamoDB and Consul factories do.

FeatureStoreCore and CachingStoreWrapper are removed. A custom store core must move to the new interface.
@keelerm84
keelerm84 marked this pull request as ready for review November 4, 2026 20:23
@keelerm84
keelerm84 requested a review from a team as a code owner November 4, 2026 20:23
@keelerm84
keelerm84 merged commit db8e2db into feat/9.x Nov 4, 2026
12 checks passed
@keelerm84
keelerm84 deleted the mk/SDK-3454/persistent-data-store branch November 4, 2026 20:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants