Repository navigation
feat!: Replace FeatureStoreCore and CachingStoreWrapper with PersistentDataStore - #483
Merged
Merged
Conversation
…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
marked this pull request as ready for review
November 4, 2026 20:23
jsonbailey
approved these changes
Nov 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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/CachingStoreWrapperare removed in favor of the publicLaunchDarkly::Interfaces::PersistentDataStoreplusPersistentDataStoreWrapper, aligned with other LaunchDarkly SDKs.Database cores now speak
SerializedItemDescriptor(read/writeserialized_item, version checks inupsert(kind, key, item)) instead of using internalModelserialize/deserialize on flag hashes. The wrapper owns serialization, deleted-item filtering, optional in-memory cache, anddisable_cachefor FDv2. Cache rules are tightened (e.g. cache misses, latchinitialized?, clear cache on failedinit, re-read store after rejected upsert).Redis drops the
RedisFeatureStorefacade;Redis.new_feature_storebuilds core + wrapper like Consul/DynamoDB. On-disk formats are unchanged. Custom store authors must implementPersistentDataStoreinstead ofFeatureStoreCore.Reviewed by Cursor Bugbot for commit a8750a4. Bugbot is set up for automated code reviews on this repo. Configure here.