Menu CommandsClientsDocumentationDocsGitHubLicense
Tile38

EVAL script numkeys [key ...] [arg ...]

Commands from the EVAL family evaluate Lua scripts using the Lua 5.1 interpreter running within the Tile38 server.

The 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 function keyword.

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[1], KEYS[2], …, and the additional arguments will be available as ARGV[1], ARGV[2], …

EVAL 'return {"Got keys", KEYS[1], KEYS[2], "Got args", ARGV[1]}' 2 fleet fleet2 drivers 1) "Got keys" 2) "fleet" 3) "fleet2" 4) "Got args" 5) "drivers"

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.call() and tile38.pcall() are the same tokens one would send as a tile38 command: EVAL 'return tile38.call("GET", "fleet", "truck1", "POINT")' 0 1) "33" 2) "-115"

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: EVAL 'return tile38.call("GET", KEYS[1], ARGV[1], "POINT")' 1 mykey myid1 1) "33" 2) "-115"

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.

Type Conversions

When a Lua script calls a tile38 command through tile38.call() or 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

Lua to RESP conversion table

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:

Helper functions

The Lua environment inside the Tile38 server also defines these helper functions:

Atomicity of scripts

Tile38 provides three different levels of atomicity:

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:

  1. 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.
  2. 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.
  3. 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.

However, you can run scripts even more efficiently (taking less bandwidth) by using -SHA version of the commands: EVALSHA, EVALROSHA, and EVALNASHA. For each script you want to run:

  1. Load that script by SCRIPT LOAD command, which returns the SHA1 digest of your script. SCRIPT LOAD "return tile38.pcall('set', KEYS[1], ARGV[1], 'point', ARGV[2], ARGV[2])" "d8bc159162250f39654a6466a92a66215814877b"
  2. 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. EVALSHA d8bc159162250f39654a6466a92a66215814877b 1 mykey myid2 33.1 -115.1 OK The script caching is not persistent, meaning it will be lost when the tile38 server is restarted.

Commands relevant to script caching are:

Replication

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.




Star