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