Skip to content

Commit 01dc51d

Browse files
LaunchDarklyReleaseBoteli-darklyLaunchDarklyCIjacobthemythapache-hb
authored
prepare 7.2.0 release (#218)
* add event payload ID header * (6.0) drop support for old Ruby versions * add Ruby version constraint to gemspec * remove Rake dependency * update ld-eventsource to 1.0.2 which doesn't have Rake dependency * implement diagnostic events in Ruby (#130) * update ruby-eventsource to 1.0.3 for backoff bug * fix incorrect initialization of EventProcessor * remove install-time openssl check that breaks if you don't have rake * treat comparison with wrong data type as a non-match, not an exception (#134) * fail fast for nil SDK key when appropriate * tolerate nil value for user.custom (#137) * Only shutdown the Redis pool if it is owned by the SDK (#158) * Only shutdown a Redis pool created by SDK * Make pool shutdown behavior an option * improve doc comment * remove support for indirect/patch and indirect/put (#138) * update to json 2.3.1 (#139) * update json dep to 2.3.x to fix CVE * add publication of API docs on GitHub Pages (#143) * try fixing release metadata * update the default base url (#144) * revert renames of feature_store & update_processor * [ch92483] Use http gem and add socket factory support (#142) * update dependencies and add CI for ruby 3 (#141) * reference eventsource 2.0 in gemspec * add 5.x releasable branch for releaser * use Ruby 2.6.6 in releases * Removed the guides link * [ch99757] add alias method (#147) * don't send event for nil user evaluation * remove lockfile (#148) * rm redundant nil check * Experiment Allocation Changes (#150) * WIP - from sam's pairing session * starting sdk changes * adding tests and making sure everything works * adding more tests * removing the singleton for fallthrough * Revert "removing the singleton for fallthrough" This reverts commit dff7adbb809ecc63118d0fbff9742a88a039c679. * taking a different approach to keep things immutable * adding tests for untracked * remove unnecessary comment * making sure to return two values in all code paths Co-authored-by: pellyg-ld <[email protected]> * Use camelCase for JSON property names (#151) The in_experiment attribute was added to reasons as part of #150 but it doesn't appear to be received in events. I think that's because it's sending it in JSON as "in_experiment" rather than "inExperiment" as we expect to parse it. * fixing ruby logic causing ih failures (#152) * fixing ruby logic * adding missing spec * Apply suggestions from code review Co-authored-by: Sam Stokes <[email protected]> * pr tweaks * making spec language consistent Co-authored-by: Sam Stokes <[email protected]> * add log warning for missing user key (#153) * add log warnings for nil/empty user key * rm warning for empty string key * fix test * diagnostic events should respect HTTPS_PROXY (#154) * minor test simplification (#155) * allow higher minor versions of json and http gems * allow v5.x of http gem (#157) * use Bundler 2.2.10 + modernize CI config (#158) * enable verbose rspec output * fix socket factory tests * restore log suppression * Replacing deprecated circleci image usage (#159) * use Releaser v2 config (#161) * Updates docs URLs * Update lib/ldclient-rb/ldclient.rb Co-authored-by: Louis Chan <[email protected]> * remove reliance on git in gemspec (#163) * use ruby-eventsource 2.1.1 for fix of sc-123850 and sc-125504 (#164) * use ruby-eventsource 2.1.1 for fix of sc-123850 and sc-125504 * comment phrasing * Start work on flag builder. * Add user targeting and rule builder * Add datasource implementation * Convert the current_flags hash to use symbols instead of strings as keys * Fix typo on FlagRuleBuilder copy constructor * minor refactoring of impl; Added use of new Clause struct instead of Hash in FlagRuleBuilder; Moved TestData.factory out of Impl namespace and renamed Impl to TestDataImpl * Add the doc comments * (big segments 1) add public config/interface/reason types (#167) * Cleanup docstrings to be YARD docs * Added Util.is_bool helper function to clean up the check for whether an object is a boolean; Removed the DeepCopyHash/DeepCopyArray objects in favor of deep_copy_hash and deep_copy_array functions * Move public classes out of Impl namespace. Most of it is in public namespace except for the data source now. * Move require of concurrent/atomics to the correct module * (big segments 2) implement Big Segments evaluation & status APIs (#168) * improve CONTRIBUTING.md with notes on code organization * add note about doc comments * Cleanup YARD warnings and cleanup docs * Address PR feedback: Move is_bool back to Impl namespace to avoid confusion; Remove unnecessary nil check on variations in build function; fixup comments * (big segments 3) implement Redis & DynamoDB big segment stores (#169) * add missing import * fix stale calculation * fix big segments user hash algorithm to use SHA256 * improve & refactor client/evaluation tests * more cleanup/DRY * add use_preconfigured_flag and use_preconfigured_segment to TestData (#173) * always cache big segment query result even if it's nil * comments * add test for cache expiration * use TestData in our own tests (#174) * use TestData in our own tests * fix test * replace LaunchDarkly::FileDataSource with LaunchDarkly::Integrations::FileData * update ruby-eventsource version for recent SSE fixes * Bump bundler version (#184) * Add ability to to set initial reconnect delay (#183) * Treat secondary as a built-in attribute (#180) * all_flags_state is invalid if store isn't initialized (#182) * identify should not emit events if user key is "" (#181) * Account for traffic allocation on all flags (#185) * Add contract tests (#178) * Fix string interpolation in log message (#187) * Default opts to empty hash when creating persistent feature store (#186) * Remove Hakiri badge from README (#188) Hakiri was sunset on January 31st, 2022 at which time our badge stopped working. * detect http/https proxy env vars when creating HTTP clients * rever accidental change * fix nil safety in test service config * master -> main (#190) * master -> main * update ruby-eventsource version for parsing efficiency fix * miscellaneous optimizations for event processing (#193) * Drop support for EOL ruby versions (#196) Ruby 2.5 was EOL 2021-04-05 As of June 27th, 2022, the latest jRuby is Ruby 2.6 compatible. * Remove alias support (#195) * Add polling support for contract test service (#198) * Update rubocop and enable in CI (#197) Several of the Rubocop cop definitions have been renamed or moved to entirely other gems. This brings the configuration up to date with the latest naming conventions. * Add windows tests in circleci (#199) At some point in the past, we were experimenting with using Azure to verify Window builds. Now that CircleCI supports Windows, we should keep everything on a single CI provider. * Add application info support (#194) * reuse EvaluationDetail instances by precomputing results * rubocop reformatting * add super constructor calls * disable rubocop Rails rules and fix some remaining syntax offenses * fix super calls * Add big segment support to contract tests (#201) * Initial creation of LDContext (#206) This introduces the initial structure and usage of the LDContext class. Instances of this class are expected to be created through static factory methods: ```ruby LaunchDarkly::LDContext.create({...}) LaunchDarkly::LDContext.create_multi([...]) ``` This class is not completed yet. Rather, this initial commit is focused on the creation patterns and the most basic operations. Subsequent commits will continue fleshing out this class and its operation. The `get_value` method will see significant changes as we introduce attribute reference support. Its current more simplistic implementation exists only to serve some interim unit tests. * Add reference based value retrieval (#207) This commit introduces the References type used for targeting complex attributes in the new LDContexts. References are expected to be created through static factory methods: ```ruby LaunchDarkly::Reference.create("/a/b") LaunchDarkly::Reference.create_literal("/a/b") ``` These references can be used to retrieve values from an existing LDContext ```ruby ref = LaunchDarkly::Reference.create("/a/b") result = context.get_value_for_reference(ref) ``` * Basic changes to use contexts in evaluations instead of users (#208) This commit follows the general approach of the [equivalent PHP SDK PR][pr]. This replaces `LDUser` with `LDContext` in the parameters for evaluations, and makes the minimum necessary adjustments to allow evaluations to keep working as before as long as the context kind is "user". None of the new behavior defined in the U2C spec is implemented yet. Generation of evaluation events is temporarily disabled because the event logic hasn't been updated yet. U2C contract tests for evaluations are partially enabled; a lot of functionality is still missing, but all the tests that only cover previously-existing evaluation behavior are passing. [pr]: launchdarkly/php-server-sdk-private#103 * Support ContextKind in Clauses (#209) This commit follows the general approach of the [equivalent PHP SDK PR][pr]. The main features of this commit are: - introduction of `individual_context` and `individual_context_count` methods - context kind matching in clauses [pr]: launchdarkly/php-server-sdk-private#108 * Support included / excluded contexts in segments (#210) This commit follows the general approach of the [equivalent PHP SDK PR][pr]. Segments are now able to provide `includedContext` and `excludedContext` properties which can target values within a specific context kind. ```json { "includedContexts": [ { "contextKind": "org", "values": ["orgkey1", "orgkey2"] } ] } ``` [pr]: launchdarkly/php-server-sdk-private#111 * Add contextKind support for rollouts & experiements (#211) This commit follows the general approach of the [equivalent PHP SDK PR][pr]. [pr]: launchdarkly/php-server-sdk-private#110 * Style and test matcher improvements (#212) This commit enables several rubocop rules that were previously disabled. Once enabled, `rubocop -A` was run to automatically apply these fixes. There are a couple of additional changes that were made by hand: - I added the rubocop and rubocop-performance gems as dev packages. This should help address the original installation issue we ran into when I introduced these tools. - By default, new rubocop rules are disabled. This was the default before, but if you don't explicitly set this value, each run generates a ton of warning noise. This quiets that down. - Updates some LDContext tests to be more strict in their expectations of truth. * Remove support for secondary attribute (#213) As decided in the [spec], we are removing the special behavior of the secondary attribute. Going forward, secondary will be treated like any other attribute, and will no longer be included when determining the bucket for a context. [spec]: https://launchdarkly.atlassian.net/wiki/spaces/ENG/pages/2165212563/Consistent+and+Transparent+Rollout+Behavior+Unifying+Percent+Rollout+and+Traffic+Allocation * Remove deprecated APIs (#214) Since the users to context change requires a version break, this is the perfect time to remove previously deprecated bits of functionality. This includes: - Removing `update_processor*` config entries - `FileDataSource` entry point - `RedisFeatureStore` entry point - `Redis::sadd?` warning in unit tests * store data model with classes that aren't Hash * lint * remove [] override methods in places where we don't need them * comments * migrate some more of the model to be non-hash classes * lint * Anonymous cannot be nil in new context format (#216) The legacy user format allowed anonymous to be missing or explicitly provided but set to nil. The new context format requires anonymous to either not be set, or if it is explicitly set, it must be a boolean value. * Tweak error message language and style (#217) Our previous error messages suffered from a couple drawbacks: - The messages were complete sentences, limiting our ability to compose error messages - The messages were overly broad in many cases - The messages unnecessarily required string interpolation that rarely provided much value These new messages are more succinct and are written as small clauses which can be used in conjunction with other error messages more easily. * copyedit Co-authored-by: Matthew M. Keeler <[email protected]> * Implement prerequisite cycle detection (#219) * Support attribute reference lookups (#215) This adds support for slash-delimited paths in clause attributes, bucketBy, etc. It does not do anything related to private attribute redaction because none of the U2C event logic is implemented yet. * Implement segment recursion and cycle detection (#220) Clauses in segment rules are now allowed to reference segments. To prevent an infinite recursion edge case, we implement a similar cycle detection mechanism as used on prerequisites. * Update event logic to support users to context change (#221) * Add legacy user-type support to the contract tests (#222) * Remove inline user configuration option (#223) * Add context_ configuration options (#224) These new context_ configuration options are meant to replace the historic user_ options. If both are provided, the context_ variant will take precedence. * Add support for flag context targets (#225) * Bump diplomat * Bump redis * Remove oga * Bump connection_pool * Favor set for faster target lookups (#228) A few of our internal models maintain arrays of values. These arrays can frequently be checked to see if they contain specific values. Since set lookups are much faster than array lookups, this commit changes the internal structure to a set for the values stored in Target and SegmentTarget. * Add secure mode hash to contract tests (#229) * Update big segment support for users to context (#226) To support the users to context change for big segments, this commit makes the following changes: - Introduces a new `Segment.unboundedContextKind` attribute. This will default to `LDContext::KIND_DEFAULT` and is only referenced when `Segment.unbounded` is true. - With the creation of multi-kind contexts, a single evaluation may result in multiple queries to the big segment store. This is reflected in the changes to the `EvalResult` processing. * Drop support for ruby 2.6 (#227) Ruby 2.6 went EOL in March 2022. We originally didn't drop support for it as doing so would require dropping support for jRuby as well. However, jRuby recently released 9.4 which is Ruby 2.7+ compatible. * Update remaining references from user to contexts (#231) There are multiple places throughout the code where we are still referencing users. I have tried to update all the places where a rename seems reasonable or appropriate. There is still some work to do in the test flag builders, but that will be done in a subsequent commit. * Remove new relic integration (#233) The new relic integration was removed many versions ago but a small trace remained behind. * Rename config option private_attribute_names (#234) Co-authored-by: Eli Bishop <[email protected]> * Update test data integration to support contexts (#232) * improve data model validation logging; allow missing/empty attribute for segmentMatch (#236) * Fix JSON serialization failure (#237) When we introduced models for the flag and segment data, we added to_json methods which proxy to the underlying to_json method provided by the `json` gem. We defined the method with a variadic parameter, but we failed to unpack it when passing it on to the underlying implementation. This resulted in a serialization failure which prevented the redis data store from initializing. * fix: Bump eventsource to resolve header parsing (#239) The underlying event source library had an issue where, in certain environments, content-type header detection was failing. This was resolved in v2.2.2 of the event source gem. * Add key to error log for invalid context during variation call for easier debugging (#214) * feat: Add support for payload filtering (#238) * feat: Add data source status provider support (#240) The client instance will now provide access to a `data_source_status_provider`. This provider allows developers to retrieve the status of the SDK on demand, or asynchronously by registering listeners. * test: Skip database integration tests by default (#241) The full unit test suite includes tests relying on externally instances of Redis, Consult, and DynamoDB. By default, we do not want to run these tests. Rather, they should be opt-in by setting the environment variable `LD_SKIP_DATABASE_TESTS=0`. * feat: Introduce flag change tracker api (#242) The client instance will now provide access to a `flag_tracker`. This tracker allows developers to be notified when a flag configuration changes (or optionally when the /value/ of a flag changes for a particular context). * ci: Add code coverage generation (#244) * feat: Add support for data store status monitoring (#243) The client instance will now provide access to a `data_store_status_provider`. This provider allows developers to retrieve the data store status of the SDK on demand, or asynchronously by registering listeners. * Fix monitoring_enabled? access --------- Co-authored-by: Eli Bishop <[email protected]> Co-authored-by: LaunchDarklyCI <[email protected]> Co-authored-by: Jacob Smith <[email protected]> Co-authored-by: Elliot <[email protected]> Co-authored-by: Ben Woskow <[email protected]> Co-authored-by: Ben Woskow <[email protected]> Co-authored-by: hroederld <[email protected]> Co-authored-by: Kerrie Martinez <[email protected]> Co-authored-by: pellyg-ld <[email protected]> Co-authored-by: Sam Stokes <[email protected]> Co-authored-by: LaunchDarklyReleaseBot <[email protected]> Co-authored-by: Ember Stevens <[email protected]> Co-authored-by: ember-stevens <[email protected]> Co-authored-by: Louis Chan <[email protected]> Co-authored-by: Matthew M. Keeler <[email protected]> Co-authored-by: Ben Levy <[email protected]> Co-authored-by: Ben Levy <[email protected]> Co-authored-by: Matthew M. Keeler <[email protected]> Co-authored-by: Louis Chan <[email protected]> Co-authored-by: Matt Hooks <[email protected]>
1 parent d8cc735 commit 01dc51d

35 files changed

+2138
-74
lines changed

.circleci/config.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ jobs:
3030
build-test-windows:
3131
executor: win/default
3232

33+
environment:
34+
LD_SKIP_DATABASE_TESTS: 0
35+
LD_ENABLE_CODE_COVERAGE: 1
36+
3337
steps:
3438
- checkout
3539

@@ -79,6 +83,7 @@ jobs:
7983
- run: bundle _2.2.33_ install
8084
- run: mkdir /tmp/circle-artifacts
8185
- run: bundle _2.2.33_ exec rspec --format documentation --format RspecJunitFormatter -o /tmp/circle-artifacts/rspec.xml spec
86+
- run: mv coverage /tmp/circle-artifacts/
8287

8388
- store_test_results:
8489
path: /tmp/circle-artifacts
@@ -99,6 +104,10 @@ jobs:
99104
- image: redis
100105
- image: amazon/dynamodb-local
101106

107+
environment:
108+
LD_SKIP_DATABASE_TESTS: 0
109+
LD_ENABLE_CODE_COVERAGE: 1
110+
102111
steps:
103112
- checkout
104113
- when:
@@ -116,6 +125,7 @@ jobs:
116125
- run: bundle _2.2.33_ install
117126
- run: mkdir /tmp/circle-artifacts
118127
- run: bundle _2.2.33_ exec rspec --format documentation --format RspecJunitFormatter -o /tmp/circle-artifacts/rspec.xml spec
128+
- run: mv coverage /tmp/circle-artifacts/
119129

120130
- when:
121131
condition:

.ldrelease/config.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ jobs:
2020
image: ruby:2.7-buster
2121
template:
2222
name: ruby
23-
env:
24-
LD_SKIP_DATABASE_TESTS: "1" # Don't run Redis/Consul/DynamoDB tests in release; they are run in CI
2523

2624
documentation:
2725
gitHubPages: true

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ To run all unit tests:
3030
bundle exec rspec spec
3131
```
3232

33-
By default, the full unit test suite includes live tests of the integrations for Consul, DynamoDB, and Redis. Those tests expect you to have instances of all of those databases running locally. To skip them, set the environment variable `LD_SKIP_DATABASE_TESTS=1` before running the tests.
33+
The full unit test suite includes live tests of the integrations for Consul, DynamoDB, and Redis. Those tests expect you to have instances of all of those databases running locally. By default, these tests will be skipped. To run them, set the environment variable `LD_SKIP_DATABASE_TESTS=0` before running the tests.
3434

3535
### Building documentation
3636

launchdarkly-server-sdk.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
2323

2424
spec.add_development_dependency "aws-sdk-dynamodb", "~> 1.57"
2525
spec.add_development_dependency "bundler", "2.2.33"
26+
spec.add_development_dependency "simplecov", "~> 0.21"
2627
spec.add_development_dependency "rspec", "~> 3.10"
2728
spec.add_development_dependency "diplomat", "~> 2.6"
2829
spec.add_development_dependency "redis", "~> 5.0"

lib/ldclient-rb/config.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,23 @@ def initialize(opts = {})
9090
@big_segments = opts[:big_segments] || BigSegmentsConfig.new(store: nil)
9191
@application = LaunchDarkly::Impl::Util.validate_application_info(opts[:application] || {}, @logger)
9292
@payload_filter_key = opts[:payload_filter_key]
93+
@data_source_update_sink = nil
9394
end
9495

96+
#
97+
# Returns the component that allows a data source to push data into the SDK.
98+
#
99+
# This property should only be set by the SDK. Long term access of this
100+
# property is not supported; it is temporarily being exposed to maintain
101+
# backwards compatibility while the SDK structure is updated.
102+
#
103+
# Custom data source implementations should integrate with this sink if
104+
# they want to provide support for data source status listeners.
105+
#
106+
# @private
107+
#
108+
attr_accessor :data_source_update_sink
109+
95110
#
96111
# The base URL for the LaunchDarkly server. This is configurable mainly for testing
97112
# purposes; most users should use the default value.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# frozen_string_literal: true
2+
3+
module LaunchDarkly
4+
module Impl
5+
6+
#
7+
# A generic mechanism for registering event listeners and broadcasting
8+
# events to them.
9+
#
10+
# The SDK maintains an instance of this for each available type of listener
11+
# (flag change, data store status, etc.). They are all intended to share a
12+
# single executor service; notifications are submitted individually to this
13+
# service for each listener.
14+
#
15+
class Broadcaster
16+
def initialize(executor, logger)
17+
@listeners = Concurrent::Set.new
18+
@executor = executor
19+
@logger = logger
20+
end
21+
22+
#
23+
# Register a listener to this broadcaster.
24+
#
25+
# @param listener [#update]
26+
#
27+
def add_listener(listener)
28+
unless listener.respond_to? :update
29+
logger.warn("listener (#{listener.class}) does not respond to :update method. ignoring as registered listener")
30+
return
31+
end
32+
33+
listeners.add(listener)
34+
end
35+
36+
#
37+
# Removes a registered listener from this broadcaster.
38+
#
39+
def remove_listener(listener)
40+
listeners.delete(listener)
41+
end
42+
43+
def has_listeners?
44+
!listeners.empty?
45+
end
46+
47+
#
48+
# Broadcast the provided event to all registered listeners.
49+
#
50+
# Each listener will be notified using the broadcasters executor. This
51+
# method is non-blocking.
52+
#
53+
def broadcast(event)
54+
listeners.each do |listener|
55+
executor.post do
56+
begin
57+
listener.update(event)
58+
rescue StandardError => e
59+
logger.error("listener (#{listener.class}) raised exception (#{e}) processing event (#{event.class})")
60+
end
61+
end
62+
end
63+
end
64+
65+
66+
private
67+
68+
# @return [Concurrent::ThreadPoolExecutor]
69+
attr_reader :executor
70+
71+
# @return [Logger]
72+
attr_reader :logger
73+
74+
# @return [Concurrent::Set]
75+
attr_reader :listeners
76+
end
77+
end
78+
end
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
require "concurrent"
2+
require "forwardable"
3+
require "ldclient-rb/impl/dependency_tracker"
4+
require "ldclient-rb/interfaces"
5+
require "set"
6+
7+
module LaunchDarkly
8+
module Impl
9+
module DataSource
10+
class StatusProvider
11+
include LaunchDarkly::Interfaces::DataSource::StatusProvider
12+
13+
extend Forwardable
14+
def_delegators :@status_broadcaster, :add_listener, :remove_listener
15+
16+
def initialize(status_broadcaster, update_sink)
17+
# @type [Broadcaster]
18+
@status_broadcaster = status_broadcaster
19+
# @type [UpdateSink]
20+
@data_source_update_sink = update_sink
21+
end
22+
23+
def status
24+
@data_source_update_sink.current_status
25+
end
26+
end
27+
28+
class UpdateSink
29+
include LaunchDarkly::Interfaces::DataSource::UpdateSink
30+
31+
# @return [LaunchDarkly::Interfaces::DataSource::Status]
32+
attr_reader :current_status
33+
34+
def initialize(data_store, status_broadcaster, flag_change_broadcaster)
35+
# @type [LaunchDarkly::Interfaces::FeatureStore]
36+
@data_store = data_store
37+
# @type [Broadcaster]
38+
@status_broadcaster = status_broadcaster
39+
# @type [Broadcaster]
40+
@flag_change_broadcaster = flag_change_broadcaster
41+
@dependency_tracker = LaunchDarkly::Impl::DependencyTracker.new
42+
43+
@mutex = Mutex.new
44+
@current_status = LaunchDarkly::Interfaces::DataSource::Status.new(
45+
LaunchDarkly::Interfaces::DataSource::Status::INITIALIZING,
46+
Time.now,
47+
nil)
48+
end
49+
50+
def init(all_data)
51+
old_data = nil
52+
monitor_store_update do
53+
if @flag_change_broadcaster.has_listeners?
54+
old_data = {}
55+
LaunchDarkly::ALL_KINDS.each do |kind|
56+
old_data[kind] = @data_store.all(kind)
57+
end
58+
end
59+
60+
@data_store.init(all_data)
61+
end
62+
63+
update_full_dependency_tracker(all_data)
64+
65+
return if old_data.nil?
66+
67+
send_change_events(
68+
compute_changed_items_for_full_data_set(old_data, all_data)
69+
)
70+
end
71+
72+
def upsert(kind, item)
73+
monitor_store_update { @data_store.upsert(kind, item) }
74+
75+
# TODO(sc-197908): We only want to do this if the store successfully
76+
# updates the record.
77+
@dependency_tracker.update_dependencies_from(kind, item[:key], item)
78+
if @flag_change_broadcaster.has_listeners?
79+
affected_items = Set.new
80+
@dependency_tracker.add_affected_items(affected_items, {kind: kind, key: item[:key]})
81+
send_change_events(affected_items)
82+
end
83+
end
84+
85+
def delete(kind, key, version)
86+
monitor_store_update { @data_store.delete(kind, key, version) }
87+
88+
@dependency_tracker.update_dependencies_from(kind, key, nil)
89+
if @flag_change_broadcaster.has_listeners?
90+
affected_items = Set.new
91+
@dependency_tracker.add_affected_items(affected_items, {kind: kind, key: key})
92+
send_change_events(affected_items)
93+
end
94+
end
95+
96+
def update_status(new_state, new_error)
97+
return if new_state.nil?
98+
99+
status_to_broadcast = nil
100+
101+
@mutex.synchronize do
102+
old_status = @current_status
103+
104+
if new_state == LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED && old_status.state == LaunchDarkly::Interfaces::DataSource::Status::INITIALIZING
105+
# See {LaunchDarkly::Interfaces::DataSource::UpdateSink#update_status} for more information
106+
new_state = LaunchDarkly::Interfaces::DataSource::Status::INITIALIZING
107+
end
108+
109+
unless new_state == old_status.state && new_error.nil?
110+
@current_status = LaunchDarkly::Interfaces::DataSource::Status.new(
111+
new_state,
112+
new_state == current_status.state ? current_status.state_since : Time.now,
113+
new_error.nil? ? current_status.last_error : new_error
114+
)
115+
status_to_broadcast = current_status
116+
end
117+
end
118+
119+
@status_broadcaster.broadcast(status_to_broadcast) unless status_to_broadcast.nil?
120+
end
121+
122+
private def update_full_dependency_tracker(all_data)
123+
@dependency_tracker.reset
124+
all_data.each do |kind, items|
125+
items.each do |key, item|
126+
@dependency_tracker.update_dependencies_from(kind, item.key, item)
127+
end
128+
end
129+
end
130+
131+
132+
#
133+
# Method to monitor updates to the store. You provide a block to update
134+
# the store. This mthod wraps that block, catching and re-raising all
135+
# errors, and notifying all status listeners of the error.
136+
#
137+
private def monitor_store_update
138+
begin
139+
yield
140+
rescue => e
141+
error_info = LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(LaunchDarkly::Interfaces::DataSource::ErrorInfo::STORE_ERROR, 0, e.to_s, Time.now)
142+
update_status(LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED, error_info)
143+
raise
144+
end
145+
end
146+
147+
148+
#
149+
# @param [Hash] old_data
150+
# @param [Hash] new_data
151+
# @return [Set]
152+
#
153+
private def compute_changed_items_for_full_data_set(old_data, new_data)
154+
affected_items = Set.new
155+
156+
LaunchDarkly::ALL_KINDS.each do |kind|
157+
old_items = old_data[kind] || {}
158+
new_items = new_data[kind] || {}
159+
160+
old_items.keys.concat(new_items.keys).each do |key|
161+
old_item = old_items[key]
162+
new_item = new_items[key]
163+
164+
next if old_item.nil? && new_item.nil?
165+
166+
if old_item.nil? || new_item.nil? || old_item[:version] < new_item[:version]
167+
@dependency_tracker.add_affected_items(affected_items, {kind: kind, key: key.to_s})
168+
end
169+
end
170+
end
171+
172+
affected_items
173+
end
174+
175+
#
176+
# @param affected_items [Set]
177+
#
178+
private def send_change_events(affected_items)
179+
affected_items.each do |item|
180+
if item[:kind] == LaunchDarkly::FEATURES
181+
@flag_change_broadcaster.broadcast(LaunchDarkly::Interfaces::FlagChange.new(item[:key]))
182+
end
183+
end
184+
end
185+
end
186+
end
187+
end
188+
end

0 commit comments

Comments
 (0)