这个redis lua脚本处理关键的到期竞争条件是一个纯函数吗?

时间:2018-03-07 18:21:15

标签: redis

我一直在玩redis来跟踪分布式系统中外部api的速率限制。我决定为每个存在限制的路线创建一个密钥。密钥的值是在限制重置之前我仍可以进行多少次请求。并且通过将键的TTL设置为限制将重置时进行重置。

为此,我写了以下lua脚本:

if redis.call("EXISTS", KEYS[1]) == 1 then
    local remaining = redis.call("DECR", KEYS[1])
    if remaining < 0 then
        local pttl = redis.call("PTTL", KEYS[1])
        if pttl > 0 then
            --[[
            -- We would exceed the limit if we were to do a call now, so let's send back that a limit exists (1)
            -- Also let's send back how much we would have exceeded the ratelimit if we were to ignore it (ramaning)
            -- and how long we need to wait in ms untill we can try again (pttl)
              ]]
            return {1, remaining, pttl}
        elseif pttl == -1 then
            -- The key expired the instant after we checked that it existed, so delete it and say there is no ratelimit
            redis.call("DEL", KEYS[1])
            return {0}
        elseif pttl == -2 then
            -- The key expired the instant after we decreased it by one. So let's just send back that there is no limit
           return  {0}
        end
    else
        -- Great we have a ratelimit, but we did not exceed it yet.
        return {1, remaining}
    end
else
   return {0}
end

Since a watched key can expire in the middle of a multi transaction without aborting it。我假设lua脚本的情况也是如此。因此,我将ttl为-1或-2时的情况放入。

在我编写该脚本后,我在eval命令页面上进行了更深入的研究,发现lua脚本必须是pure function

在那里说

  

脚本必须始终使用相同的Redis写命令来评估   给定相同输入数据集的相同参数。执行的操作   由脚本不能依赖任何隐藏(非显式)信息   或者在脚本执行过程中或之间可能发生变化的状态   脚本的不同执行,也不依赖于任何外部   来自I / O设备的输入。

通过这个描述,我不确定我的函数是否是纯函数。

2 个答案:

答案 0 :(得分:3)

在Itamar的回答之后,我想为自己确认一下,所以我写了一个小的lua脚本来测试它。脚本创建一个10ms TTL的密钥并检查ttl,直到它小于0:

redis.call("SET", KEYS[1], "someVal","PX", 10)
local tmp = redis.call("PTTL", KEYS[1])
while tmp >= 0
do
    tmp = redis.call("PTTL", KEYS[1])
    redis.log(redis.LOG_WARNING, "PTTL:" .. tmp)
end
return 0

当我运行此脚本时,它永远不会终止。它只是继续垃圾邮件我的日志,直到我杀死了redis服务器。但是,当脚本运行时,时间不会停滞不前,而是一旦TTL为0,它就会停止。

所以关键时代,它永远不会过期。

答案 1 :(得分:1)

  

由于观看的密钥可以在多事务中间到期而不会中止。我假设lua脚本的情况也是如此。因此,我将ttl为-1或-2时的情况放入。

与Lua脚本不同的AFAIR - 当脚本运行时,时间有点停止(至少在TTL方面)。

  

通过这个描述,我不确定我的函数是否是纯函数。

你的剧本很棒(实际上没有试图理解它的作用),不要担心:)