class RecordCache::Strategy::UniqueIndexCache
Public Class Methods
attributes(base)
click to toggle source
All attributes with a unique index for the given model
# File lib/record_cache/strategy/unique_index_cache.rb, line 6 def self.attributes(base) (@attributes ||= {})[base.name] ||= [] end
new(base, attribute, record_store, options, type)
click to toggle source
Calls superclass method
RecordCache::Strategy::Base::new
# File lib/record_cache/strategy/unique_index_cache.rb, line 27 def initialize(base, attribute, record_store, options, type) super(base, attribute, record_store, options) # remember the attributes with a unique index UniqueIndexCache.attributes(base) << attribute # for unique indexes that are not on the :id column, use key: rc/<key or model name>/<attribute>: @cache_key_prefix << "#{attribute}:" unless attribute == :id @type = type end
parse(base, record_store, options)
click to toggle source
parse the options and return (an array of) instances of this strategy
# File lib/record_cache/strategy/unique_index_cache.rb, line 11 def self.parse(base, record_store, options) return nil unless base.table_exists? attributes = [options[:unique_index]].flatten.compact # add unique index for :id by default attributes << :id if base.columns_hash['id'] unless base.record_cache[:id] attributes.uniq! # in development mode, do not keep adding 'id' to the list of unique index attributes return nil if attributes.empty? attributes.map do |attribute| type = base.columns_hash[attribute.to_s].try(:type) raise "No column found for unique index '#{index}' on #{base.name}." unless type raise "Incorrect type (expected string or integer, found #{type}) for unique index '#{attribute}' on #{base.name}." unless type == :string || type == :integer UniqueIndexCache.new(base, attribute, record_store, options, type) end end
Public Instance Methods
cacheable?(query)
click to toggle source
Can the cache retrieve the records based on this query?
# File lib/record_cache/strategy/unique_index_cache.rb, line 37 def cacheable?(query) values = query.where_values(@attribute, @type) values && (query.limit.nil? || (query.limit == 1 && values.size == 1)) end
record_change(record, action)
click to toggle source
Update the version store and the record store
# File lib/record_cache/strategy/unique_index_cache.rb, line 43 def record_change(record, action) key = cache_key(record.send(@attribute)) if action == :destroy version_store.delete(key) else # update the version store and add the record to the cache new_version = version_store.renew(key, version_opts) record_store.write(versioned_key(key, new_version), Util.serialize(record)) end end
Protected Instance Methods
fetch_records(query)
click to toggle source
retrieve the record(s) with the given id(s) as an array
# File lib/record_cache/strategy/unique_index_cache.rb, line 57 def fetch_records(query) ids = query.where_values(@attribute, @type) query.wheres.delete(@attribute) # make sure CacheCase.filter! does not see this where anymore id_to_key_map = ids.inject({}){|h,id| h[id] = cache_key(id); h } # retrieve the current version of the records current_versions = version_store.current_multi(id_to_key_map) # get the keys for the records for which a current version was found id_to_version_key_map = Hash[id_to_key_map.map{ |id, key| current_versions[id] ? [id, versioned_key(key, current_versions[id])] : nil }.compact] # retrieve the records from the cache records = id_to_version_key_map.size > 0 ? from_cache(id_to_version_key_map) : [] # query the records with missing ids id_to_key_map.except!(*records.map(&@attribute)) # logging (only in debug mode!) and statistics log_id_cache_hit(ids, id_to_key_map.keys) if RecordCache::Base.logger.debug? statistics.add(ids.size, records.size) if statistics.active? # retrieve records from DB in case there are some missing ids records += from_db(id_to_key_map, id_to_version_key_map) if id_to_key_map.size > 0 # return the array records end
Private Instance Methods
from_cache(id_to_versioned_key_map)
click to toggle source
retrieve the records from the cache with the given keys
# File lib/record_cache/strategy/unique_index_cache.rb, line 83 def from_cache(id_to_versioned_key_map) records = record_store.read_multi(*(id_to_versioned_key_map.values)).values.compact records.map do |record| record = Util.deserialize(record) record.becomes(self.instance_variable_get('@base')) unless record.class == self.instance_variable_get('@base') record end end
from_db(id_to_key_map, id_to_version_key_map)
click to toggle source
retrieve the records with the given ids from the database
# File lib/record_cache/strategy/unique_index_cache.rb, line 93 def from_db(id_to_key_map, id_to_version_key_map) # skip record cache itself RecordCache::Base.without_record_cache do # set version store in multi-mode RecordCache::Base.version_store.multi do # set record store in multi-mode record_store.multi do # retrieve the records from the database records = @base.where(@attribute => id_to_key_map.keys).to_a records.each do |record| versioned_key = id_to_version_key_map[record.send(@attribute)] unless versioned_key # renew the key in the version store in case it was missing key = id_to_key_map[record.send(@attribute)] versioned_key = versioned_key(key, version_store.renew_for_read(key, version_opts)) end # store the record based on the versioned key record_store.write(versioned_key, Util.serialize(record)) end records end end end end
log_id_cache_hit(ids, missing_ids)
click to toggle source
log cache hit/miss to debug log
# File lib/record_cache/strategy/unique_index_cache.rb, line 121 def log_id_cache_hit(ids, missing_ids) hit = missing_ids.empty? ? "hit" : ids.size == missing_ids.size ? "miss" : "partial hit" missing = missing_ids.empty? || ids.size == missing_ids.size ? "" : ": missing #{missing_ids.inspect}" msg = "UniqueIndexCache on '#{@base.name}.#{@attribute}' #{hit} for ids #{ids.size == 1 ? ids.first.inspect : ids.inspect}#{missing}" RecordCache::Base.logger.debug{ msg } end