class AsyncCache::Store
Attributes
backend[RW]
worker_klass[RW]
Public Class Methods
base_cache_key(locator, block_source)
click to toggle source
Build the base part of the cache key with the locator and the digest of the block source. This ensures that if the implementation (block) changes then the cache key will also change.
# File lib/async_cache/store.rb, line 143 def self.base_cache_key(locator, block_source) ActiveSupport::Cache.expand_cache_key([ locator, Digest::MD5.hexdigest(block_source) ]) end
new(opts = {})
click to toggle source
@param [Hash] opts Initialization options @option opts [Object] :backend The backend that it will read/write
entries to/from
@option opts [Symbol] :worker Shorthand symbol for the worker to use,
options are `:active_job` and `:sidekiq`.
@option ops [Class] :worker_klass Class of the worker to use.
# File lib/async_cache/store.rb, line 18 def initialize(opts = {}) @worker_klass = if opts[:worker_klass] opts[:worker_klass] elsif opts[:worker] AsyncCache::Workers.worker_for_name opts[:worker] else raise ArgumentError, 'Must have a :worker_klass or :worker option' end @backend = opts[:backend] || AsyncCache.backend # Register ourselves in the array of known store instances self.class.stores << self end
stores()
click to toggle source
Global index of store instances
# File lib/async_cache/store.rb, line 8 def self.stores @stores ||= [] end
Public Instance Methods
clear()
click to toggle source
# File lib/async_cache/store.rb, line 81 def clear @worker_klass.clear end
determine_strategy(has_cached_data:, needs_regen:, synchronous_regen:)
click to toggle source
# File lib/async_cache/store.rb, line 85 def determine_strategy(has_cached_data:, needs_regen:, synchronous_regen:) case when !has_cached_data # Not present at all :generate when needs_regen && synchronous_regen # Caller has indicated we should synchronously regenerate :generate when needs_regen && !worker_klass.has_workers? # No workers available to regnerate, so do it ourselves; we'll log a # warning message that we can alert on AsyncCache.logger.warn "No workers running to handle AsyncCache jobs" :generate when needs_regen :enqueue else :current end end
enqueue_generation(key:, version:, expires_in:, block_source:, arguments:)
click to toggle source
# File lib/async_cache/store.rb, line 118 def enqueue_generation(key:, version:, expires_in:, block_source:, arguments:) worker_klass.enqueue_async_job( key: key, version: version, expires_in: expires_in, block: block_source, arguments: arguments ) end
fetch(locator, version, options = {}, &block)
click to toggle source
@param [String] locator The constant locator for the entry in the cache @param [Fixnum] version Version of the value identified by that locator @param [Hash] options @yield [*arguments in options] Called if entry out-of-date
# File lib/async_cache/store.rb, line 38 def fetch(locator, version, options = {}, &block) options = options.dup # Duplicate to avoid side effects version = version.to_i # Versions must *always* be convertible to integers # Expires-in must be an integer if present, nil if not expires_in = options[:expires_in] ? options[:expires_in].to_i : nil block_source = block.to_source block_arguments = check_arguments(options.delete(:arguments) || []) # Serialize arguments into the full cache key key = ActiveSupport::Cache.expand_cache_key([ Store.base_cache_key(locator, block_source), block_arguments ].flatten) cached_data, cached_version = @backend.read key strategy = determine_strategy( has_cached_data: !!cached_data, needs_regen: version > (cached_version || 0), synchronous_regen: options[:synchronous_regen] ) return cached_data if strategy == :current context = { key: key, version: version, expires_in: expires_in, block_source: block_source, arguments: block_arguments } case strategy when :generate return generate_and_cache context when :enqueue enqueue_generation context return cached_data end end
generate_and_cache(key:, version:, expires_in:, block_source:, arguments:)
click to toggle source
# File lib/async_cache/store.rb, line 105 def generate_and_cache(key:, version:, expires_in:, block_source:, arguments:) # Mimic the destruction-of-scope behavior of the worker in development # so it will *fail* for developers if they try to depend upon scope block = eval(block_source) data = block.call(*arguments) entry = [data, version] @backend.write key, entry, :expires_in => expires_in return data end
inspect()
click to toggle source
# File lib/async_cache/store.rb, line 128 def inspect pointer_format = '0x%014x' pointer = Kernel.sprintf pointer_format, self.object_id * 2 backend_pointer = Kernel.sprintf pointer_format, @backend.object_id * 2 '#<' + [ "#{self.class.name}:#{pointer} ", "@worker_klass=#{@worker_klass.name}, ", "@backend=#<#{@backend.class.name}:#{backend_pointer}>" ].join('') + '>' end
Private Instance Methods
check_arguments(arguments)
click to toggle source
Ensures the arguments are primitives.
# File lib/async_cache/store.rb, line 153 def check_arguments arguments arguments.each_with_index do |argument, index| next if argument.is_a? Numeric next if argument.is_a? String next if argument.is_a? Symbol next if argument.is_a? Hash next if argument.is_a? NilClass next if argument.is_a? TrueClass next if argument.is_a? FalseClass raise ArgumentError, "Cannot send complex data for block argument #{index + 1}: #{argument.class.name}" end arguments end