From fc7581fa54d37071e369d2d7e49095c3a42bc4bf Mon Sep 17 00:00:00 2001 From: Matthew Keeler Date: Thu, 27 Aug 2026 14:51:42 -0400 Subject: [PATCH 1/2] test: Compare DataKind hash values with eq instead of be RSpec's `be` matcher compares with `equal?`, i.e. object identity. On CRuby that is indistinguishable from value equality for Integers in Fixnum range because they are immediates, so these expectations passed. JRuby boxes Integers as objects. RubyFixnum#equal? only falls back to value comparison when the value satisfies `fixnumable`, i.e. |value| <= Long.MAX_VALUE / 2; above that it uses Java object identity, and two separately computed hashes are never the same object. Ruby seeds `hash` randomly per process, so roughly half of all runs produce a value over that threshold and the examples fail. Value equality is what these examples actually mean to assert. Co-Authored-By: Claude Opus 5 (1M context) --- spec/impl/data_store_spec.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/spec/impl/data_store_spec.rb b/spec/impl/data_store_spec.rb index cce0964e..2a1a6789 100644 --- a/spec/impl/data_store_spec.rb +++ b/spec/impl/data_store_spec.rb @@ -36,25 +36,25 @@ module DataStore describe "hash" do it "constant instances are equal to themselves" do - expect(FEATURES.hash).to be FEATURES.hash - expect(SEGMENTS.hash).to be SEGMENTS.hash + expect(FEATURES.hash).to eq FEATURES.hash + expect(SEGMENTS.hash).to eq SEGMENTS.hash end it "same constructions are equal" do - expect(FEATURES.hash).to be DataKind.new(namespace: "features", priority: 1).hash - expect(DataKind.new(namespace: "features", priority: 1).hash).to be DataKind.new(namespace: "features", priority: 1).hash + expect(FEATURES.hash).to eq DataKind.new(namespace: "features", priority: 1).hash + expect(DataKind.new(namespace: "features", priority: 1).hash).to eq DataKind.new(namespace: "features", priority: 1).hash - expect(SEGMENTS.hash).to be DataKind.new(namespace: "segments", priority: 0).hash - expect(DataKind.new(namespace: "segments", priority: 0).hash).to be DataKind.new(namespace: "segments", priority: 0).hash + expect(SEGMENTS.hash).to eq DataKind.new(namespace: "segments", priority: 0).hash + expect(DataKind.new(namespace: "segments", priority: 0).hash).to eq DataKind.new(namespace: "segments", priority: 0).hash end it "distinct namespaces are not equal" do - expect(DataKind.new(namespace: "features", priority: 1).hash).not_to be DataKind.new(namespace: "segments", priority: 1).hash + expect(DataKind.new(namespace: "features", priority: 1).hash).not_to eq DataKind.new(namespace: "segments", priority: 1).hash end it "distinct priorities are not equal" do - expect(DataKind.new(namespace: "features", priority: 1).hash).not_to be DataKind.new(namespace: "features", priority: 2).hash - expect(DataKind.new(namespace: "segments", priority: 1).hash).not_to be DataKind.new(namespace: "segments", priority: 2).hash + expect(DataKind.new(namespace: "features", priority: 1).hash).not_to eq DataKind.new(namespace: "features", priority: 2).hash + expect(DataKind.new(namespace: "segments", priority: 1).hash).not_to eq DataKind.new(namespace: "segments", priority: 2).hash end end end From 8e867065772300632e35cdb28106ce4f847d05ac Mon Sep 17 00:00:00 2001 From: Matthew Keeler Date: Thu, 27 Aug 2026 14:51:42 -0400 Subject: [PATCH 2/2] fix: Publish the OFF data source status before releasing polling waiters On an unrecoverable polling error the processor set the ready event and only then ran stop_with_error_info, which is what broadcasts the OFF status. A caller that returned from start could therefore observe a data source status that did not yet reflect the failure. Broadcast the status first, then release the waiters. RepeatingTask#stop does not join when called from the task's own thread, so ordering the calls this way cannot block the ready event. Co-Authored-By: Claude Opus 5 (1M context) --- lib/ldclient-rb/impl/data_source/polling.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/ldclient-rb/impl/data_source/polling.rb b/lib/ldclient-rb/impl/data_source/polling.rb index 13e22448..6f01138b 100644 --- a/lib/ldclient-rb/impl/data_source/polling.rb +++ b/lib/ldclient-rb/impl/data_source/polling.rb @@ -67,8 +67,11 @@ def poll error_info ) else - @ready.set # if client was waiting on us, make it stop waiting - has no effect if already set + # Publish the OFF status before releasing anyone waiting on the + # ready event, so a client that returns from start can rely on the + # data source status already reflecting the failure. stop_with_error_info error_info + @ready.set # if client was waiting on us, make it stop waiting - has no effect if already set end rescue StandardError => e Impl::Util.log_exception(@config.logger, "Exception while polling", e)