A script runs to completion with nothing interleaved, which is exactly why it is useful and exactly why a slow one is an outage.
-- a rate limiter that is one round trip and one atom
local current = redis.call('INCR', KEYS[1])
if current == 1 then
redis.call('EXPIRE', KEYS[1], ARGV[1])
end
return current
-- and the rule: no loops over unbounded key sets,
-- no KEYS, no blocking commands. milliseconds, not more.
The atomicity removes the check-then-act race that every naive rate limiter has, and it is the only way to get it without a transaction and a watch. The danger is that a script iterating over a large collection blocks every other client for its whole duration, and lua-time-limit does not kill it — it only starts returning BUSY to other clients, which is arguably worse. Keeping scripts to a handful of commands is the discipline that makes this safe.