launchdarkly-server-sdk 6.1.1 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -5
  3. data/lib/ldclient-rb/config.rb +118 -4
  4. data/lib/ldclient-rb/evaluation_detail.rb +104 -14
  5. data/lib/ldclient-rb/events.rb +201 -107
  6. data/lib/ldclient-rb/file_data_source.rb +9 -300
  7. data/lib/ldclient-rb/flags_state.rb +23 -12
  8. data/lib/ldclient-rb/impl/big_segments.rb +117 -0
  9. data/lib/ldclient-rb/impl/diagnostic_events.rb +1 -1
  10. data/lib/ldclient-rb/impl/evaluator.rb +116 -62
  11. data/lib/ldclient-rb/impl/evaluator_bucketing.rb +22 -9
  12. data/lib/ldclient-rb/impl/evaluator_helpers.rb +53 -0
  13. data/lib/ldclient-rb/impl/evaluator_operators.rb +1 -1
  14. data/lib/ldclient-rb/impl/event_summarizer.rb +63 -0
  15. data/lib/ldclient-rb/impl/event_types.rb +90 -0
  16. data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +82 -18
  17. data/lib/ldclient-rb/impl/integrations/file_data_source.rb +212 -0
  18. data/lib/ldclient-rb/impl/integrations/redis_impl.rb +84 -31
  19. data/lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb +40 -0
  20. data/lib/ldclient-rb/impl/model/preprocessed_data.rb +177 -0
  21. data/lib/ldclient-rb/impl/model/serialization.rb +7 -37
  22. data/lib/ldclient-rb/impl/repeating_task.rb +47 -0
  23. data/lib/ldclient-rb/impl/util.rb +62 -1
  24. data/lib/ldclient-rb/integrations/consul.rb +8 -1
  25. data/lib/ldclient-rb/integrations/dynamodb.rb +48 -3
  26. data/lib/ldclient-rb/integrations/file_data.rb +108 -0
  27. data/lib/ldclient-rb/integrations/redis.rb +42 -2
  28. data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +438 -0
  29. data/lib/ldclient-rb/integrations/test_data.rb +209 -0
  30. data/lib/ldclient-rb/integrations/util/store_wrapper.rb +5 -0
  31. data/lib/ldclient-rb/integrations.rb +2 -51
  32. data/lib/ldclient-rb/interfaces.rb +152 -2
  33. data/lib/ldclient-rb/ldclient.rb +131 -33
  34. data/lib/ldclient-rb/polling.rb +22 -41
  35. data/lib/ldclient-rb/requestor.rb +3 -3
  36. data/lib/ldclient-rb/stream.rb +4 -3
  37. data/lib/ldclient-rb/util.rb +10 -1
  38. data/lib/ldclient-rb/version.rb +1 -1
  39. data/lib/ldclient-rb.rb +0 -1
  40. metadata +35 -132
  41. data/.circleci/config.yml +0 -40
  42. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -37
  43. data/.github/ISSUE_TEMPLATE/config.yml +0 -5
  44. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  45. data/.github/pull_request_template.md +0 -21
  46. data/.gitignore +0 -16
  47. data/.hound.yml +0 -2
  48. data/.ldrelease/build-docs.sh +0 -18
  49. data/.ldrelease/circleci/linux/execute.sh +0 -18
  50. data/.ldrelease/circleci/mac/execute.sh +0 -18
  51. data/.ldrelease/circleci/template/build.sh +0 -29
  52. data/.ldrelease/circleci/template/publish.sh +0 -23
  53. data/.ldrelease/circleci/template/set-gem-home.sh +0 -7
  54. data/.ldrelease/circleci/template/test.sh +0 -10
  55. data/.ldrelease/circleci/template/update-version.sh +0 -8
  56. data/.ldrelease/circleci/windows/execute.ps1 +0 -19
  57. data/.ldrelease/config.yml +0 -29
  58. data/.rspec +0 -2
  59. data/.rubocop.yml +0 -600
  60. data/.simplecov +0 -4
  61. data/CHANGELOG.md +0 -351
  62. data/CODEOWNERS +0 -1
  63. data/CONTRIBUTING.md +0 -37
  64. data/Gemfile +0 -3
  65. data/azure-pipelines.yml +0 -51
  66. data/docs/Makefile +0 -26
  67. data/docs/index.md +0 -9
  68. data/launchdarkly-server-sdk.gemspec +0 -45
  69. data/lib/ldclient-rb/event_summarizer.rb +0 -55
  70. data/lib/ldclient-rb/impl/event_factory.rb +0 -120
  71. data/spec/config_spec.rb +0 -63
  72. data/spec/diagnostic_events_spec.rb +0 -163
  73. data/spec/evaluation_detail_spec.rb +0 -135
  74. data/spec/event_sender_spec.rb +0 -197
  75. data/spec/event_summarizer_spec.rb +0 -63
  76. data/spec/events_spec.rb +0 -607
  77. data/spec/expiring_cache_spec.rb +0 -76
  78. data/spec/feature_store_spec_base.rb +0 -213
  79. data/spec/file_data_source_spec.rb +0 -283
  80. data/spec/fixtures/feature.json +0 -37
  81. data/spec/fixtures/feature1.json +0 -36
  82. data/spec/fixtures/user.json +0 -9
  83. data/spec/flags_state_spec.rb +0 -81
  84. data/spec/http_util.rb +0 -132
  85. data/spec/impl/evaluator_bucketing_spec.rb +0 -111
  86. data/spec/impl/evaluator_clause_spec.rb +0 -55
  87. data/spec/impl/evaluator_operators_spec.rb +0 -141
  88. data/spec/impl/evaluator_rule_spec.rb +0 -96
  89. data/spec/impl/evaluator_segment_spec.rb +0 -125
  90. data/spec/impl/evaluator_spec.rb +0 -305
  91. data/spec/impl/evaluator_spec_base.rb +0 -75
  92. data/spec/impl/model/serialization_spec.rb +0 -41
  93. data/spec/in_memory_feature_store_spec.rb +0 -12
  94. data/spec/integrations/consul_feature_store_spec.rb +0 -40
  95. data/spec/integrations/dynamodb_feature_store_spec.rb +0 -103
  96. data/spec/integrations/store_wrapper_spec.rb +0 -276
  97. data/spec/launchdarkly-server-sdk_spec.rb +0 -13
  98. data/spec/launchdarkly-server-sdk_spec_autoloadtest.rb +0 -9
  99. data/spec/ldclient_end_to_end_spec.rb +0 -157
  100. data/spec/ldclient_spec.rb +0 -643
  101. data/spec/newrelic_spec.rb +0 -5
  102. data/spec/polling_spec.rb +0 -120
  103. data/spec/redis_feature_store_spec.rb +0 -121
  104. data/spec/requestor_spec.rb +0 -196
  105. data/spec/segment_store_spec_base.rb +0 -95
  106. data/spec/simple_lru_cache_spec.rb +0 -24
  107. data/spec/spec_helper.rb +0 -9
  108. data/spec/store_spec.rb +0 -10
  109. data/spec/stream_spec.rb +0 -45
  110. data/spec/user_filter_spec.rb +0 -91
  111. data/spec/util_spec.rb +0 -17
  112. data/spec/version_spec.rb +0 -7
@@ -1,314 +1,23 @@
1
- require 'concurrent/atomics'
2
- require 'json'
3
- require 'yaml'
4
- require 'pathname'
1
+ require "ldclient-rb/integrations/file_data"
5
2
 
6
3
  module LaunchDarkly
7
- # To avoid pulling in 'listen' and its transitive dependencies for people who aren't using the
8
- # file data source or who don't need auto-updating, we only enable auto-update if the 'listen'
9
- # gem has been provided by the host app.
10
- # @private
11
- @@have_listen = false
12
- begin
13
- require 'listen'
14
- @@have_listen = true
15
- rescue LoadError
16
- end
17
-
18
- # @private
19
- def self.have_listen?
20
- @@have_listen
21
- end
22
-
23
- #
24
- # Provides a way to use local files as a source of feature flag state. This allows using a
25
- # predetermined feature flag state without an actual LaunchDarkly connection.
26
- #
27
- # Reading flags from a file is only intended for pre-production environments. Production
28
- # environments should always be configured to receive flag updates from LaunchDarkly.
29
- #
30
- # To use this component, call {FileDataSource#factory}, and store its return value in the
31
- # {Config#data_source} property of your LaunchDarkly client configuration. In the options
32
- # to `factory`, set `paths` to the file path(s) of your data file(s):
33
- #
34
- # file_source = FileDataSource.factory(paths: [ myFilePath ])
35
- # config = LaunchDarkly::Config.new(data_source: file_source)
36
- #
37
- # This will cause the client not to connect to LaunchDarkly to get feature flags. The
38
- # client may still make network connections to send analytics events, unless you have disabled
39
- # this with {Config#send_events} or {Config#offline?}.
40
- #
41
- # Flag data files can be either JSON or YAML. They contain an object with three possible
42
- # properties:
43
- #
44
- # - `flags`: Feature flag definitions.
45
- # - `flagValues`: Simplified feature flags that contain only a value.
46
- # - `segments`: User segment definitions.
47
- #
48
- # The format of the data in `flags` and `segments` is defined by the LaunchDarkly application
49
- # and is subject to change. Rather than trying to construct these objects yourself, it is simpler
50
- # to request existing flags directly from the LaunchDarkly server in JSON format, and use this
51
- # output as the starting point for your file. In Linux you would do this:
52
- #
53
- # ```
54
- # curl -H "Authorization: YOUR_SDK_KEY" https://sdk.launchdarkly.com/sdk/latest-all
55
- # ```
56
4
  #
57
- # The output will look something like this (but with many more properties):
5
+ # Deprecated entry point for the file data source feature.
58
6
  #
59
- # {
60
- # "flags": {
61
- # "flag-key-1": {
62
- # "key": "flag-key-1",
63
- # "on": true,
64
- # "variations": [ "a", "b" ]
65
- # }
66
- # },
67
- # "segments": {
68
- # "segment-key-1": {
69
- # "key": "segment-key-1",
70
- # "includes": [ "user-key-1" ]
71
- # }
72
- # }
73
- # }
7
+ # The new preferred usage is {LaunchDarkly::Integrations::FileData#data_source}.
74
8
  #
75
- # Data in this format allows the SDK to exactly duplicate all the kinds of flag behavior supported
76
- # by LaunchDarkly. However, in many cases you will not need this complexity, but will just want to
77
- # set specific flag keys to specific values. For that, you can use a much simpler format:
78
- #
79
- # {
80
- # "flagValues": {
81
- # "my-string-flag-key": "value-1",
82
- # "my-boolean-flag-key": true,
83
- # "my-integer-flag-key": 3
84
- # }
85
- # }
86
- #
87
- # Or, in YAML:
88
- #
89
- # flagValues:
90
- # my-string-flag-key: "value-1"
91
- # my-boolean-flag-key: true
92
- # my-integer-flag-key: 1
93
- #
94
- # It is also possible to specify both "flags" and "flagValues", if you want some flags
95
- # to have simple values and others to have complex behavior. However, it is an error to use the
96
- # same flag key or segment key more than once, either in a single file or across multiple files.
97
- #
98
- # If the data source encounters any error in any file-- malformed content, a missing file, or a
99
- # duplicate key-- it will not load flags from any of the files.
9
+ # @deprecated This is replaced by {LaunchDarkly::Integrations::FileData}.
100
10
  #
101
11
  class FileDataSource
102
12
  #
103
- # Returns a factory for the file data source component.
104
- #
105
- # @param options [Hash] the configuration options
106
- # @option options [Array] :paths The paths of the source files for loading flag data. These
107
- # may be absolute paths or relative to the current working directory.
108
- # @option options [Boolean] :auto_update True if the data source should watch for changes to
109
- # the source file(s) and reload flags whenever there is a change. Auto-updating will only
110
- # work if all of the files you specified have valid directory paths at startup time.
111
- # Note that the default implementation of this feature is based on polling the filesystem,
112
- # which may not perform well. If you install the 'listen' gem (not included by default, to
113
- # avoid adding unwanted dependencies to the SDK), its native file watching mechanism will be
114
- # used instead. However, 'listen' will not be used in JRuby 9.1 due to a known instability.
115
- # @option options [Float] :poll_interval The minimum interval, in seconds, between checks for
116
- # file modifications - used only if auto_update is true, and if the native file-watching
117
- # mechanism from 'listen' is not being used. The default value is 1 second.
118
- # @return an object that can be stored in {Config#data_source}
13
+ # Deprecated entry point for the file data source feature.
119
14
  #
120
- def self.factory(options={})
121
- return lambda { |sdk_key, config| FileDataSourceImpl.new(config.feature_store, config.logger, options) }
122
- end
123
- end
124
-
125
- # @private
126
- class FileDataSourceImpl
127
- def initialize(feature_store, logger, options={})
128
- @feature_store = feature_store
129
- @logger = logger
130
- @paths = options[:paths] || []
131
- if @paths.is_a? String
132
- @paths = [ @paths ]
133
- end
134
- @auto_update = options[:auto_update]
135
- if @auto_update && LaunchDarkly.have_listen? && !options[:force_polling] # force_polling is used only for tests
136
- # We have seen unreliable behavior in the 'listen' gem in JRuby 9.1 (https://github.com/guard/listen/issues/449).
137
- # Therefore, on that platform we'll fall back to file polling instead.
138
- if defined?(JRUBY_VERSION) && JRUBY_VERSION.start_with?("9.1.")
139
- @use_listen = false
140
- else
141
- @use_listen = true
142
- end
143
- end
144
- @poll_interval = options[:poll_interval] || 1
145
- @initialized = Concurrent::AtomicBoolean.new(false)
146
- @ready = Concurrent::Event.new
147
- end
148
-
149
- def initialized?
150
- @initialized.value
151
- end
152
-
153
- def start
154
- ready = Concurrent::Event.new
155
-
156
- # We will return immediately regardless of whether the file load succeeded or failed -
157
- # the difference can be detected by checking "initialized?"
158
- ready.set
159
-
160
- load_all
161
-
162
- if @auto_update
163
- # If we're going to watch files, then the start event will be set the first time we get
164
- # a successful load.
165
- @listener = start_listener
166
- end
167
-
168
- ready
169
- end
170
-
171
- def stop
172
- @listener.stop if !@listener.nil?
173
- end
174
-
175
- private
176
-
177
- def load_all
178
- all_data = {
179
- FEATURES => {},
180
- SEGMENTS => {}
181
- }
182
- @paths.each do |path|
183
- begin
184
- load_file(path, all_data)
185
- rescue => exn
186
- Util.log_exception(@logger, "Unable to load flag data from \"#{path}\"", exn)
187
- return
188
- end
189
- end
190
- @feature_store.init(all_data)
191
- @initialized.make_true
192
- end
193
-
194
- def load_file(path, all_data)
195
- parsed = parse_content(IO.read(path))
196
- (parsed[:flags] || {}).each do |key, flag|
197
- add_item(all_data, FEATURES, flag)
198
- end
199
- (parsed[:flagValues] || {}).each do |key, value|
200
- add_item(all_data, FEATURES, make_flag_with_value(key.to_s, value))
201
- end
202
- (parsed[:segments] || {}).each do |key, segment|
203
- add_item(all_data, SEGMENTS, segment)
204
- end
205
- end
206
-
207
- def parse_content(content)
208
- # We can use the Ruby YAML parser for both YAML and JSON (JSON is a subset of YAML and while
209
- # not all YAML parsers handle it correctly, we have verified that the Ruby one does, at least
210
- # for all the samples of actual flag data that we've tested).
211
- symbolize_all_keys(YAML.safe_load(content))
212
- end
213
-
214
- def symbolize_all_keys(value)
215
- # This is necessary because YAML.load doesn't have an option for parsing keys as symbols, and
216
- # the SDK expects all objects to be formatted that way.
217
- if value.is_a?(Hash)
218
- value.map{ |k, v| [k.to_sym, symbolize_all_keys(v)] }.to_h
219
- elsif value.is_a?(Array)
220
- value.map{ |v| symbolize_all_keys(v) }
221
- else
222
- value
223
- end
224
- end
225
-
226
- def add_item(all_data, kind, item)
227
- items = all_data[kind]
228
- raise ArgumentError, "Received unknown item kind #{kind} in add_data" if items.nil? # shouldn't be possible since we preinitialize the hash
229
- key = item[:key].to_sym
230
- if !items[key].nil?
231
- raise ArgumentError, "#{kind[:namespace]} key \"#{item[:key]}\" was used more than once"
232
- end
233
- items[key] = item
234
- end
235
-
236
- def make_flag_with_value(key, value)
237
- {
238
- key: key,
239
- on: true,
240
- fallthrough: { variation: 0 },
241
- variations: [ value ]
242
- }
243
- end
244
-
245
- def start_listener
246
- resolved_paths = @paths.map { |p| Pathname.new(File.absolute_path(p)).realpath.to_s }
247
- if @use_listen
248
- start_listener_with_listen_gem(resolved_paths)
249
- else
250
- FileDataSourcePoller.new(resolved_paths, @poll_interval, self.method(:load_all), @logger)
251
- end
252
- end
253
-
254
- def start_listener_with_listen_gem(resolved_paths)
255
- path_set = resolved_paths.to_set
256
- dir_paths = resolved_paths.map{ |p| File.dirname(p) }.uniq
257
- opts = { latency: @poll_interval }
258
- l = Listen.to(*dir_paths, opts) do |modified, added, removed|
259
- paths = modified + added + removed
260
- if paths.any? { |p| path_set.include?(p) }
261
- load_all
262
- end
263
- end
264
- l.start
265
- l
266
- end
267
-
15
+ # The new preferred usage is {LaunchDarkly::Integrations::FileData#data_source}.
268
16
  #
269
- # Used internally by FileDataSource to track data file changes if the 'listen' gem is not available.
17
+ # @deprecated This is replaced by {LaunchDarkly::Integrations::FileData#data_source}.
270
18
  #
271
- class FileDataSourcePoller
272
- def initialize(resolved_paths, interval, reloader, logger)
273
- @stopped = Concurrent::AtomicBoolean.new(false)
274
- get_file_times = Proc.new do
275
- ret = {}
276
- resolved_paths.each do |path|
277
- begin
278
- ret[path] = File.mtime(path)
279
- rescue Errno::ENOENT
280
- ret[path] = nil
281
- end
282
- end
283
- ret
284
- end
285
- last_times = get_file_times.call
286
- @thread = Thread.new do
287
- while true
288
- sleep interval
289
- break if @stopped.value
290
- begin
291
- new_times = get_file_times.call
292
- changed = false
293
- last_times.each do |path, old_time|
294
- new_time = new_times[path]
295
- if !new_time.nil? && new_time != old_time
296
- changed = true
297
- break
298
- end
299
- end
300
- reloader.call if changed
301
- rescue => exn
302
- Util.log_exception(logger, "Unexpected exception in FileDataSourcePoller", exn)
303
- end
304
- end
305
- end
306
- end
307
-
308
- def stop
309
- @stopped.make_true
310
- @thread.run # wakes it up if it's sleeping
311
- end
19
+ def self.factory(options={})
20
+ LaunchDarkly::Integrations::FileData.data_source(options)
312
21
  end
313
22
  end
314
23
  end
@@ -16,21 +16,32 @@ module LaunchDarkly
16
16
 
17
17
  # Used internally to build the state map.
18
18
  # @private
19
- def add_flag(flag, value, variation, reason = nil, details_only_if_tracked = false)
20
- key = flag[:key]
21
- @flag_values[key] = value
19
+ def add_flag(flag_state, with_reasons, details_only_if_tracked)
20
+ key = flag_state[:key]
21
+ @flag_values[key] = flag_state[:value]
22
22
  meta = {}
23
- with_details = !details_only_if_tracked || flag[:trackEvents]
24
- if !with_details && flag[:debugEventsUntilDate]
25
- with_details = flag[:debugEventsUntilDate] > Impl::Util::current_time_millis
23
+
24
+ omit_details = false
25
+ if details_only_if_tracked
26
+ if !flag_state[:trackEvents] && !flag_state[:trackReason] && !(flag_state[:debugEventsUntilDate] && flag_state[:debugEventsUntilDate] > Impl::Util::current_time_millis)
27
+ omit_details = true
28
+ end
29
+ end
30
+
31
+ reason = (!with_reasons and !flag_state[:trackReason]) ? nil : flag_state[:reason]
32
+
33
+ if !reason.nil? && !omit_details
34
+ meta[:reason] = reason
26
35
  end
27
- if with_details
28
- meta[:version] = flag[:version]
29
- meta[:reason] = reason if !reason.nil?
36
+
37
+ if !omit_details
38
+ meta[:version] = flag_state[:version]
30
39
  end
31
- meta[:variation] = variation if !variation.nil?
32
- meta[:trackEvents] = true if flag[:trackEvents]
33
- meta[:debugEventsUntilDate] = flag[:debugEventsUntilDate] if flag[:debugEventsUntilDate]
40
+
41
+ meta[:variation] = flag_state[:variation] if !flag_state[:variation].nil?
42
+ meta[:trackEvents] = true if flag_state[:trackEvents]
43
+ meta[:trackReason] = true if flag_state[:trackReason]
44
+ meta[:debugEventsUntilDate] = flag_state[:debugEventsUntilDate] if flag_state[:debugEventsUntilDate]
34
45
  @flag_metadata[key] = meta
35
46
  end
36
47
 
@@ -0,0 +1,117 @@
1
+ require "ldclient-rb/config"
2
+ require "ldclient-rb/expiring_cache"
3
+ require "ldclient-rb/impl/repeating_task"
4
+ require "ldclient-rb/interfaces"
5
+ require "ldclient-rb/util"
6
+
7
+ require "digest"
8
+
9
+ module LaunchDarkly
10
+ module Impl
11
+ BigSegmentMembershipResult = Struct.new(:membership, :status)
12
+
13
+ class BigSegmentStoreManager
14
+ # use this as a singleton whenever a membership query returns nil; it's safe to reuse it because
15
+ # we will never modify the membership properties after they're queried
16
+ EMPTY_MEMBERSHIP = {}
17
+
18
+ def initialize(big_segments_config, logger)
19
+ @store = big_segments_config.store
20
+ @stale_after_millis = big_segments_config.stale_after * 1000
21
+ @status_provider = BigSegmentStoreStatusProviderImpl.new(-> { get_status })
22
+ @logger = logger
23
+ @last_status = nil
24
+
25
+ if !@store.nil?
26
+ @cache = ExpiringCache.new(big_segments_config.user_cache_size, big_segments_config.user_cache_time)
27
+ @poll_worker = RepeatingTask.new(big_segments_config.status_poll_interval, 0, -> { poll_store_and_update_status }, logger)
28
+ @poll_worker.start
29
+ end
30
+ end
31
+
32
+ attr_reader :status_provider
33
+
34
+ def stop
35
+ @poll_worker.stop if !@poll_worker.nil?
36
+ @store.stop if !@store.nil?
37
+ end
38
+
39
+ def get_user_membership(user_key)
40
+ return nil if !@store
41
+ membership = @cache[user_key]
42
+ if !membership
43
+ begin
44
+ membership = @store.get_membership(BigSegmentStoreManager.hash_for_user_key(user_key))
45
+ membership = EMPTY_MEMBERSHIP if membership.nil?
46
+ @cache[user_key] = membership
47
+ rescue => e
48
+ LaunchDarkly::Util.log_exception(@logger, "Big Segment store membership query returned error", e)
49
+ return BigSegmentMembershipResult.new(nil, BigSegmentsStatus::STORE_ERROR)
50
+ end
51
+ end
52
+ poll_store_and_update_status if !@last_status
53
+ if !@last_status.available
54
+ return BigSegmentMembershipResult.new(membership, BigSegmentsStatus::STORE_ERROR)
55
+ end
56
+ BigSegmentMembershipResult.new(membership, @last_status.stale ? BigSegmentsStatus::STALE : BigSegmentsStatus::HEALTHY)
57
+ end
58
+
59
+ def get_status
60
+ @last_status || poll_store_and_update_status
61
+ end
62
+
63
+ def poll_store_and_update_status
64
+ new_status = Interfaces::BigSegmentStoreStatus.new(false, false) # default to "unavailable" if we don't get a new status below
65
+ if !@store.nil?
66
+ begin
67
+ metadata = @store.get_metadata
68
+ new_status = Interfaces::BigSegmentStoreStatus.new(true, !metadata || is_stale(metadata.last_up_to_date))
69
+ rescue => e
70
+ LaunchDarkly::Util.log_exception(@logger, "Big Segment store status query returned error", e)
71
+ end
72
+ end
73
+ @last_status = new_status
74
+ @status_provider.update_status(new_status)
75
+
76
+ new_status
77
+ end
78
+
79
+ def is_stale(timestamp)
80
+ !timestamp || ((Impl::Util.current_time_millis - timestamp) >= @stale_after_millis)
81
+ end
82
+
83
+ def self.hash_for_user_key(user_key)
84
+ Digest::SHA256.base64digest(user_key)
85
+ end
86
+ end
87
+
88
+ #
89
+ # Default implementation of the BigSegmentStoreStatusProvider interface.
90
+ #
91
+ # There isn't much to this because the real implementation is in BigSegmentStoreManager - we pass in a lambda
92
+ # that allows us to get the current status from that class. Also, the standard Observer methods such as
93
+ # add_observer are provided for us because BigSegmentStoreStatusProvider mixes in Observer, so all we need to
94
+ # to do make notifications happen is to call the Observer methods "changed" and "notify_observers".
95
+ #
96
+ class BigSegmentStoreStatusProviderImpl
97
+ include LaunchDarkly::Interfaces::BigSegmentStoreStatusProvider
98
+
99
+ def initialize(status_fn)
100
+ @status_fn = status_fn
101
+ @last_status = nil
102
+ end
103
+
104
+ def status
105
+ @status_fn.call
106
+ end
107
+
108
+ def update_status(new_status)
109
+ if !@last_status || new_status != @last_status
110
+ @last_status = new_status
111
+ changed
112
+ notify_observers(new_status)
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -79,7 +79,7 @@ module LaunchDarkly
79
79
  streamingDisabled: !config.stream?,
80
80
  userKeysCapacity: config.user_keys_capacity,
81
81
  userKeysFlushIntervalMillis: self.seconds_to_millis(config.user_keys_flush_interval),
82
- usingProxy: ENV.has_key?('http_proxy') || ENV.has_key?('https_proxy') || ENV.has_key?('HTTP_PROXY'),
82
+ usingProxy: ENV.has_key?('http_proxy') || ENV.has_key?('https_proxy') || ENV.has_key?('HTTP_PROXY') || ENV.has_key?('HTTPS_PROXY'),
83
83
  usingRelayDaemon: config.use_ldd?,
84
84
  }
85
85
  ret