class LaunchDarkly::Integrations::Util::CachingStoreWrapper

CachingStoreWrapper is a partial implementation of the {LaunchDarkly::Interfaces::FeatureStore} pattern that delegates part of its behavior to another object, while providing optional caching behavior and other logic that would otherwise be repeated in every feature store implementation. This makes it easier to create new database integrations by implementing only the database-specific logic.

The mixin {FeatureStoreCore} describes the methods that need to be supported by the inner implementation object.

Public Class Methods

new(core, opts) click to toggle source

Creates a new store wrapper instance.

@param core [Object] an object that implements the {FeatureStoreCore} methods @param opts [Hash] a hash that may include cache-related options; all others will be ignored @option opts [Float] :expiration (15) cache TTL; zero means no caching @option opts [Integer] :capacity (1000) maximum number of items in the cache

# File lib/ldclient-rb/integrations/util/store_wrapper.rb, line 29
def initialize(core, opts)
  @core = core

  expiration_seconds = opts[:expiration] || 15
  if expiration_seconds > 0
    capacity = opts[:capacity] || 1000
    @cache = ExpiringCache.new(capacity, expiration_seconds)
  else
    @cache = nil
  end

  @inited = Concurrent::AtomicBoolean.new(false)
end

Public Instance Methods

all(kind) click to toggle source
# File lib/ldclient-rb/integrations/util/store_wrapper.rb, line 74
def all(kind)
  if !@cache.nil?
    items = @cache[all_cache_key(kind)]
    return items if !items.nil?
  end

  items = items_if_not_deleted(@core.get_all_internal(kind))
  @cache[all_cache_key(kind)] = items if !@cache.nil?
  items
end
delete(kind, key, version) click to toggle source
# File lib/ldclient-rb/integrations/util/store_wrapper.rb, line 94
def delete(kind, key, version)
  upsert(kind, { key: key, version: version, deleted: true })
end
get(kind, key) click to toggle source
# File lib/ldclient-rb/integrations/util/store_wrapper.rb, line 58
def get(kind, key)
  if !@cache.nil?
    cache_key = item_cache_key(kind, key)
    cached = @cache[cache_key] # note, item entries in the cache are wrapped in an array so we can cache nil values
    return item_if_not_deleted(cached[0]) if !cached.nil?
  end

  item = @core.get_internal(kind, key)

  if !@cache.nil?
    @cache[cache_key] = [item]
  end

  item_if_not_deleted(item)
end
init(all_data) click to toggle source
# File lib/ldclient-rb/integrations/util/store_wrapper.rb, line 43
def init(all_data)
  @core.init_internal(all_data)
  @inited.make_true

  if !@cache.nil?
    @cache.clear
    all_data.each do |kind, items|
      @cache[kind] = items_if_not_deleted(items)
      items.each do |key, item|
        @cache[item_cache_key(kind, key)] = [item]
      end
    end
  end
end
initialized?() click to toggle source
# File lib/ldclient-rb/integrations/util/store_wrapper.rb, line 98
def initialized?
  return true if @inited.value

  if @cache.nil?
    result = @core.initialized_internal?
  else
    result = @cache[inited_cache_key]
    if result.nil?
      result = @core.initialized_internal?
      @cache[inited_cache_key] = result
    end
  end

  @inited.make_true if result
  result
end
stop() click to toggle source
# File lib/ldclient-rb/integrations/util/store_wrapper.rb, line 115
def stop
  @core.stop
end
upsert(kind, item) click to toggle source
# File lib/ldclient-rb/integrations/util/store_wrapper.rb, line 85
def upsert(kind, item)
  new_state = @core.upsert_internal(kind, item)

  if !@cache.nil?
    @cache[item_cache_key(kind, item[:key])] = [new_state]
    @cache.delete(all_cache_key(kind))
  end
end

Private Instance Methods

all_cache_key(kind) click to toggle source

The result of a call to get_all_internal is cached using the “kind” object as a key.

# File lib/ldclient-rb/integrations/util/store_wrapper.rb, line 127
def all_cache_key(kind)
  kind
end
inited_cache_key() click to toggle source

The result of initialized_internal? is cached using this key.

# File lib/ldclient-rb/integrations/util/store_wrapper.rb, line 132
def inited_cache_key
  "$inited"
end
item_cache_key(kind, key) click to toggle source

We use just one cache for 3 kinds of objects. Individual entities use a key like 'features:my-flag'.

# File lib/ldclient-rb/integrations/util/store_wrapper.rb, line 122
def item_cache_key(kind, key)
  kind[:namespace] + ":" + key.to_s
end
item_if_not_deleted(item) click to toggle source
# File lib/ldclient-rb/integrations/util/store_wrapper.rb, line 136
def item_if_not_deleted(item)
  (item.nil? || item[:deleted]) ? nil : item
end
items_if_not_deleted(items) click to toggle source
# File lib/ldclient-rb/integrations/util/store_wrapper.rb, line 140
def items_if_not_deleted(items)
  items.select { |key, item| !item[:deleted] }
end