zodb: ошибка базы данных - PullRequest
       15

zodb: ошибка базы данных

1 голос
/ 21 марта 2012

У меня есть сервер и клиент.

Клиент отправляет запрос.С этим запросом связан определенный ключ, например, 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

Это решает мою проблему, но я не ставлю это как ответ, потому что я все еще хочу знать: почему это вызывает конфликтошибки, если я не запускаю новую транзакцию прямо перед этим?

1 Ответ

3 голосов
/ 22 марта 2012

ZODB обеспечивает соединения с единообразным представлением с момента начала транзакции.Это означает, что больше всего поток не будет видеть изменения в базе данных, сделанные другими потоками, пока не начнется новая транзакция!Это фундаментальная особенность баз данных, называемых Multiversion Concurrency Control .

Другими словами, если вы хотите, чтобы потоки вашего приложения не конфликтовали при использовании блокировки, вам также необходимо запустить новую транзакцию, когдаблокировка становится доступной.

По моему мнению, блокировка - это узкое место в производительности, которого вы хотите избежать.Чем дольше вы удерживаете блокировку, тем больше вероятность того, что вы натолкнетесь на противоречивые данные (ваше начальное состояние по сравнению с состоянием базы данных).

В Zope вместо этого используется оптимистический подход: когда возникает ошибка конфликта,транзакция отменяется и повторяется заново с самого начала.Если ваши транзакции короткие и быстрые, вы избегаете большинства проблем с блокировками, и вместо этого вы просто пересчитываете свои изменения в базе данных всякий раз, когда постоянные изменения меняются, вызывая конфликт.

...