Lua: A Guide for Redis Users
redisgreen.net
redisgreen.net
>> I think that for what we need Lua beats everybody else hands down. The language is not one that I particularly like, compared to Ruby for instance, but who cares? We are programmers and can code a short script in any language we want, but the point is, Lua is a wonderful implementation. Easy to embed, without even a configure script, like Redis! And FAST.[1]
[1] http://oldblog.antirez.com/post/redis-and-scripting.html
I think it’s dangerous to say that nils terminate tables if you’re not going to explain table iteration in Lua.
Edit: Here's a picture example showing how easily nils can cause confusion if you're using a #table based loop. https://i.imgur.com/qW0XoSs.png
for i,v in pairs(table) will go through an entire table.
for i,v in ipairs(table) will stop at a nil. It's meant for consistent behavior in tables with numerical indices.
A sigil value can work too if you absolutely need it. ex:
undef = {}
function isundef(val)
return undef == val
end
The sigil value is truthy and makes your code more convoluted, though. ex: if not val or isundef(val) then -- ...
So, :/. I do love the language though. function exists(t, k)
return t[k] ~= nil or t.exists[k]
end
t = { 1, 2, nil, 4, exists={ [3]=true } }
i = math.random(4)
if exists(t, i) then
...
That's pretty close to Perl's semantics, where undef is inoperable under 'use strict' but arrays/hashes can have them explicit. Also can move exists out of t easily with weak table. local exists_t = setmetatable({ }, { __mode='k' })
function set_exists(t, k)
exists_t[t] = exists_t[t] or { }
exists_t[t][k] = true
end
function exists(t, k)
return t[k] ~= nil or exists_t[t][k]
end
That said, I really like math-like attitude of definitions in Lua. No bs like 'bloated for your convenience', you just use your logic skills to program. We use the term sequence to denote a table where the set of
all positive numeric keys is equal to {1..n} for some non
negative integer n, which is called the length of the
sequence (see §3.4.7).
If a table contains numeric keys with any gaps (i.e. nil values between sequential numeric indices), then it's not a sequence. Because Lua uses a binary search to find the end of the sequence, it might select any nil as the one ending the sequence.Also, another thing to remember is that a Lua table can have both a sequence and non-numerical (e.g. string) keys. So
local t = { 1, 2, 3, n = 3 }
is a valid sequence. Using n to store the length of the "array" part is a common Lua idiom when you want array behavior but cannot guarantee a valid sequence. For example, table.pack creates a table out of a variable argument list; but because any of the arguments might be nil, it sets n as the length of the argument list. Of course, you have to explicitly make use of n in those cases.Lots of people complain about this aspect of Lua. But Lua is very minimalist and there aren't any obvious alternatives. Lua could try to keep track of the maximum numeric index, but that can have poor asymptotic behavior unless Lua used a binary tree for tables instead of arrays and hashes. Lua could implement a type-safe array object, but that violates a core design guideline of Lua, which is that it provides the table as the only primitive for compound data structures. And in any event, it's trivial to implement your own array implementation using metamethods to maintain the invariants of a table while providing transparent support for # and ipairs.
> The best scripts simply extend the existing Redis vocabulary of small atomic data operations with the smallest bit of logic necessary.
Having used quite a bit of Lua (in Redis) I whole heartedly agree.
Lua is a relatively modern script language. It has all the tools you need to create modular programs in whatever style you like. Imperative is easy, OO style nearly so (there are plenty of OO implementations to choose from), and there is even some support for functional programming.
The PUC-Rio (main) implementation has a fast bytecode interpreter, and LuaJIT is wicked fast compared to just about anything else.
People don't tend to write really huge applications in Lua, but that is more due to the lack of libraries rather than anything else.
We used it to write almost all the game-logic on a few titles. Ran great on all sorts of platforms(this was before LuaJIT). Coroutines are awesome for AI, lets you bundle in all sorts of implicit state easily and concisely.
I don't see that as a valid concern anymore. The main concerns are 1) size of script you can comprehend, debug, and maintain and 2) do the most with the fewest network/redis calls. If you're 200 line lua script replaces 5-10 individual redis calls, that's surely a win simply from the perspective of network latency and data locality.
Shameless plug: the integration of Lua into Redis served as inspiration for our recent integration of Lua into Ceph/RADOS [1] for building custom object interfaces. I'll taking a cue from this article when we update our out-of-date documentation on the feature [2].
[1]: https://github.com/ceph/ceph/pull/7338
[2]: http://noahdesu.github.io/2014/01/22/dynamic-rados-object-in...
Modules are great when you want to use Redis as a server for something it does not support, but I would still use scripting for most things.
I wish redis offered a mechanism to save and call these scripts from the server, akin to the usual database functions and stored procedures.
I wonder if this was a conscious decision or something that's in the pipeline eventually.
http://redis.io/commands/script-load http://redis.io/commands/evalsha
EDIT:
I realize this was mentioned in the article, just wondering why it doesn't fit your needs.
While I agree that the caller should be ready for a script not existing, one could build an automated tool to load scripts when starting redis using the SCRIPT LOAD command.