Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Atomic increment and expire #1

Closed
simonhaines opened this issue Mar 10, 2020 · 5 comments
Closed

Atomic increment and expire #1

simonhaines opened this issue Mar 10, 2020 · 5 comments

Comments

@simonhaines
Copy link

Raised by olegkv in this thread:

As far as I see, there is still race condition there, because call to Redis INCR command and a call to set expiration are different calls and counter may change in between.

Indeed the key creation and expiry is a little loose and in very rare cases can create keys that never expire or expire too early. The former case is a (very rare) problem for long-running caches, and I've had a crack at tightening it up a little on the branch atomic-incr-expireat (PRs welcome etc).

However the expectation for rate-limiting middleware is low latency, so this is preferred over formal correctness (within limits). I'll profile the branch and see how it goes. From the failure scenarios you describe, it sounds like your use-case requires consensus in a network of failing nodes, so maybe you're better served by something like an implementation of Raft?

@olegkv
Copy link

olegkv commented Mar 10, 2020

With Lua script such as:

local current
current = redis.call("incr",KEYS[1])
if tonumber(current) == 1 then
redis.call("expire",KEYS[1],1)
end

it will be bullet proof atomic, no race conditions at all.
StackExchange.Redis supports Lua scripts, so it seems rather easy to solve all race conditions once and for all.
Also, see this:
https://redis.io/commands/incr

@simonhaines
Copy link
Author

Looks to me like a great candidate for a pull request!

@olegkv
Copy link

olegkv commented Mar 12, 2020

Also, Lua is good because it moves time logic to Redis which means it will use central (Redis) time.
With your transaction logic here
https://github.com/statusas/AspNetCoreRateLimit/commits/atomic-incr-expireat
it is atomic but depends on local time. If you have two machines with different time and the difference is comparable to rate limit intervals, counters will not work correctly, each machine may work in different interval.

@simonhaines
Copy link
Author

@olegkv so... no pull request then?

@simonhaines
Copy link
Author

This commit adds an atomic 'set and expire key' operation to the rate limit counter store.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants