Skip to content

Commit

Permalink
fix: Modify data kind constants to avoid Process.warmup rehash (#301)
Browse files Browse the repository at this point in the history
The data stores in the SDK have to deal with multiple types of data --
features (or flags) and segments. These types have long been simple
hashes in our SDK, with one defining an optional lambda property.

With Ruby 3.3 and the introduction of `Process.warmup`, we have seen an
issue where, after warmup, the in memory store needs a `rehash` method
call before these kind constants can be used reliably to access the
store.

To combat this, I am moving these kinds into a class. This class as
explicit hash key behaviors, so theoretically shouldn't have this
problem Local testing has shown this to be the case.

This class is also given a dictionary interface to maintain compliance
with the existing implementation. While people should not be relying on
these constants explicitly, they do flow through the system in ways that
might make their signatures somewhat public.
  • Loading branch information
keelerm84 authored Oct 30, 2024
1 parent 354ef13 commit 06f11f0
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 9 deletions.
50 changes: 50 additions & 0 deletions lib/ldclient-rb/impl/data_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,56 @@
module LaunchDarkly
module Impl
module DataStore

class DataKind
FEATURES = "features".freeze
SEGMENTS = "segments".freeze

FEATURE_PREREQ_FN = lambda { |flag| (flag[:prerequisites] || []).map { |p| p[:key] } }.freeze

attr_reader :namespace
attr_reader :priority

#
# @param namespace [String]
# @param priority [Integer]
#
def initialize(namespace:, priority:)
@namespace = namespace
@priority = priority
end

#
# Maintain the same behavior when these data kinds were standard ruby hashes.
#
# @param key [Symbol]
# @return [Object]
#
def [](key)
return priority if key == :priority
return namespace if key == :namespace
return get_dependency_keys_fn() if key == :get_dependency_keys
nil
end

#
# Retrieve the dependency keys for a particular data kind. Right now, this is only defined for flags.
#
def get_dependency_keys_fn()
return nil unless @namespace == FEATURES

FEATURE_PREREQ_FN
end

def eql?(other)
namespace == other.namespace && priority == other.priority
end

def hash
[namespace, priority].hash
end
end

class StatusProvider
include LaunchDarkly::Interfaces::DataStore::StatusProvider

Expand Down
11 changes: 2 additions & 9 deletions lib/ldclient-rb/in_memory_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,10 @@ module LaunchDarkly
# to ensure data consistency during non-atomic updates.

# @private
FEATURES = {
namespace: "features",
priority: 1, # that is, features should be stored after segments
get_dependency_keys: lambda { |flag| (flag[:prerequisites] || []).map { |p| p[:key] } },
}.freeze
FEATURES = Impl::DataStore::DataKind.new(namespace: "features", priority: 1).freeze

# @private
SEGMENTS = {
namespace: "segments",
priority: 0,
}.freeze
SEGMENTS = Impl::DataStore::DataKind.new(namespace: "segments", priority: 0).freeze

# @private
ALL_KINDS = [FEATURES, SEGMENTS].freeze
Expand Down

0 comments on commit 06f11f0

Please sign in to comment.