1package redisstore 2 3const luaTemplate = ` 4local C_EXPIRE = 'EXPIRE' 5local C_HGETALL = 'HGETALL' 6local C_HSET = 'HSET' 7local F_START = 's' 8local F_TICK = 't' 9local F_TOKENS = 'k' 10 11-- speed up access to next 12local next = next 13 14local key = KEYS[1] 15local now = tonumber(ARGV[1]) -- current unix time in nanoseconds 16local maxtokens = %d 17local interval = %d 18local rate = %f 19local ttl = %d 20 21-- hgetall gets all the fields as a lua table. 22local hgetall = function (key) 23 local data = redis.call(C_HGETALL, key) 24 local result = {} 25 for i = 1, #data, 2 do 26 result[data[i]] = data[i+1] 27 end 28 return result 29end 30 31-- availabletokens returns the number of available tokens given the last tick, 32-- current tick, max, and fill rate. 33local availabletokens = function (last, curr, max, fillrate) 34 local delta = curr - last 35 local available = delta * fillrate 36 if available > max then 37 available = max 38 end 39 return available 40end 41 42-- tick returns the total number of times the interval has occurred between 43-- start and current. 44local tick = function (start, curr, interval) 45 return math.floor((curr - start) / interval) 46end 47 48 49-- 50-- begin exec 51-- 52 53-- reset TTL, we saw the key 54redis.call(C_EXPIRE, key, ttl) 55 56local data = hgetall(key) 57local start, lasttick, tokens 58if next(data) == nil then 59 start = now 60 lasttick = 0 61 tokens = maxtokens-1 62 redis.call(C_HSET, key, F_START, start, F_TICK, lasttick, F_TOKENS, tokens) 63 64 local nexttime = start + interval 65 return {tokens, nexttime, true} 66else 67 start = tonumber(data[F_START]) 68 lasttick = tonumber(data[F_TICK]) 69 tokens = tonumber(data[F_TOKENS]) 70end 71 72local currtick = tick(start, now, interval) 73local nexttime = start + ((currtick+1) * interval) 74 75if lasttick < currtick then 76 tokens = availabletokens(lasttick, currtick, maxtokens, rate) 77 lasttick = currtick 78 redis.call(C_HSET, key, F_TICK, lasttick, F_TOKENS, tokens) 79end 80 81if tokens > 0 then 82 tokens = tokens-1 83 redis.call(C_HSET, key, F_TOKENS, tokens) 84 85 return {tokens, nexttime, true} 86end 87 88return {0, nexttime, false} 89` 90