Class: Redstruct::Script

Inherits:
Object
  • Object
show all
Defined in:
lib/redstruct/script.rb

Overview

Utility class to interact with Lua scripts on Redis. It is recommended you flush your script cache on the redis server every once in a while to remove scripts that are not used anymore.

Constant Summary

ERROR_MESSAGE_PREFIX =

Redis returns an error starting with NOSCRIPT when we try to evaluate am unknown script using its sha1.

'NOSCRIPT'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(script:, connection:, sha1: nil) ⇒ Script

Returns a new instance of Script

Parameters:

  • script (String)

    the lua source code for the script

  • connection (Redstruct::ConnectionProxy)

    connection to use for script commands

  • sha1 (String)

    the sha1 representation of the script; optional

Raises:

  • (ArgumentError)


25
26
27
28
29
30
31
# File 'lib/redstruct/script.rb', line 25

def initialize(script:, connection:, sha1: nil)
  self.script = script
  @connection = connection
  @sha1 = sha1 unless sha1.nil?

  raise ArgumentError, 'requires a connection proxy' unless @connection.is_a?(Redstruct::ConnectionProxy)
end

Instance Attribute Details

#connectionRedstruct::ConnectionProxy (readonly)

Returns the connection used for script commands

Returns:



20
21
22
# File 'lib/redstruct/script.rb', line 20

def connection
  @connection
end

#scriptString

Returns the Lua script to evaluate

Returns:

  • (String)

    the Lua script to evaluate



17
18
19
# File 'lib/redstruct/script.rb', line 17

def script
  @script
end

Instance Method Details

#eval(keys: [], argv: []) ⇒ nil, ...

Evaluates the script using the given keys and argv arrays, and returns the unparsed result. Caller is in charge of interpreting the result. NOTE: To minimize the number of redis commands, this always first assumes that the script was already loaded using its sha1 representation, and tells redis to execute the script cached by `sha1`. If it receives as error that the script does not exist, only then will it send the source to be executed. So in the worst case you get 2 redis commands, but in the average case you get 1, and it's much faster as redis does not have to reparse the script, and we don't need to send the lua source every time.

Parameters:

  • keys (Array<String>)

    the KEYS array as described in the Redis doc for eval

  • argv (Array<String>)

    the ARGV array as described in the Redis doc for eval

Returns:

  • (nil, Boolean, String, Numeric)

    returns whatever redis returns



76
77
78
79
80
81
82
83
84
85
# File 'lib/redstruct/script.rb', line 76

def eval(keys: [], argv: [])
  keys = [keys] unless keys.is_a?(Array)
  argv = [argv] unless argv.is_a?(Array)
  argv = normalize(argv)

  @connection.evalsha(self.sha1, keys, argv)
rescue Redis::CommandError => err
  raise unless err.message.start_with?(ERROR_MESSAGE_PREFIX)
  @connection.eval(@script, keys, argv)
end

#exists?Boolean

Checks if the script was already loaded for the given redis db using #sha1

Returns:

  • (Boolean)

    true if the script was already loaded, false otherwise



55
56
57
# File 'lib/redstruct/script.rb', line 55

def exists?
  return @connection.script(:exists, self.sha1)
end

#loadString

Loads the given script to redis (i.e. sends the source, which gets compiled and saved) and saves the returned sha1

Returns:



61
62
63
64
# File 'lib/redstruct/script.rb', line 61

def load
  @sha1 = @connection.script(:load, @script)
  return @sha1
end

#sha1String

Returns the sha1 representation of the source code at `script` When running a lua script, redis will compile it once and cache the bytecode, using the sha1 of the source code as the cache key.

Returns:

  • (String)

    sha1 representation of `script`



47
48
49
50
51
# File 'lib/redstruct/script.rb', line 47

def sha1
  return @sha1 ||= begin
    Digest::SHA1.hexdigest(@script)
  end
end