Как предотвратить состояние гонки при использовании Redis для реализации управления потоком?

У нас есть сервер, который становится капризным, если слишком много пользователей входят в систему одновременно (то есть с интервалом менее 7 секунд). После того, как пользователи вошли в систему, проблем нет (один или два входа одновременно тоже не проблема, но когда 10-20 попыток, весь сервер уходит в смертельную спираль вздох) .

Я пытаюсь написать страницу, которая будет удерживать пользователей (отображать анимированный обратный отсчет и т. д.) и позволять им проходить через 7 секунд. Алгоритм прост

  1. получить отметку времени (t), когда произошел последний вход в систему
  2. если t+7 в прошлом, запустите логин и сохраните now() в качестве новой метки времени
  3. если t+7 будет в будущем, сохраните его как новую отметку времени, подождите до t+7, затем начните вход в систему.

Прямая реализация python/redis будет:

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

Состояние гонки здесь очевидно, на линии my_start = одновременно может находиться много процессов. Как я могу решить это с помощью Redis?

Я пробовал функцию redis-py pipeline, но, конечно, она не получает фактического значения до вызова r.get()...


person thebjorn    schedule 01.11.2013    source источник


Ответы (1)


Я задокументирую ответ, если кто-то еще найдет это...

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.

немного сложнее, чем исходные три строки...

person thebjorn    schedule 03.11.2013