Skip to content

script command for Redis-cli v2.6.17 #94

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
rbrenes opened this issue Feb 5, 2014 · 10 comments
Open

script command for Redis-cli v2.6.17 #94

rbrenes opened this issue Feb 5, 2014 · 10 comments

Comments

@rbrenes
Copy link

rbrenes commented Feb 5, 2014

ERR unknown command 'script' (Redis::CommandError) tryint to do a Redis.script( :load, @script_file_content )

@rbrenes
Copy link
Author

rbrenes commented Feb 5, 2014

evalsha command missing too, for redis-cli v2.6.17

@bsubramaniam
Copy link
Contributor

Is anyone working on this ?
@rbrenes What is the workaround you are using ?

@hurricane766
Copy link

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 👍

@carsonreinke
Copy link
Contributor

Without adding a bunch (including Lua) into this gem, I started forking Redis when starting any tests and terminating on Kernel.at_exit.

@hurricane766
Copy link

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.

@carsonreinke
Copy link
Contributor

One alternative would be to use camcorder to mock those missing Redis methods.

@hobodave
Copy link

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.

@carsonreinke
Copy link
Contributor

This is making the assumption that Redlock will not calculate the SHA independently of Redis.

@polmuz
Copy link

polmuz commented May 4, 2023

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

@timherby
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants