Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5003b43
chore: Add FDv2 compatible data source for testing
jsonbailey Jan 7, 2026
065f066
Update lib/ldclient-rb/integrations/test_data_v2/flag_builder_v2.rb
jsonbailey Jan 12, 2026
e99c7fc
adding tests and including fixes identified during tests
jsonbailey Jan 12, 2026
8a8b294
fix tests
jsonbailey Jan 12, 2026
13cd0a4
fix deep copy issue
jsonbailey Jan 13, 2026
2fc9fa0
fix lint error
jsonbailey Jan 13, 2026
1077e2d
because of thread.join with timeouts increase waits to avoid flaky tests
jsonbailey Jan 13, 2026
7066680
chore: Create FDv2 and fallback polling data source
jsonbailey Jan 13, 2026
4f75f75
address feedback
jsonbailey Jan 13, 2026
4b79d47
Merge branch 'main' into jb/sdk-1544/fdv2-polling-data-source
jsonbailey Jan 13, 2026
4bf0b8f
one more name change
jsonbailey Jan 13, 2026
b97a23d
prevent immediate retry
jsonbailey Jan 13, 2026
fa0bb87
always set selector in changeset builder
jsonbailey Jan 13, 2026
6547940
chore: Create FDv2 streaming data source
jsonbailey Jan 13, 2026
2d00acf
fix lint issues
jsonbailey Jan 14, 2026
e4fdafe
enable updating basis on reconnect
jsonbailey Jan 15, 2026
6e0a89b
Merge branch 'main' into jb/sdk-1545/fdv2-streaming-data-source
jsonbailey Jan 20, 2026
e7458e2
fix lint error
jsonbailey Jan 20, 2026
a0725ef
Merge branch 'main' into jb/sdk-1545/fdv2-streaming-data-source
jsonbailey Jan 20, 2026
dc700f0
stop streamer if we are falling back
jsonbailey Jan 20, 2026
b3ffac4
enable fdv2 configs
jsonbailey Jan 21, 2026
021b0b2
fix streaming sync to block while running
jsonbailey Jan 21, 2026
3761f39
address feedback
jsonbailey Jan 21, 2026
7c73f81
address feedback
jsonbailey Jan 21, 2026
7745751
chore: Switch to using symbols in fdv2 for consistency with v1
jsonbailey Jan 22, 2026
170e389
Merge branch 'main' into jb/sdk-1547/test-persistent-data-store-recovery
jsonbailey Jan 22, 2026
4e9325c
fix mismatch
jsonbailey Jan 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def get(kind, key)
items_of_kind = @items[kind]
return nil if items_of_kind.nil?

item = items_of_kind[key]
item = items_of_kind[key.to_sym]
return nil if item.nil?
return nil if item[:deleted]

Expand Down
4 changes: 2 additions & 2 deletions lib/ldclient-rb/impl/data_store/store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ def get_data_store_status_provider
# Convert a list of Changes to the pre-existing format used by FeatureStore.
#
# @param changes [Array<LaunchDarkly::Interfaces::DataSystem::Change>] List of changes
# @return [Hash{DataKind => Hash{String => Hash}}] Hash suitable for FeatureStore operations
# @return [Hash{DataKind => Hash{Symbol => Hash}}] Hash suitable for FeatureStore operations
#
private def changes_to_store_data(changes)
all_data = {
Expand All @@ -307,7 +307,7 @@ def get_data_store_status_provider
#
# Reset dependency tracker with new full data set.
#
# @param all_data [Hash{DataKind => Hash{String => Hash}}] Hash of data kinds to items
# @param all_data [Hash{DataKind => Hash{Symbol => Hash}}] Hash of data kinds to items
# @return [void]
#
private def reset_dependency_tracker(all_data)
Expand Down
2 changes: 1 addition & 1 deletion lib/ldclient-rb/impl/data_system/polling.rb
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ def self.fdv1_polling_payload_to_changeset(data)
version = flag_or_segment[:version]
return LaunchDarkly::Result.fail("Invalid format: #{key} does not have a version set") if version.nil?

builder.add_put(kind, key.to_s, version, flag_or_segment)
builder.add_put(kind, key, version, flag_or_segment)
end
end

Expand Down
12 changes: 6 additions & 6 deletions lib/ldclient-rb/impl/data_system/protocolv2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ class DeleteObject
# @return [String] The object kind ({LaunchDarkly::Interfaces::DataSystem::ObjectKind})
attr_reader :kind

# @return [String] The key
# @return [Symbol] The key
attr_reader :key

#
# @param version [Integer] The version
# @param kind [String] The object kind ({LaunchDarkly::Interfaces::DataSystem::ObjectKind})
# @param key [String] The key
# @param key [Symbol] The key
#
def initialize(version:, kind:, key:)
@version = version
Expand Down Expand Up @@ -72,7 +72,7 @@ def self.from_h(data)

raise ArgumentError, "Missing required fields in DeleteObject" if version.nil? || kind.nil? || key.nil?

new(version: version, kind: kind, key: key)
new(version: version, kind: kind, key: key.to_sym)
end
end

Expand All @@ -89,7 +89,7 @@ class PutObject
# @return [String] The object kind ({LaunchDarkly::Interfaces::DataSystem::ObjectKind})
attr_reader :kind

# @return [String] The key
# @return [Symbol] The key
attr_reader :key

# @return [Hash] The object data
Expand All @@ -98,7 +98,7 @@ class PutObject
#
# @param version [Integer] The version
# @param kind [String] The object kind ({LaunchDarkly::Interfaces::DataSystem::ObjectKind})
# @param key [String] The key
# @param key [Symbol] The key
# @param object [Hash] The object data
#
def initialize(version:, kind:, key:, object:)
Expand Down Expand Up @@ -146,7 +146,7 @@ def self.from_h(data)

raise ArgumentError, "Missing required fields in PutObject" if version.nil? || kind.nil? || key.nil? || object_data.nil?

new(version: version, kind: kind, key: key, object: object_data)
new(version: version, kind: kind, key: key.to_sym, object: object_data)
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,10 @@ def upsert_flag(flag_data)
builder.start(LaunchDarkly::Interfaces::DataSystem::IntentCode::TRANSFER_CHANGES)

# Add the updated flag
flag_key = flag_data[:key].to_sym
builder.add_put(
LaunchDarkly::Interfaces::DataSystem::ObjectKind::FLAG,
flag_data[:key],
flag_key,
flag_data[:version] || 1,
flag_data
)
Expand Down Expand Up @@ -247,9 +248,10 @@ def upsert_segment(segment_data)
builder.start(LaunchDarkly::Interfaces::DataSystem::IntentCode::TRANSFER_CHANGES)

# Add the updated segment
segment_key = segment_data[:key].to_sym
builder.add_put(
LaunchDarkly::Interfaces::DataSystem::ObjectKind::SEGMENT,
segment_data[:key],
segment_key,
segment_data[:version] || 1,
segment_data
)
Expand Down
2 changes: 1 addition & 1 deletion lib/ldclient-rb/impl/store_data_set_sorter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def self.sort_collection(kind, input)
items_out = {}
until remaining_items.empty?
# pick a random item that hasn't been updated yet
key, item = remaining_items.first
_, item = remaining_items.first
self.add_with_dependencies_first(item, dependency_fn, remaining_items, items_out)
end
items_out
Expand Down
7 changes: 4 additions & 3 deletions lib/ldclient-rb/integrations/test_data_v2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,13 @@ def update(flag_builder)
instances_copy = []
new_flag = nil
@lock.with_write_lock do
old_flag = @current_flags[flag_builder._key]
flag_key = flag_builder._key.to_sym
old_flag = @current_flags[flag_key]
old_version = old_flag ? old_flag[:version] : 0

new_flag = flag_builder.build(old_version + 1)

@current_flags[flag_builder._key] = new_flag
@current_flags[flag_key] = new_flag
@flag_builders[flag_builder._key] = flag_builder.clone

# Create a copy of instances while holding the lock to avoid race conditions
Expand Down Expand Up @@ -200,7 +201,7 @@ def use_preconfigured_segment(segment)
else
segment.as_json
end
segment_key = segment_hash[:key]
segment_key = segment_hash[:key].to_sym

old_segment = @current_segments[segment_key]
old_version = old_segment ? old_segment[:version] : 0
Expand Down
8 changes: 4 additions & 4 deletions lib/ldclient-rb/interfaces/data_system.rb
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ class Change
# @return [String] The kind ({ObjectKind})
attr_reader :kind

# @return [String] The key
# @return [Symbol] The key
attr_reader :key

# @return [Integer] The version
Expand All @@ -239,7 +239,7 @@ class Change
#
# @param action [String] The action type ({ChangeType})
# @param kind [String] The object kind ({ObjectKind})
# @param key [String] The key
# @param key [Symbol] The key
# @param version [Integer] The version
# @param object [Hash, nil] The object data
#
Expand Down Expand Up @@ -546,7 +546,7 @@ def finish(selector)
# Adds a new object to the changeset.
#
# @param kind [String] The object kind ({ObjectKind})
# @param key [String] The key
# @param key [Symbol] The key
# @param version [Integer] The version
# @param obj [Hash] The object data
# @return [void]
Expand All @@ -565,7 +565,7 @@ def add_put(kind, key, version, obj)
# Adds a deletion to the changeset.
#
# @param kind [String] The object kind ({ObjectKind})
# @param key [String] The key
# @param key [Symbol] The key
# @param version [Integer] The version
# @return [void]
#
Expand Down
4 changes: 2 additions & 2 deletions lib/ldclient-rb/interfaces/feature_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ module FeatureStore
# the correct order), storing each item, and then delete any leftover items at the very end.
#
# @param all_data [Hash] a hash where each key is one of the data kind objects, and each
# value is in turn a hash of string keys to entities
# value is in turn a hash of symbol keys to entities
# @return [void]
#
def init(all_data)
Expand All @@ -46,7 +46,7 @@ def init(all_data)
# Returns the entity to which the specified key is mapped, if any.
#
# @param kind [Object] the kind of entity to get
# @param key [String] the unique key of the entity to get
# @param key [String, Symbol] the unique key of the entity to get
# @return [Hash] the entity; nil if the key was not found, or if the stored entity's
# `:deleted` property was true
#
Expand Down
51 changes: 51 additions & 0 deletions spec/impl/data_store/in_memory_feature_store_v2_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
require "spec_helper"
require "ldclient-rb/impl/data_store/in_memory_feature_store"
require "ldclient-rb/impl/data_store"

module LaunchDarkly
module Impl
module DataStore
describe InMemoryFeatureStoreV2 do
let(:logger) { double.as_null_object }
subject { InMemoryFeatureStoreV2.new(logger) }

let(:flag_key) { "test-flag" }
let(:flag) do
{
key: flag_key,
version: 1,
on: true,
fallthrough: { variation: 0 },
variations: [true, false],
}
end

describe "#get with string/symbol key compatibility" do
before do
# Store items with symbol keys (as done by FDv2 protocol layer)
collections = {
FEATURES => { flag_key.to_sym => flag },
}
subject.set_basis(collections)
end

it "retrieves items with string keys (critical for variation calls)" do
result = subject.get(FEATURES, flag_key)
expect(result).to be_a(LaunchDarkly::Impl::Model::FeatureFlag)
expect(result.key).to eq(flag_key)
end

it "retrieves items with symbol keys" do
result = subject.get(FEATURES, flag_key.to_sym)
expect(result).to be_a(LaunchDarkly::Impl::Model::FeatureFlag)
expect(result.key).to eq(flag_key)
end

it "returns nil for non-existent keys" do
expect(subject.get(FEATURES, "nonexistent")).to be_nil
end
end
end
end
end
end
Loading