Class: FeatureHub::Sdk::RedisSessionStore
- Inherits:
-
RawUpdateFeatureListener
- Object
- RawUpdateFeatureListener
- FeatureHub::Sdk::RedisSessionStore
- Includes:
- SessionStoreHelpers
- Defined in:
- lib/feature_hub/sdk/redis_session_store.rb
Overview
Persists feature values from a FeatureHubRepository in Redis so they survive process restarts and are shared across multiple processes.
Uses SHA256-based change detection and Redis WATCH/MULTI/EXEC for multi-process safety.
WARNING: Do not use with server-evaluated features. Each server-evaluated context sends different resolved values; storing them in a shared Redis key will cause processes to overwrite each other’s feature states.
On initialization the store reads any previously saved features from Redis and replays them into the repository. A periodic timer re-reads the SHA key so that updates published by other processes are picked up automatically.
Constant Summary collapse
- SOURCE =
"redis-store"
Instance Method Summary collapse
- #close ⇒ Object
- #delete_feature(feature, source) ⇒ Object
-
#initialize(connection_or_client, config, opts = nil) ⇒ RedisSessionStore
constructor
A new instance of RedisSessionStore.
- #process_update(feature, source) ⇒ Object
- #process_updates(features, source) ⇒ Object
Methods inherited from RawUpdateFeatureListener
Constructor Details
#initialize(connection_or_client, config, opts = nil) ⇒ RedisSessionStore
Returns a new instance of RedisSessionStore.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/feature_hub/sdk/redis_session_store.rb', line 45 def initialize(connection_or_client, config, opts = nil) super() = opts.is_a?(RedisSessionStoreOptions) ? opts : RedisSessionStoreOptions.new(opts) @repository = config.repository @environment_id = config.environment_id @prefix = .prefix @backoff_timeout = .backoff_timeout @retry_update_count = .retry_update_count @refresh_timeout = .refresh_timeout @internal_sha = nil @mutex = Mutex.new @task = nil @logger = .logger return unless redis_available? @redis = if connection_or_client.is_a?(String) Redis.new(url: connection_or_client, db: .db) else connection_or_client end config.register_raw_update_listener(self) @logger&.debug("[featurehubsdk] started redis store") Concurrent::Future.execute { load_from_redis } start_timer end |
Instance Method Details
#close ⇒ Object
111 112 113 114 115 116 |
# File 'lib/feature_hub/sdk/redis_session_store.rb', line 111 def close return if @task.nil? @task.shutdown @task = nil end |
#delete_feature(feature, source) ⇒ Object
102 103 104 105 106 107 108 109 |
# File 'lib/feature_hub/sdk/redis_session_store.rb', line 102 def delete_feature(feature, source) return if source == SOURCE || !redis_available? || !feature["id"] perform_store_with_retry do |redis_features| updated = redis_features.reject { |f| f["id"] == feature["id"] } updated.length < redis_features.length ? updated : nil end end |
#process_update(feature, source) ⇒ Object
91 92 93 94 95 96 97 98 99 100 |
# File 'lib/feature_hub/sdk/redis_session_store.rb', line 91 def process_update(feature, source) return if source == SOURCE || !redis_available? perform_store_with_retry do |redis_features| existing = redis_features.find { |f| f["id"] == feature["id"] } next nil if existing && version_of(existing) >= version_of(feature) merge_features(redis_features, [feature]) end end |
#process_updates(features, source) ⇒ Object
76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/feature_hub/sdk/redis_session_store.rb', line 76 def process_updates(features, source) return if source == SOURCE || !redis_available? incoming_sha = calculate_sha(features) return if incoming_sha == @redis.get(sha_key) perform_store_with_retry do |redis_features| has_newer = features.any? do |f| existing = redis_features.find { |rf| rf["id"] == f["id"] } existing.nil? || version_of(f) > version_of(existing) end has_newer ? merge_features(redis_features, features) : nil end end |