Skip to content

Instantly share code, notes, and snippets.

@kyleschmolze
Forked from barmstrong/faster_fetch.rb
Last active September 30, 2015 21:13
Show Gist options
  • Save kyleschmolze/224c37c0202f71423e95 to your computer and use it in GitHub Desktop.
Save kyleschmolze/224c37c0202f71423e95 to your computer and use it in GitHub Desktop.
# Another attempt using the built in Rails.cache.fetch (better serialization/deserialization)
class ActiveSupport::Cache::DalliStore
def fast_fetch(name, options=nil)
options ||= {}
name = expanded_key name
dupe_name = name+'_dupe'
if block_given?
unless options[:force]
entry = instrument(:read, name, options) do |payload|
payload[:super_operation] = :fetch if payload
read_entry(name, options)
end
end
if !entry.nil?
instrument(:fetch_hit, name, options) { |payload| }
entry
else
Thread.new do
result = instrument(:generate, name, options) do |payload|
yield
end
write(name, result, options)
write(dupe_name, result)
end
puts 'using old value'
read(dupe_name)
end
else
read(name, options)
end
end
end
# This is an alternative using redis
require 'yaml'
class Redis
def fetch key, options=nil
expires_in = options[:expires_in] || 10.minutes
race_condition_ttl = options[:race_condition_ttl] || 10.seconds
key_expires = key + '_expires'
entry = get(key)
expires = Time.parse(get(key_expires)) rescue 1.second.ago
if expires < Time.now
if entry.nil?
p 'update in real time'
new_entry = yield
set key, YAML::load(new_entry)
set key_expires, expires_in.from_now
new_entry
else
Thread.new do
p 'update in background thread'
new_entry = yield
set key, YAML::load(new_entry)
set key_expires, expires_in.from_now
end
set key_expires, race_condition_ttl
YAML::dump(entry) if entry
end
else
YAML::dump(entry) if entry
end
end
end
irb(main):612:0> REDIS.fetch('test', :expires_in => 6){ sleep 1; Time.now }
"update in real time"
=> "2012-11-12 14:03:26 -0800"
irb(main):612:0> REDIS.fetch('test', :expires_in => 6){ sleep 1; Time.now }
=> "2012-11-12 14:03:26 -0800"
irb(main):612:0> REDIS.fetch('test', :expires_in => 6){ sleep 1; Time.now }
=> "2012-11-12 14:03:26 -0800"
irb(main):613:0> REDIS.fetch('test', :expires_in => 6){ sleep 1; Time.now }
"update in background thread"
=> "2012-11-12 14:03:26 -0800"
irb(main):612:0> REDIS.fetch('test', :expires_in => 6){ sleep 1; Time.now }
=> "2012-11-12 14:03:26 -0800"
irb(main):614:0> REDIS.fetch('test', :expires_in => 6){ sleep 1; Time.now }
=> "2012-11-12 14:03:33 -0800"
irb(main):615:0> REDIS.fetch('test', :expires_in => 6){ sleep 1; Time.now }
=> "2012-11-12 14:03:33 -0800"
irb(main):620:0>
# the first line where it says "update in real time" is the only one that takes 1 second to return
@kyleschmolze
Copy link
Author

This is a simple modification to https://gist.github.com/barmstrong/4062316 which adds serialization and race_condition_ttl support to the redis adapter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment