Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implement a lua script cache.

It's unnecessary (and slows things down) to load the same script into the same
redis server multiple times in the same process.
  • Loading branch information...
commit 87413331ccd93088567e019f32175d74adb8d734 1 parent a662978
@myronmarston myronmarston authored
View
8 lib/qless.rb
@@ -14,7 +14,7 @@ module Qless
USING_LEGACY_REDIS_VERSION = ::Redis::VERSION.to_f < 3.0
end
-require "qless/lua_script"
+require "qless/lua_script_cache"
require "qless/version"
require "qless/config"
require "qless/queue"
@@ -40,6 +40,10 @@ def worker_name
@worker_name ||= [Socket.gethostname, Process.pid.to_s].join('-')
end
+ def lua_script_cache
+ @lua_script_cache ||= LuaScriptCache.new
+ end
+
class ClientJobs
def initialize(client)
@client = client
@@ -158,7 +162,7 @@ def initialize(options = {})
@config = Config.new(self)
['cancel', 'config', 'complete', 'depends', 'fail', 'failed', 'get', 'heartbeat', 'jobs', 'peek', 'pop',
'priority', 'put', 'queues', 'recur', 'retry', 'stats', 'tag', 'track', 'workers', 'pause', 'unpause'].each do |f|
- self.instance_variable_set("@_#{f}", LuaScript.new(f, @redis))
+ self.instance_variable_set("@_#{f}", Qless.lua_script_cache.script_for(f, @redis))
end
@jobs = ClientJobs.new(self)
View
2  lib/qless/lua_script.rb
@@ -9,6 +9,8 @@ def initialize(name, redis)
reload()
end
+ attr_reader :name, :redis
+
def reload()
@sha = @redis.script(:load, File.read(File.join(LUA_SCRIPT_DIR, "#{@name}.lua")))
end
View
20 lib/qless/lua_script_cache.rb
@@ -0,0 +1,20 @@
+require 'qless/lua_script'
+
+module Qless
+ class LuaScriptCache
+ def initialize
+ @cache = {}
+ end
+
+ def script_for(script_name, redis_connection)
+ key = CacheKey.new(script_name, redis_connection.id)
+
+ @cache.fetch(key) do
+ @cache[key] = LuaScript.new(script_name, redis_connection)
+ end
+ end
+
+ CacheKey = Struct.new(:script_name, :redis_server_url)
+ end
+end
+
View
55 spec/unit/lua_script_cache_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+require 'qless'
+require 'qless/lua_script_cache'
+
+module Qless
+ describe LuaScriptCache do
+ let(:redis_1a) { fire_double("Redis", id: "redis://foo:1234/1", script: "sha") }
+ let(:redis_1b) { fire_double("Redis", id: "redis://foo:1234/1", script: "sha") }
+ let(:redis_2) { fire_double("Redis", id: "redis://foo:1234/2", script: "sha") }
+ let(:cache) { LuaScriptCache.new }
+
+ before { File.stub(read: "script content") }
+
+ it 'returns different lua script objects when the script name is different' do
+ script_1 = cache.script_for("foo", redis_1a)
+ script_2 = cache.script_for("bar", redis_1a)
+
+ expect(script_1).to be_a(LuaScript)
+ expect(script_2).to be_a(LuaScript)
+
+ expect(script_1).not_to be(script_2)
+
+ expect(script_1.name).to eq("foo")
+ expect(script_2.name).to eq("bar")
+ end
+
+ it 'returns different lua script objects when the redis connection is to a different server' do
+ script_1 = cache.script_for("foo", redis_1a)
+ script_2 = cache.script_for("foo", redis_2)
+
+ expect(script_1).to be_a(LuaScript)
+ expect(script_2).to be_a(LuaScript)
+
+ expect(script_1).not_to be(script_2)
+
+ expect(script_1.redis).to be(redis_1a)
+ expect(script_2.redis).to be(redis_2)
+ end
+
+ it 'returns the same lua script object when the script name and redis connection are the same' do
+ script_1 = cache.script_for("foo", redis_1a)
+ script_2 = cache.script_for("foo", redis_1a)
+
+ expect(script_1).to be(script_2)
+ end
+
+ it 'returns the same lua script object when the script name and redis conneciton URL are the same' do
+ script_1 = cache.script_for("foo", redis_1a)
+ script_2 = cache.script_for("foo", redis_1b)
+
+ expect(script_1).to be(script_2)
+ end
+ end
+end
+
View
20 spec/unit/qless_spec.rb
@@ -18,8 +18,15 @@
end
end
+ describe ".lua_script_cache" do
+ it 'returns a memoized script cache instance' do
+ expect(Qless.lua_script_cache).to be_a(Qless::LuaScriptCache)
+ expect(Qless.lua_script_cache).to be(Qless.lua_script_cache)
+ end
+ end
+
context 'when instantiated' do
- let(:redis) { fire_double("Redis", info: { "redis_version" => "2.6.0" }) }
+ let(:redis) { fire_double("Redis", id: "redis://foo:1/1", info: { "redis_version" => "2.6.0" }) }
let(:redis_class) { fire_replaced_class_double("Redis") }
before do
@@ -48,6 +55,17 @@
client = Qless::Client.new(redis: redis)
client.redis.should be(redis)
end
+
+ it 'loads the lua scripts from the cache so that the scripts are not unnecessarily loaded multiple times' do
+ cache = fire_double("Qless::LuaScriptCache")
+ Qless.stub(lua_script_cache: cache)
+
+ loaded_scripts = []
+ cache.stub(:script_for) { |name, redis| loaded_scripts << name }
+
+ client = Qless::Client.new(redis: redis)
+ expect(loaded_scripts).to include("put", "complete")
+ end
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.