lib/lookup_by/cache.rb in lookup_by-0.9.1 vs lib/lookup_by/cache.rb in lookup_by-0.10.0
- old
+ new
@@ -7,24 +7,27 @@
@klass = klass
@primary_key = klass.primary_key
@primary_key_type = klass.columns_hash[@primary_key].type
@field = options[:field].to_sym
@cache = {}
+ @reverse = {}
@order = options[:order] || @field
@read = options[:find_or_create] || options[:find]
@write = options[:find_or_create]
@allow_blank = options[:allow_blank] || false
@normalize = options[:normalize]
@raise_on_miss = options[:raise] || false
@testing = false
@enabled = true
@safe = options[:safe] || concurrent?
+ @mutex = Mutex.new if @safe
@stats = { db: Hash.new(0), cache: Hash.new(0) }
raise ArgumentError, %Q(unknown attribute "#{@field}" for <#{klass}>) unless klass.column_names.include?(@field.to_s)
+ # Order matters here, some instance variables depend on prior assignments.
case options[:cache]
when true
@type = :all
@read ||= false
@@ -33,10 +36,11 @@
raise ArgumentError, "`#{@klass}.lookup_by :#{@field}` options[:find] must be true when caching N" if @read == false
@type = :lru
@limit = options[:cache]
@cache = @safe ? Caching::SafeLRU.new(@limit) : Caching::LRU.new(@limit)
+ @reverse = @safe ? Caching::SafeLRU.new(@limit) : Caching::LRU.new(@limit)
@read = true
@write ||= false
@testing = true if Rails.env.test? && @write
else
@read = true
@@ -51,35 +55,39 @@
return unless @type == :all
clear
::ActiveRecord::Base.connection.send :log, "", "#{@klass.name} Load Cache All" do
- @klass.order(@order).each do |i|
- @cache[i.id] = i
+ @klass.order(@order).each do |object|
+ cache_write(object)
end
end
end
def clear
@cache.clear
end
def create(*args, &block)
created = @klass.create(*args, &block)
- @cache[created.id] = created if created && cache?
+
+ cache_write(created) if cache?
+
created
end
def create!(*args, &block)
created = @klass.create!(*args, &block)
- @cache[created.id] = created if cache?
+
+ cache_write(created) if cache?
+
created
end
def seed(*values)
@klass.transaction(requires_new: true) do
- values.each { |value| create!(@field => value) }
+ values.map { |value| @klass.where(@field => value).first_or_create! }
end
end
def fetch(value)
increment :cache, :get
@@ -87,11 +95,11 @@
value = normalize(value) if @normalize && !primary_key?(value)
found = cache_read(value) if cache?
found ||= db_read(value) if @read || !@enabled
- @cache[found.id] = found if found && cache?
+ cache_write(found) if cache?
found ||= db_write(value) if @write
if @raise_on_miss && found.nil?
raise LookupBy::RecordNotFound, "No #{@klass.name} lookup record found for value: #{value.inspect}"
@@ -130,17 +138,13 @@
clear
end
private
+ # RAILS_ENV=test will not use the SafeLRU
def concurrent?
- case Rails::VERSION::MAJOR
- when 4 then Rails.configuration.cache_classes && Rails.configuration.eager_load
- when 3 then Rails.configuration.allow_concurrency
- else
- true
- end
+ Rails.configuration.cache_classes && Rails.configuration.eager_load
end
def primary_key?(value)
case @primary_key_type
when :integer
@@ -152,38 +156,73 @@
def normalize(value)
@klass.new(@field => value).send(@field)
end
- def cache_read(value)
- if primary_key?(value)
- found = @cache[value]
- else
- found = @cache.values.detect { |o| o.send(@field) == value }
+
+ if Rails.env.production?
+ def cache_read(value)
+ if primary_key?(value)
+ @cache[value]
+ else
+ @reverse[value]
+ end
end
+ else
+ def cache_read(value)
+ found = if primary_key?(value)
+ @cache[value]
+ else
+ @reverse[value]
+ end
- increment :cache, found ? :hit : :miss
+ increment :cache, found ? :hit : :miss
- found
+ found
+ end
end
- def db_read(value)
- increment :db, :get
+ if Rails.env.production?
+ def db_read(value)
+ @klass.where(column_for(value) => value).first
+ end
+ else
+ def db_read(value)
+ increment :db, :get
- found = @klass.where(column_for(value) => value).first
+ found = @klass.where(column_for(value) => value).first
- increment :db, found ? :hit : :miss
+ increment :db, found ? :hit : :miss
- found
+ found
+ end
end
+ if @safe
+ def cache_write(object)
+ return unless object
+
+ @mutex.synchronize do
+ @cache[object.id] = object
+ @reverse[object.send(@field)] = object
+ end
+ end
+ else
+ def cache_write(object)
+ return unless object
+
+ @cache[object.id] = object
+ @reverse[object.send(@field)] = object
+ end
+ end
+
def db_write(value)
column = column_for(value)
return if column == @primary_key
@klass.transaction(requires_new: true) do
- @klass.create!(column => value)
+ @klass.create(column => value)
end
rescue ActiveRecord::RecordNotUnique
db_read(value)
end