class ArCache::Table

Attributes

all[R]
column_indexes[R]
column_names[R]
identity_cache_key[R]
name[R]
primary_key[R]
short_sha1[R]
unique_indexes[R]

Public Class Methods

new(table_name) click to toggle source
Calls superclass method
# File lib/ar_cache/table.rb, line 12
def self.new(table_name)
  @lock.synchronize do
    table = @all.find { |t| t.name == table_name }

    unless table
      table = super
      @all << table
    end

    table
  end
end
new(table_name) click to toggle source
# File lib/ar_cache/table.rb, line 27
def initialize(table_name)
  @name = table_name
  @primary_key = ::ActiveRecord::Base.connection.primary_key(@name)

  options = ArCache::Configuration.get_table_options(@name)
  @disabled = @primary_key.nil? ? true : options[:disabled] # ArCache can't work if primary key does not exist.
  @select_disabled = options[:select_disabled]

  columns = ::ActiveRecord::Base.connection.columns(@name)
  @unique_indexes = normalize_unique_indexes(options[:unique_indexes], columns).freeze
  @column_indexes = @unique_indexes.flatten.uniq.freeze
  @column_names = columns.map(&:name).freeze

  @identity_cache_key = "ar:cache:#{@name}"
  @short_sha1 = Digest::SHA1.hexdigest("#{@disabled}:#{columns.to_json}").first(7)

  # For avoid to skip Arcache read cache, must delete cache when disable Arcache.
  # For keep table's schema is consistent, must delete cache after modified the table.
  ArCache.delete(@identity_cache_key) if disabled? || !cache_key_prefix.start_with?("#{@identity_cache_key}:#{@short_sha1}") # rubocop:disable Layout/LineLength
end

Public Instance Methods

cache_key(where_values_hash, index, multi_values_key = nil, key_value = nil) click to toggle source
# File lib/ar_cache/table.rb, line 75
def cache_key(where_values_hash, index, multi_values_key = nil, key_value = nil)
  where_value = index.map do |column|
    value = column == multi_values_key ? key_value : where_values_hash[column]
    "#{column}=#{value}"
  end.sort.join('&')

  "#{cache_key_prefix}:#{where_value}"
end
cache_key_prefix() click to toggle source
# File lib/ar_cache/table.rb, line 56
def cache_key_prefix
  return '' if disabled?

  ArCache.read(identity_cache_key, raw: true) || update_cache
end
disabled?() click to toggle source
# File lib/ar_cache/table.rb, line 48
def disabled?
  @disabled
end
primary_cache_key(id) click to toggle source
# File lib/ar_cache/table.rb, line 71
def primary_cache_key(id)
  "#{cache_key_prefix}:#{primary_key}=#{id}"
end
select_disabled?() click to toggle source
# File lib/ar_cache/table.rb, line 52
def select_disabled?
  @select_disabled
end
update_cache() click to toggle source

In order to avoid cache avalanche, we must set cache_key_prefix never expired.

# File lib/ar_cache/table.rb, line 63
def update_cache
  return '' if disabled?

  key = "#{identity_cache_key}:#{short_sha1}:#{Time.now.to_f}"
  ArCache.write(identity_cache_key, key, raw: true, expires_in: 20.years)
  key
end

Private Instance Methods

normalize_unique_indexes(indexes, columns) click to toggle source
# File lib/ar_cache/table.rb, line 84
        def normalize_unique_indexes(indexes, columns)
  indexes = indexes.empty? ? query_unique_indexes(columns) : validate_unique_indexes(indexes, columns)
  (indexes - [primary_key]).sort_by(&:size).unshift([primary_key])
end
query_unique_indexes(columns) click to toggle source
# File lib/ar_cache/table.rb, line 89
        def query_unique_indexes(columns)
  ::ActiveRecord::Base.connection.indexes(name).filter_map do |index|
    next unless index.unique
    next unless index.columns.is_a?(Array)

    index.columns.each do |column|
      next if columns.none? { |c| c.name == column }
    end

    index.columns
  end
end
validate_unique_indexes(indexes, columns) click to toggle source
# File lib/ar_cache/table.rb, line 102
        def validate_unique_indexes(indexes, columns)
  indexes.each do |attrs|
    attrs.each do |attr|
      column = columns.find { |c| c.name == attr }
      raise ArgumentError, "The #{name} table not found #{attr} column" if column.nil?
    end
  end
end