class RecordCache::Strategy::IndexCache
Public Class Methods
new(base, attribute, record_store, options)
click to toggle source
Calls superclass method
RecordCache::Strategy::Base::new
# File lib/record_cache/strategy/index_cache.rb, line 20 def initialize(base, attribute, record_store, options) super @cache_key_prefix << "#{attribute}=" 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/index_cache.rb, line 6 def self.parse(base, record_store, options) return nil unless options[:index] return nil unless base.table_exists? raise "Index cache '#{options[:index].inspect}' on #{base.name} is redundant as index cache queries are handled by the full table cache." if options[:full_table] raise ":index => #{options[:index].inspect} option cannot be used unless 'id' is present on #{base.name}" unless base.columns_hash['id'] [options[:index]].flatten.compact.map do |attribute| type = base.columns_hash[attribute.to_s].try(:type) raise "No column found for index '#{attribute}' on #{base.name}." unless type raise "Incorrect type (expected integer, found #{type}) for index '#{attribute}' on #{base.name}." unless type == :integer IndexCache.new(base, attribute, record_store, options) 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/index_cache.rb, line 26 def cacheable?(query) # allow limit of 1 for has_one query.where_value(@attribute) && (query.limit.nil? || (query.limit == 1 && !query.sorted?)) end
record_change(record, action)
click to toggle source
Handle create/update/destroy (use record.previous_changes to find the old values in case of an update)
# File lib/record_cache/strategy/index_cache.rb, line 32 def record_change(record, action) if action == :destroy remove_from_index(record.send(@attribute), record.id) elsif action == :create add_to_index(record.send(@attribute), record.id) else index_change = record.previous_changes[@attribute.to_s] || record.previous_changes[@attribute] return unless index_change remove_from_index(index_change[0], record.id) add_to_index(index_change[1], record.id) end end
Protected Instance Methods
fetch_records(query)
click to toggle source
retrieve the record(s) based on the given query
# File lib/record_cache/strategy/index_cache.rb, line 48 def fetch_records(query) value = query.where_value(@attribute) # make sure CacheCase.filter! does not see this where clause anymore query.wheres.delete(@attribute) # retrieve the cache key for this index and value key = cache_key(value) # retrieve the current version of the ids list current_version = version_store.current(key) # create the versioned key, renew the version in case it was missing in the version store versioned_key = versioned_key(key, current_version || version_store.renew_for_read(key, version_opts)) # retrieve the ids from the local cache based on the current version from the version store ids = current_version ? fetch_ids_from_cache(versioned_key) : nil # logging (only in debug mode!) and statistics log_cache_hit(versioned_key, ids) if RecordCache::Base.logger.debug? statistics.add(1, ids ? 1 : 0) if statistics.active? # retrieve the ids from the DB if the result was not fresh ids = fetch_ids_from_db(versioned_key, value) unless ids # use the IdCache to retrieve the records based on the ids @base.record_cache[:id].send(:fetch_records, ::RecordCache::Query.new({:id => ids})) end
Private Instance Methods
add_to_index(value, id)
click to toggle source
add one record(id) to the index with the given value
# File lib/record_cache/strategy/index_cache.rb, line 92 def add_to_index(value, id) renew_version(value.to_i) { |ids| ids << id } if value end
fetch_ids_from_cache(versioned_key)
click to toggle source
Retrieve the ids from the local cache
# File lib/record_cache/strategy/index_cache.rb, line 74 def fetch_ids_from_cache(versioned_key) record_store.read(versioned_key) end
fetch_ids_from_db(versioned_key, value)
click to toggle source
retrieve the ids from the database and update the local cache
# File lib/record_cache/strategy/index_cache.rb, line 79 def fetch_ids_from_db(versioned_key, value) RecordCache::Base.without_record_cache do # go straight to SQL result for optimal performance sql = @base.select('id').where(@attribute => value).to_sql ids = []; @base.connection.execute(sql).each{ |row| ids << (row.is_a?(Hash) ? row['id'] : row.first).to_i } record_store.write(versioned_key, ids) ids end end
log_cache_hit(key, ids)
click to toggle source
log cache hit/miss to debug log
# File lib/record_cache/strategy/index_cache.rb, line 119 def log_cache_hit(key, ids) RecordCache::Base.logger.debug{ "IndexCache #{ids ? 'hit' : 'miss'} for #{key}: found #{ids ? ids.size : 'no'} ids" } end
remove_from_index(value, id)
click to toggle source
remove one record(id) from the index with the given value
# File lib/record_cache/strategy/index_cache.rb, line 97 def remove_from_index(value, id) renew_version(value.to_i) { |ids| ids.delete(id) } if value end
renew_version(value) { |ids| ... }
click to toggle source
renew the version store and update the local store
# File lib/record_cache/strategy/index_cache.rb, line 102 def renew_version(value, &block) # retrieve local version and increment version store key = cache_key(value) old_version = version_store.current(key) new_version = version_store.renew(key, true, version_opts) # try to update the ids list based on the last version ids = fetch_ids_from_cache(versioned_key(key, old_version)) if ids ids = Array.new(ids) yield ids record_store.write(versioned_key(key, new_version), ids) end end