diff --git a/lib/ldclient-rb/impl/data_system/fdv2.rb b/lib/ldclient-rb/impl/data_system/fdv2.rb index 860b0238..330b8e66 100644 --- a/lib/ldclient-rb/impl/data_system/fdv2.rb +++ b/lib/ldclient-rb/impl/data_system/fdv2.rb @@ -129,7 +129,7 @@ def start # Start the main coordination thread main_thread = Thread.new { run_main_loop } main_thread.name = "FDv2-main" - @threads << main_thread + @lock.synchronize { @threads << main_thread } @ready_event end @@ -148,12 +148,19 @@ def stop end end - # Wait for all threads to complete - @threads.each do |thread| - next unless thread.alive? + # Wait for all threads to complete, including any started while we were joining + joined = [] + loop do + pending = @lock.synchronize { @threads.dup } - joined + break if pending.empty? - thread.join(5.0) # 5 second timeout - @logger.warn { "[LDClient] Thread #{thread.name} did not terminate in time" } if thread.alive? + pending.each do |thread| + joined << thread + next unless thread.alive? + + thread.join(5.0) # 5 second timeout + @logger.warn { "[LDClient] Thread #{thread.name} did not terminate in time" } if thread.alive? + end end # Close the store @@ -333,7 +340,7 @@ def run_synchronizers # Start synchronizer loop in a separate thread sync_thread = Thread.new { synchronizer_loop } sync_thread.name = "FDv2-synchronizers" - @threads << sync_thread + @lock.synchronize { @threads << sync_thread } end # @@ -470,8 +477,8 @@ def consume_synchronizer_results(synchronizer, check_recovery: false) # Set ready event on valid update if update.state == LaunchDarkly::Interfaces::DataSource::Status::VALID - @ready_event.set record_environment_id(update.environment_id) + @ready_event.set end # Update status diff --git a/spec/impl/data_system/fdv2_datasystem_spec.rb b/spec/impl/data_system/fdv2_datasystem_spec.rb index eb724467..15ba84cc 100644 --- a/spec/impl/data_system/fdv2_datasystem_spec.rb +++ b/spec/impl/data_system/fdv2_datasystem_spec.rb @@ -235,14 +235,12 @@ def with_data_system(initializers, synchronizers) .build changed = Concurrent::Event.new - changes = [] - count = 0 + changes = Concurrent::Array.new listener = Object.new listener.define_singleton_method(:update) do |flag_change| - count += 1 changes << flag_change - changed.set if count == 2 + changed.set if changes.length >= 2 end fdv2 = FDv2.new(sdk_key, config, data_system_config) @@ -254,9 +252,7 @@ def with_data_system(initializers, synchronizers) td.update(td.flag("flagkey").on(false)) expect(changed.wait(2)).to be true - expect(changes.length).to eq(2) - expect(changes[0].key).to eq("flagkey") - expect(changes[1].key).to eq("flagkey") + expect(changes.map(&:key).uniq).to eq(["flagkey"]) fdv2.stop end @@ -441,12 +437,12 @@ def with_data_system(initializers, synchronizers) .build changed = Concurrent::Event.new - changes = [] + flag_keys = Concurrent::Array.new listener = Object.new listener.define_singleton_method(:update) do |flag_change| - changes << flag_change - changed.set if changes.length >= 2 + flag_keys << flag_change.key + changed.set if flag_keys.include?("initialflag") && flag_keys.include?("fdv1replacementflag") end fdv2 = FDv2.new(sdk_key, config, data_system_config) @@ -454,12 +450,7 @@ def with_data_system(initializers, synchronizers) ready_event = fdv2.start expect(ready_event.wait(2)).to be true - expect(changed.wait(3)).to be true - - # Verify we got changes for both flags - flag_keys = changes.map { |change| change.key } - expect(flag_keys).to include("initialflag") - expect(flag_keys).to include("fdv1replacementflag") + expect(changed.wait(3)).to be(true), "expected changes for both flags, got #{flag_keys.to_a}" fdv2.stop end