Skip to content

Commit cb8f339

Browse files
committed
add more discussion to INCR docu case studies
The discussion about implementing a rate limiter pattern contained in the docu about INCR command may lead one to think that the rate limiter pattern is made somewhat more complicated due to INCR not having NX features similar to SET. This is because a very simple but flawed example is demonstrated, and the working solutions all either use timestamps within the buckets (which adds complexity, and raises questions about clock synchronization across servers), or use lua scripting, or use redis lists. Meanwhile an NX feature on INCR would seem to solve the problem pointed out in the flawed example. This idea re-appears in github issues where users request additional features to be added to INCR, and point to rate limiter as a motivation: redis/redis#4423 redis/redis#7631 In all the comments, more experienced users explain how you can already basically do this using a MULTI pipeline, and you can use NX in that pipeline as well if you want, which would accomplish what these feature requests seem to be asking for. However, this idea doesn't actually appear in the case studies in the documentation. Expanding the case studies to include this idea, and working through explicitly how it avoids race conditions and pitfalls described in the other approaches, may be helpful to users. (It was helpful to me.)
1 parent 3541d0e commit cb8f339

File tree

1 file changed

+24
-2
lines changed

1 file changed

+24
-2
lines changed

commands/incr.md

+24-2
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ value greater than 10, otherwise it will expire and start again from 0.
114114
If for some reason the client performs the `INCR` command but does not perform
115115
the `EXPIRE` the key will be leaked until we'll see the same IP address again.
116116

117-
This can be fixed easily turning the `INCR` with optional `EXPIRE` into a Lua
117+
One way to fix this is by turning the `INCR` with optional `EXPIRE` into a Lua
118118
script that is send using the `EVAL` command (only available since Redis version
119119
2.6).
120120

@@ -126,7 +126,29 @@ if current == 1 then
126126
end
127127
```
128128

129-
There is a different way to fix this issue without using scripting, by using
129+
Another way to fix this, which doesn't require Lua, is to use the `NX` option of `EXPIRE`.
130+
131+
```
132+
FUNCTION LIMIT_API_CALL(ip)
133+
MULTI
134+
INCR(keyname)
135+
EXPIRE(keyname,10,NX)
136+
EXEC
137+
current = RESPONSE_OF_INCR_WITHIN_MULTI
138+
IF current > 10 THEN
139+
ERROR "too many requests per second"
140+
ELSE
141+
PERFORM_API_CALL()
142+
END
143+
```
144+
145+
Here, EXPIRE with NX is a no-op if there is already an expiry value set. So it only
146+
has an effect when this key was created just now by the call to INCR, and it isn't necessary
147+
to have a test for `current == 1` to get the desired behavior. The use of MULTI
148+
ensures that EXPIRE will always be called whenever INCR is. So keys cannot be created
149+
this way without having an expiry set, and we don't have to worry about leaking keys.
150+
151+
There is another way to fix this issue without using scripting, by using
130152
Redis lists instead of counters.
131153
The implementation is more complex and uses more advanced features but has the
132154
advantage of remembering the IP addresses of the clients currently performing an

0 commit comments

Comments
 (0)