EVAL script numkeys [key ...] [arg ...]
Commands from the EVAL family evaluate Lua scripts using the Lua 5.1 interpreter running within the Tile38 server.
script is a text of a Lua 5.1 script. The content of the script will be used to create a Lua function on the server. That function will take no arguments and should return one Lua value. Note: the script should not start with the
numkeys is the number of tile38 keys that will be passed into EVAL.
Following that, the next
numkeys arguments are the tile38 keys that can be accessed by the script. The remaining tokens are the additional arguments that can be accessed by the script but do not represent the tile38 keys.
The keys and additional arguments are available from the script as global variables KEYS and ARGV, both are one-based arrays (Lua tables). Specifically, they keys will be available in the script as KEYS, KEYS, …, and the additional arguments will be available as ARGV, ARGV, …
The Lua environment inside the Tile38 server defines two functions to call other tile38 commands from a Lua script:
The only difference between these two functions is how they handle the errors that might happen when the tile38 command is called. The
tile38.call() will raise a Lua error, while
tile38.pcall() will return a Lua table containing the error message.
The arguments of
tile38.pcall() are the same tokens one would send as a tile38 command:
Note that the above call includes the key name into the script and sends 0 keys. This should be avoided in favor of the following form:
This is useful in many ways. One reason is the potential future features involving clustering/sharding of the data. Another immediate reason is that it is beneficial to have constant scripts into which different keys/args are sent. This allows caching the compiled script on the server and avoid compiling new Lua code each time the script is evaluated (see EVALSHA and SCRIPT LOAD for more details.)
The value returned by a Lua script will be converted from its Lua type to one of the types supported by RESP.
When a Lua script calls a tile38 command through
tile38.pcall(), the result of the call (a RESP value) is converted to Lua type. Similarly, when the script returns a value to the EVAL command, it is converted from a Lua type to RESP. The type conversion rules are as follows:
RESP to Lua conversion table
- RESP integer -> Lua number
- RESP bulk string -> Lua string
- RESP array -> Lua table (array)
- RESP simple string -> Lua table (key-value map) with a single
okkey and the status message as the value
- RESP error -> Lua table (map) with a single
errkey and the error message as the value
- RESP Nil -> Lua boolean false
Lua to RESP conversion table
- Lua number -> RESP integer (if you need to return a float, return it as a string!)
- Lua string -> RESP bulk string
- Lua table (array) -> RESP array
- Lua table (key-value map) -> RESP array of (key, value) pairs, each pair being a RESP array of two items
- Lua table (key-value map) with a single
okkey -> RESP simple string
- Lua table (key-value map) with a single
errkey -> RESP error
- Lua boolean false -> RESP Nil
With these conversion rules, any RESP value that is converted to Lua and then back to RESP will be identical to the original.
There’s a couple of additional one-way Lua-RESP conversions:
- Lua boolean true -> RESP integer 1
- Lua nil -> RESP Nil
The Lua environment inside the Tile38 server also defines these helper functions:
tile38.error_reply(error_string)returns an error reply, as if obtained by the failed
tile38.status_reply(status_string)returns a status reply, as if obtained by the successful
tile38.pcall()that returns a status reply.
tile38.sha1hex(input_string)returns a hex representation of a SHA1 digest.
Atomicity of scripts
Tile38 provides three different levels of atomicity:
- Full, used by EVAL and EVALSHA: No other command is running on the server during the execution ofthe script;
- Read-only, used by EVALRO and EVALROSHA: No write-capable command is running on the server during the execution of the script. Other read-only commands or read-only scripts might run concurrently.
- None, used by EVALNA and EVALNASHA: It is possible that any command will run concurrently with the script. The usual Tile38 atomicity rules will be applied to each tile38 command, as the script calls it through
The reason for providing different atomicity levels is that in that the users might want to pick a particular behavior depending on their use case. Examples:
- You want to avoid race conditions: check some values and depending on the result maybe set or delete some values, or read some other values. In that case, you absolutely don’t want any data changes to happen between the parts of your script. Use Full atomicity: call your script through EVAL or EVALSHA.
- You want to avoid race conditions, but you know your script does not make any writes itself. In that case, you may use either Full or Read-only atomicity. If you choose Read-only your script might execute faster, as it will not have to wait for all existing readers to finish first. A Read-only script will also let other read-only commands or read-only scripts to run faster, as they will not have to wait for you to finish first. In other words, if your script is guaranteed to be read-only, you loose nothing and potentially gain by using Read-only level. Call your script using EVALRO or EVALROSHA.
- You are running long script such as a search with the cursor, then examining the returned values and picking the ones you like, and then getting more values from the cursor, and so on. The reason you use a script as opposed to multiple calls from the client is that you don’t want to waste time on the round trips. At the same time, you’re not concerned about race conditions. In that case, use the None atomicity. In other words, call your script through EVALNA or EVALNASHA.
Caching and -SHA commands
Tile38 has an internal caching mechanism that will avoid recompiling a script on every evaluation, so even if you send the same script multiple times it will only be compiled once.
- Load that script by SCRIPT LOAD command, which returns the SHA1 digest of your script.
SCRIPT LOAD"return tile38.pcall('set', KEYS, ARGV, 'point', ARGV, ARGV)" "d8bc159162250f39654a6466a92a66215814877b"
- Use a -SHA command exactly as you would have used a non-SHA command, the only difference being that you send a SHA1 digest string as your first argument.
The script caching is not persistent, meaning it will be lost when the tile38 server is restarted.
EVALSHAd8bc159162250f39654a6466a92a66215814877b 1mykey myid2 33.1 -115.1 OK
Commands relevant to script caching are:
The changes to the tile38 data made by scripts are replicated on the command level. In other words, when the script runs multiple commands, each command’s changes will be shipped to the replicas and replayed there.