Open rbrenes opened 10 years ago
evalsha command missing too, for redis-cli v2.6.17
Is anyone working on this ? @rbrenes What is the workaround you are using ?
Bump. We're trying to use fakeredis in testing with the Kue package. fakeredis crashes and says the script command is not implemented and to tell you if we want it :+1:
Without adding a bunch (including Lua) into this gem, I started forking Redis when starting any tests and terminating on Kernel.at_exit
.
Ya, for now we've done something similar... just defeats the purpose of fakeredis, and the error said we should tell guilleiguaran if we want it.
One alternative would be to use camcorder to mock those missing Redis methods.
I've been working around the absence of the script
, eval
, and evalsha
commands by just implementing the specific functionality I need for testing. Remember, the primary reason for using LUA scripts with Redis is for the atomicity -- this is usually not needed in testing.
For example, we use the Redlock gem which currently makes use of script
and evalsha
(earlier versions used eval
only).
# spec/support/redlock_helper.rb
# Monkey patching script and evalsha into Fakeredis for Redlock usage in M3LocationSyncWorkerSpec
class Redis
module Connection
class Memory
UNLOCK_SCRIPT_SHA = 'abcd'
LOCK_SCRIPT_SHA = 'bcde'
EXTEND_LIFE_SCRIPT_SHA = 'cdef'
def script(*args)
case args[1]
when Redlock::Client::RedisInstance::UNLOCK_SCRIPT
return UNLOCK_SCRIPT_SHA
when Redlock::Client::RedisInstance::LOCK_SCRIPT
return LOCK_SCRIPT_SHA
when Redlock::Client::RedisInstance::EXTEND_LIFE_SCRIPT
return EXTEND_LIFE_SCRIPT_SHA
else
raise Redis::CommandError, "ERR unknown command 'script'"
end
end
def evalsha(*args)
case args[0]
when UNLOCK_SCRIPT_SHA
if get(args[2]) == args[3]
del(args[2])
else
return 0
end
when LOCK_SCRIPT_SHA
if !exists(args[2]) || get(args[2]) == args[3]
set(args[2], args[3], 'PX', args[4])
end
when EXTEND_LIFE_SCRIPT_SHA
if get(args[2]) == args[3]
expire(args[2], args[4])
return 0
else
return 1
end
else
raise Redis::CommandError, "ERR unknown command 'evalsha'"
end
end
end
end
end
The script
method above assumes you're calling :load
(which is all Redlock does) and then just returns a hardcoded SHA for each script.
The evalsha
method then implements the LUA found in the script in Ruby.
Both methods preserve the behavior of raising when an unknown script is eval'd or loaded.
This is making the assumption that Redlock will not calculate the SHA independently of Redis.
I had to update the stubs to support redlock 1.3
and fakeredis 0.9
# Based on https://github.com/guilleiguaran/fakeredis/issues/94
# Updated for redlock 1.3
# Monkey patching evalsha into Fakeredis for Redlock usage
class Redis
module Connection
class Memory
def evalsha(*args)
script_sha = args[0]
mystery_arg = args[1]
resource = args[2]
argv = args[3..]
case script_sha
when Redlock::Scripts::UNLOCK_SCRIPT_SHA
# Original Redlock unlock Lua script to be translated to Fakeredis
# if redis.call("get",KEYS[1]) == ARGV[1] then
# return redis.call("del",KEYS[1])
# else
# return 0
# end
if get(resource) == argv[0]
del(resource)
else
return 0
end
when Redlock::Scripts::LOCK_SCRIPT_SHA
# Original Redlock lock Lua script to be translated to Fakeredis
# if (redis.call("exists", KEYS[1]) == 0 and ARGV[3] == "yes") or redis.call("get", KEYS[1]) == ARGV[1] then
# return redis.call("set", KEYS[1], ARGV[1], "PX", ARGV[2])
# end
if !exists?(resource) || get(resource) == argv[0]
set(resource, argv[0], 'PX', argv[1])
end
else
raise Redis::CommandError, "ERR unknown command 'evalsha' for #{script_sha}"
end
end
end
end
end
Thanks @polmuz . This is exactly what I needed. This allowed me to test without mocking Redlock itself, which is great!
BTW, Redlock 1.3.2 needed Redlock::Scripts::PTTL_SCRIPT_SHA
as well, so I've added it below:
# Based on https://github.com/guilleiguaran/fakeredis/issues/94
# Updated for redlock 1.3
# Monkey patching evalsha into Fakeredis for Redlock usage
class Redis
module Connection
class Memory
def evalsha(*args)
script_sha = args[0]
mystery_arg = args[1]
resource = args[2]
argv = args[3..]
case script_sha
when Redlock::Scripts::UNLOCK_SCRIPT_SHA
# Original Redlock unlock Lua script to be translated to Fakeredis
# if redis.call("get",KEYS[1]) == ARGV[1] then
# return redis.call("del",KEYS[1])
# else
# return 0
# end
if get(resource) == argv[0]
del(resource)
else
0
end
when Redlock::Scripts::LOCK_SCRIPT_SHA
# Original Redlock lock Lua script to be translated to Fakeredis
# if (redis.call("exists", KEYS[1]) == 0 and ARGV[3] == "yes") or redis.call("get", KEYS[1]) == ARGV[1] then
# return redis.call("set", KEYS[1], ARGV[1], "PX", ARGV[2])
# end
if !exists?(resource) || get(resource) == argv[0]
set(resource, argv[0], 'PX', argv[1])
end
when Redlock::Scripts::PTTL_SCRIPT_SHA
# Original Redlock lock Lua script to be translated to Fakeredis
# return { redis.call("get", KEYS[1]), redis.call("pttl", KEYS[1]) }
value = get(resource)
ttl = pttl(resource)
[value, ttl]
else
raise Redis::CommandError, "ERR unknown command 'evalsha' for #{script_sha}"
end
end
end
end
end
ERR unknown command 'script' (Redis::CommandError) tryint to do a Redis.script( :load, @script_file_content )