class Arturo::FeatureCaching::AllStrategy

Public Class Methods

expire(cache, symbol) click to toggle source
# File lib/arturo/feature_caching.rb, line 84
def expire(cache, symbol)
  cache.delete("arturo.all")
end
fetch(cache, symbol, &block) click to toggle source

@param cache [Arturo::Cache] cache backend @param symbol [Symbol] arturo identifier @return [Arturo::Feature, Arturo::NoSuchFeature]

# File lib/arturo/feature_caching.rb, line 70
def fetch(cache, symbol, &block)
  existing_features = cache.read("arturo.all")

  features = if cache_is_current?(cache, existing_features)
    existing_features
  else
    arturos_from_origin(fallback: existing_features).tap do |updated_features|
      update_and_extend_cache!(cache, updated_features)
    end
  end

  features[symbol] || Arturo::NoSuchFeature.new(symbol)
end
register_cache_update_listener(&block) click to toggle source
# File lib/arturo/feature_caching.rb, line 88
def register_cache_update_listener(&block)
  cache_update_listeners << block
end

Private Class Methods

arturos_from_origin(fallback:) click to toggle source

@param fallback [Hash] features to use on database failure @return [Hash] updated features from origin or fallback @raise [ActiveRecord::ActiveRecordError] on database failure

without cache extension option
# File lib/arturo/feature_caching.rb, line 104
def arturos_from_origin(fallback:)
  Arturo::Feature.all.to_h { |f| [f.symbol.to_sym, f] }
rescue ActiveRecord::ActiveRecordError
  raise unless Arturo::Feature.extend_cache_on_failure?

  if fallback.blank?
    log_empty_cache
    raise
  else
    log_stale_cache
    fallback
  end
end
cache_is_current?(cache, features) click to toggle source

@return [Boolean] whether the current cache has to be updated from origin @raise [ActiveRecord::ActiveRecordError] on database failure

without cache extension option
# File lib/arturo/feature_caching.rb, line 123
def cache_is_current?(cache, features)
  return unless features
  return true if cache.read("arturo.current")

  begin
    return false if origin_changed?(features)  
  rescue ActiveRecord::ActiveRecordError
    raise unless Arturo::Feature.extend_cache_on_failure?

    if features.blank?
      log_empty_cache
      raise
    else
      log_stale_cache
      update_and_extend_cache!(cache, features)
    end

    return true
  end
  mark_as_current!(cache)
end
cache_update_listeners() click to toggle source
# File lib/arturo/feature_caching.rb, line 94
def cache_update_listeners
  @cache_update_listeners ||= []
end
formatted_log(namespace, msg) click to toggle source
# File lib/arturo/feature_caching.rb, line 145
def formatted_log(namespace, msg)
  "[Arturo][#{namespace}] #{msg}"
end
log_empty_cache() click to toggle source
# File lib/arturo/feature_caching.rb, line 149
def log_empty_cache
  Arturo.logger.error(formatted_log('extend_cache_on_failure', 'Fallback cache is empty'))
end
log_stale_cache() click to toggle source
# File lib/arturo/feature_caching.rb, line 153
def log_stale_cache
  Arturo.logger.warn(formatted_log('extend_cache_on_failure', 'Falling back to stale cache'))
end
mark_as_current!(cache) click to toggle source

@return [True]

# File lib/arturo/feature_caching.rb, line 160
def mark_as_current!(cache)
  cache.write("arturo.current", true, expires_in: Arturo::Feature.cache_ttl)
end
origin_changed?(features) click to toggle source

The Arturo origin might return a big payload, so checking for the latest update is a cheaper operation.

@return [Boolean] if origin has been updated since the last cache update.

# File lib/arturo/feature_caching.rb, line 170
def origin_changed?(features)
  features.values.map(&:updated_at).compact.max != Arturo::Feature.maximum(:updated_at)
end
update_and_extend_cache!(cache, features) click to toggle source
# File lib/arturo/feature_caching.rb, line 174
def update_and_extend_cache!(cache, features)
  mark_as_current!(cache)
  cache.write("arturo.all", features, expires_in: Arturo::Feature.cache_ttl * 10)
  cache_update_listeners.each(&:call)
end