У меня есть сервер и клиент.
Клиент отправляет запрос.С этим запросом связан определенный ключ, например, a-1
, a-2
, b-1
, b-4
.
Если два запроса на один и тот же ключ поступают одновременно, возникает конфликтошибка, поскольку изменяются одни и те же структуры данных.
Я могу настроить клиента так, чтобы он просто не отправлял два запроса одного и того же ключа одновременно.Однако я бы хотел, чтобы эта система работала с несколькими клиентами.Кажется глупым заставлять клиентов координировать то, что они отправляют на сервер.Вместо этого я бы хотел, чтобы сервер просто блокировал запрос определенного ключа, если этот ключ уже был изменен, до тех пор, пока не будут выполнены другие запросы с тем же ключом.
С этой цельюсоздал систему блокировки.В начале функции на сервере я делаю:
key = ...
print "Acquiring %s lock..." % (key,)
KEY_LOCKS[key].acquire()
print "%s lock acquired." % (key,)
def after_commit_hook(success):
KEY_LOCKS[key].release()
print "(after %s commit): Released %s lock" % (('failed', 'successful')[success], key)
transaction.get().addAfterCommitHook(after_commit_hook)
, где KEY_LOCKS
- это ключи для точного сопоставления с threading.Lock
s.Затем следует код, который изменяет постоянные структуры данных.
Я предполагаю, что в случае запроса на ключ, который уже обрабатывается, он заблокируется при получении блокировки.Только тогда, когда предыдущий запрос уже был принят (таким образом, исключая любые конфликтные ошибки), новый запрос возобновится.Запросы не делают ничего, что могло бы конфликтовать до получения блокировки.
Большинство запросов работают нормально:
Acquiring a-b lock...
a-b lock acquired.
(after successful commit): Released a-b lock
Acquiring a-c lock...
a-c lock acquired.
(after successful commit): Released a-c lock
Однако, все еще проблема, когдатот же ключ отправляется, хотя блокировка, кажется, работает:
Acquiring q-q lock...
q-q lock acquired.
Acquiring q-q lock...
(after successful commit): Released q-q lock
q-q lock acquired.
(after failed commit): Released q-q lock
repoze.retry retrying, count = 1
Traceback (most recent call last):
...
ConflictError: database conflict error (oid 0x13009b, class persistent.list.PersistentList)
И затем запрос повторяется.Обратите внимание, что q-q lock
был получен только после успешной фиксации.
Что дает?Почему эта система не предотвращает конфликтные ошибки?Где мое предположение неверно?
РЕДАКТИРОВАТЬ: Ну, если, перед строкой transaction.get().addAfterCommitHook(after_commit_hook)
, я поставил transaction.begin()
, это работает.Для жизни я не могу понять, почему.Перед строкой transaction.begin()
весь мой код выглядит так:
post = request.params
if not post: return Response("No data!")
data = eval(post['data'])
time_parsed = time.time()
my_app = request.context
Это решает мою проблему, но я не ставлю это как ответ, потому что я все еще хочу знать: почему это вызывает конфликтошибки, если я не запускаю новую транзакцию прямо перед этим?