Skip to content

Commit c4cf616

Browse files
committed
Move private constants to datastore module
1 parent 4a2952b commit c4cf616

23 files changed

+248
-238
lines changed

lib/ldclient-rb/impl/data_source.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def init(all_data)
5252
monitor_store_update do
5353
if @flag_change_broadcaster.has_listeners?
5454
old_data = {}
55-
LaunchDarkly::ALL_KINDS.each do |kind|
55+
Impl::DataStore::ALL_KINDS.each do |kind|
5656
old_data[kind] = @data_store.all(kind)
5757
end
5858
end
@@ -153,7 +153,7 @@ def update_status(new_state, new_error)
153153
private def compute_changed_items_for_full_data_set(old_data, new_data)
154154
affected_items = Set.new
155155

156-
LaunchDarkly::ALL_KINDS.each do |kind|
156+
Impl::DataStore::ALL_KINDS.each do |kind|
157157
old_items = old_data[kind] || {}
158158
new_items = new_data[kind] || {}
159159

@@ -177,7 +177,7 @@ def update_status(new_state, new_error)
177177
#
178178
private def send_change_events(affected_items)
179179
affected_items.each do |item|
180-
if item[:kind] == LaunchDarkly::FEATURES
180+
if item[:kind] == Impl::DataStore::FEATURES
181181
@flag_change_broadcaster.broadcast(LaunchDarkly::Interfaces::FlagChange.new(item[:key]))
182182
end
183183
end

lib/ldclient-rb/impl/data_source/stream.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
require "ldclient-rb/impl/model/serialization"
2+
require "ldclient-rb/impl/util"
3+
require "ldclient-rb/in_memory_store"
24

35
require "concurrent/atomics"
46
require "json"
@@ -18,8 +20,8 @@ module DataSource
1820

1921
# @api private
2022
KEY_PATHS = {
21-
FEATURES => "/flags/",
22-
SEGMENTS => "/segments/",
23+
Impl::DataStore::FEATURES => "/flags/",
24+
Impl::DataStore::SEGMENTS => "/segments/",
2325
}
2426

2527
# @api private
@@ -55,7 +57,7 @@ def start
5557
}
5658
log_connection_started
5759

58-
uri = Util.add_payload_filter_key(@config.stream_uri + "/all", @config)
60+
uri = Impl::Util.add_payload_filter_key(@config.stream_uri + "/all", @config)
5961
@es = SSE::Client.new(uri, **opts) do |conn|
6062
conn.on_event { |event| process_message(event) }
6163
conn.on_error { |err|
@@ -142,7 +144,7 @@ def process_message(message)
142144
@ready.set
143145
elsif method == PATCH
144146
data = JSON.parse(message.data, symbolize_names: true)
145-
for kind in [FEATURES, SEGMENTS]
147+
for kind in [Impl::DataStore::FEATURES, Impl::DataStore::SEGMENTS]
146148
key = key_for_path(kind, data[:path])
147149
if key
148150
item = Impl::Model.deserialize(kind, data[:data], @config.logger)
@@ -152,7 +154,7 @@ def process_message(message)
152154
end
153155
elsif method == DELETE
154156
data = JSON.parse(message.data, symbolize_names: true)
155-
for kind in [FEATURES, SEGMENTS]
157+
for kind in [Impl::DataStore::FEATURES, Impl::DataStore::SEGMENTS]
156158
key = key_for_path(kind, data[:path])
157159
if key
158160
update_sink_or_data_store.delete(kind, key, data[:version])

lib/ldclient-rb/impl/data_store.rb

Lines changed: 14 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,26 @@
11
require 'concurrent'
22
require "ldclient-rb/interfaces"
3+
require "ldclient-rb/impl/data_store/data_kind"
34

45
module LaunchDarkly
56
module Impl
67
module DataStore
7-
class DataKind
8-
FEATURES = "features".freeze
9-
SEGMENTS = "segments".freeze
10-
11-
FEATURE_PREREQ_FN = lambda { |flag| (flag[:prerequisites] || []).map { |p| p[:key] } }.freeze
12-
13-
attr_reader :namespace
14-
attr_reader :priority
15-
16-
#
17-
# @param namespace [String]
18-
# @param priority [Integer]
19-
#
20-
def initialize(namespace:, priority:)
21-
@namespace = namespace
22-
@priority = priority
23-
end
24-
8+
# These constants denote the types of data that can be stored in the feature store. If
9+
# we add another storable data type in the future, as long as it follows the same pattern
10+
# (having "key", "version", and "deleted" properties), we only need to add a corresponding
11+
# constant here and the existing store should be able to handle it.
2512
#
26-
# Maintain the same behavior when these data kinds were standard ruby hashes.
27-
#
28-
# @param key [Symbol]
29-
# @return [Object]
30-
#
31-
def [](key)
32-
return priority if key == :priority
33-
return namespace if key == :namespace
34-
return get_dependency_keys_fn() if key == :get_dependency_keys
35-
nil
36-
end
37-
38-
#
39-
# Retrieve the dependency keys for a particular data kind. Right now, this is only defined for flags.
40-
#
41-
def get_dependency_keys_fn()
42-
return nil unless @namespace == FEATURES
43-
44-
FEATURE_PREREQ_FN
45-
end
46-
47-
def eql?(other)
48-
other.is_a?(DataKind) && namespace == other.namespace && priority == other.priority
49-
end
50-
51-
def hash
52-
[namespace, priority].hash
53-
end
54-
end
55-
56-
class StatusProvider
57-
include LaunchDarkly::Interfaces::DataStore::StatusProvider
58-
59-
def initialize(store, update_sink)
60-
# @type [LaunchDarkly::Impl::FeatureStoreClientWrapper]
61-
@store = store
62-
# @type [UpdateSink]
63-
@update_sink = update_sink
64-
end
65-
66-
def status
67-
@update_sink.last_status.get
68-
end
69-
70-
def monitoring_enabled?
71-
@store.monitoring_enabled?
72-
end
73-
74-
def add_listener(listener)
75-
@update_sink.broadcaster.add_listener(listener)
76-
end
77-
78-
def remove_listener(listener)
79-
@update_sink.broadcaster.remove_listener(listener)
80-
end
81-
end
82-
83-
class UpdateSink
84-
include LaunchDarkly::Interfaces::DataStore::UpdateSink
85-
86-
# @return [LaunchDarkly::Impl::Broadcaster]
87-
attr_reader :broadcaster
88-
89-
# @return [Concurrent::AtomicReference]
90-
attr_reader :last_status
13+
# The :priority and :get_dependency_keys properties are used by FeatureStoreDataSetSorter
14+
# to ensure data consistency during non-atomic updates.
9115

92-
def initialize(broadcaster)
93-
@broadcaster = broadcaster
94-
@last_status = Concurrent::AtomicReference.new(
95-
LaunchDarkly::Interfaces::DataStore::Status.new(true, false)
96-
)
97-
end
16+
# @api private
17+
FEATURES = DataKind.new(namespace: "features", priority: 1).freeze
9818

99-
def update_status(status)
100-
return if status.nil?
19+
# @api private
20+
SEGMENTS = DataKind.new(namespace: "segments", priority: 0).freeze
10121

102-
old_status = @last_status.get_and_set(status)
103-
@broadcaster.broadcast(status) unless old_status == status
104-
end
105-
end
22+
# @api private
23+
ALL_KINDS = [FEATURES, SEGMENTS].freeze
10624
end
10725
end
108-
end
26+
end
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
require 'concurrent'
2+
require "ldclient-rb/interfaces"
3+
4+
module LaunchDarkly
5+
module Impl
6+
module DataStore
7+
class DataKind
8+
FEATURES = "features".freeze
9+
SEGMENTS = "segments".freeze
10+
11+
FEATURE_PREREQ_FN = lambda { |flag| (flag[:prerequisites] || []).map { |p| p[:key] } }.freeze
12+
13+
attr_reader :namespace
14+
attr_reader :priority
15+
16+
#
17+
# @param namespace [String]
18+
# @param priority [Integer]
19+
#
20+
def initialize(namespace:, priority:)
21+
@namespace = namespace
22+
@priority = priority
23+
end
24+
25+
#
26+
# Maintain the same behavior when these data kinds were standard ruby hashes.
27+
#
28+
# @param key [Symbol]
29+
# @return [Object]
30+
#
31+
def [](key)
32+
return priority if key == :priority
33+
return namespace if key == :namespace
34+
return get_dependency_keys_fn() if key == :get_dependency_keys
35+
nil
36+
end
37+
38+
#
39+
# Retrieve the dependency keys for a particular data kind. Right now, this is only defined for flags.
40+
#
41+
def get_dependency_keys_fn()
42+
return nil unless @namespace == FEATURES
43+
44+
FEATURE_PREREQ_FN
45+
end
46+
47+
def eql?(other)
48+
other.is_a?(DataKind) && namespace == other.namespace && priority == other.priority
49+
end
50+
51+
def hash
52+
[namespace, priority].hash
53+
end
54+
end
55+
56+
class StatusProvider
57+
include LaunchDarkly::Interfaces::DataStore::StatusProvider
58+
59+
def initialize(store, update_sink)
60+
# @type [LaunchDarkly::Impl::FeatureStoreClientWrapper]
61+
@store = store
62+
# @type [UpdateSink]
63+
@update_sink = update_sink
64+
end
65+
66+
def status
67+
@update_sink.last_status.get
68+
end
69+
70+
def monitoring_enabled?
71+
@store.monitoring_enabled?
72+
end
73+
74+
def add_listener(listener)
75+
@update_sink.broadcaster.add_listener(listener)
76+
end
77+
78+
def remove_listener(listener)
79+
@update_sink.broadcaster.remove_listener(listener)
80+
end
81+
end
82+
83+
class UpdateSink
84+
include LaunchDarkly::Interfaces::DataStore::UpdateSink
85+
86+
# @return [LaunchDarkly::Impl::Broadcaster]
87+
attr_reader :broadcaster
88+
89+
# @return [Concurrent::AtomicReference]
90+
attr_reader :last_status
91+
92+
def initialize(broadcaster)
93+
@broadcaster = broadcaster
94+
@last_status = Concurrent::AtomicReference.new(
95+
LaunchDarkly::Interfaces::DataStore::Status.new(true, false)
96+
)
97+
end
98+
99+
def update_status(status)
100+
return if status.nil?
101+
102+
old_status = @last_status.get_and_set(status)
103+
@broadcaster.broadcast(status) unless old_status == status
104+
end
105+
end
106+
end
107+
end
108+
end

lib/ldclient-rb/impl/dependency_tracker.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def update_dependencies_from(from_kind, from_key, from_item)
3939
def self.segment_keys_from_clauses(clauses)
4040
clauses.flat_map do |clause|
4141
if clause.op == :segmentMatch
42-
clause.values.map { |value| {kind: LaunchDarkly::SEGMENTS, key: value }}
42+
clause.values.map { |value| {kind: DataStore::SEGMENTS, key: value }}
4343
else
4444
[]
4545
end
@@ -54,13 +54,13 @@ def self.segment_keys_from_clauses(clauses)
5454
def self.compute_dependencies_from(from_kind, from_item)
5555
return Set.new if from_item.nil?
5656

57-
if from_kind == LaunchDarkly::FEATURES
57+
if from_kind == DataStore::FEATURES
5858
prereq_keys = from_item.prerequisites.map { |prereq| {kind: from_kind, key: prereq.key} }
5959
segment_keys = from_item.rules.flat_map { |rule| DependencyTracker.segment_keys_from_clauses(rule.clauses) }
6060

6161
results = Set.new(prereq_keys)
6262
results.merge(segment_keys)
63-
elsif from_kind == LaunchDarkly::SEGMENTS
63+
elsif from_kind == DataStore::SEGMENTS
6464
kind_and_keys = from_item.rules.flat_map do |rule|
6565
DependencyTracker.segment_keys_from_clauses(rule.clauses)
6666
end

lib/ldclient-rb/impl/integrations/file_data_source.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ def stop
7575

7676
def load_all
7777
all_data = {
78-
FEATURES => {},
79-
SEGMENTS => {},
78+
Impl::DataStore::FEATURES => {},
79+
Impl::DataStore::SEGMENTS => {},
8080
}
8181
@paths.each do |path|
8282
begin
@@ -105,14 +105,14 @@ def load_file(path, all_data)
105105
parsed = parse_content(IO.read(path))
106106
(parsed[:flags] || {}).each do |key, flag|
107107
flag[:version] = version
108-
add_item(all_data, FEATURES, flag)
108+
add_item(all_data, Impl::DataStore::FEATURES, flag)
109109
end
110110
(parsed[:flagValues] || {}).each do |key, value|
111-
add_item(all_data, FEATURES, make_flag_with_value(key.to_s, value, version))
111+
add_item(all_data, Impl::DataStore::FEATURES, make_flag_with_value(key.to_s, value, version))
112112
end
113113
(parsed[:segments] || {}).each do |key, segment|
114114
segment[:version] = version
115-
add_item(all_data, SEGMENTS, segment)
115+
add_item(all_data, Impl::DataStore::SEGMENTS, segment)
116116
end
117117
end
118118

lib/ldclient-rb/impl/model/serialization.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ module Model
3434
# SDK code outside of Impl::Model should use this method instead of calling the model class
3535
# constructors directly, so as not to rely on implementation details.
3636
#
37-
# @param kind [Hash] normally either FEATURES or SEGMENTS
37+
# @param kind [Hash] normally either Impl::DataStore::FEATURES or Impl::DataStore::SEGMENTS
3838
# @param input [object] a JSON string or a parsed hash (or a data model object, in which case
3939
# we'll just return the original object)
4040
# @param logger [Logger|nil] logs errors if there are any data validation problems
@@ -44,9 +44,9 @@ def self.deserialize(kind, input, logger = nil)
4444
return input if !input.is_a?(String) && !input.is_a?(Hash)
4545
data = input.is_a?(Hash) ? input : JSON.parse(input, symbolize_names: true)
4646
case kind
47-
when FEATURES
47+
when Impl::DataStore::FEATURES
4848
FeatureFlag.new(data, logger)
49-
when SEGMENTS
49+
when Impl::DataStore::SEGMENTS
5050
Segment.new(data, logger)
5151
else
5252
data
@@ -63,8 +63,8 @@ def self.serialize(kind, item)
6363
# Translates a { flags: ..., segments: ... } object received from LaunchDarkly to the data store format.
6464
def self.make_all_store_data(received_data, logger = nil)
6565
{
66-
FEATURES => (received_data[:flags] || {}).transform_values { |data| FeatureFlag.new(data, logger) },
67-
SEGMENTS => (received_data[:segments] || {}).transform_values { |data| Segment.new(data, logger) },
66+
Impl::DataStore::FEATURES => (received_data[:flags] || {}).transform_values { |data| FeatureFlag.new(data, logger) },
67+
Impl::DataStore::SEGMENTS => (received_data[:segments] || {}).transform_values { |data| Segment.new(data, logger) },
6868
}
6969
end
7070
end

0 commit comments

Comments
 (0)