2

We have a server that gets cranky if it gets too many users logging in at the same time (meaning less than 7 seconds apart). Once the users are logged in, there is no problem (one or two logging in at the same time is also not a problem, but when 10-20 try the entire server goes into a death spiral sigh).

I'm attempting to write a page that will hold onto users (displaying an animated countdown etc.) and let them through 7 seconds apart. The algorithm is simple

  1. fetch the timestamp (t) when the last login happened
  2. if t+7 is in the past start the login and store now() as the new timestamp
  3. if t+7 is in the future, store it as the new timestamp, wait until t+7, then start the login.

A straight forward python/redis implementation would be:

import time, redis
SLOT_LENGTH = 7  # seconds

now = time.time()

r = redis.StrictRedis()

# lines below contain race condition..
last_start = float(r.get('FLOWCONTROL') or '0.0')  # 0.0 == time-before-time
my_start = last_start + SLOT_LENGTH
r.set('FLOWCONTROL', max(my_start, now))  

wait_period = max(0, my_start - now)
time.sleep(wait_period)

# .. login

The race condition here is obvious, many processes can be at the my_start = line simultaneously. How can I solve this using redis?

I've tried the redis-py pipeline functionality, but of course that doesn't get an actual value until in the r.get() call...

4

1 回答 1

3

我会记录答案,以防其他人发现这个......

r = redis.StrictRedis()
with r.pipeline() as p:
    while 1:
        try:
            p.watch('FLOWCONTROL')  # --> immediate mode
            last_slot = float(p.get('FLOWCONTROL') or '0.0')
            p.multi()  # --> back to buffered mode
            my_slot = last_slot + SLOT_LENGTH
            p.set('FLOWCONTROL', max(my_slot, now))
            p.execute()  # raises WatchError if anyone changed TCTR-FLOWCONTROL
            break  # break out of while loop
        except WatchError:
            pass  # someone else got there before us, retry.

比原来的三行稍微复杂一点...

于 2013-11-03T14:06:40.313 回答