サーバーとクライアントがあります。
クライアントがリクエストを送信します。a-1
リクエストには、 、a-2
、b-1
などの特定のキーが関連付けられていb-4
ます。
同じキーに対する 2 つのリクエストが同時に来ると、同じデータ構造が変更されているため、競合エラーが発生します。
同じキーの 2 つの要求を一度に送信しないようにクライアントを適応させることができます。ただし、このシステムが複数のクライアントでも動作することを望みます。クライアントがサーバーに送信するものを調整するのはばかげているようです。代わりに、特定のキーが既に変更されている場合、同じキーを持つ他のリクエストが完了するまで、サーバーが特定のキーのリクエストを単純にブロックするようにしたいと思います。
この目的のために、ロック システムを作成しました。サーバー上の関数の開始時に、次のことを行います。
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 にマッピングする dict です。その後、永続データ構造を変更するコードに従います。
私が想定しているのは、すでに処理されているキーのリクエストが来た場合、ロックを取得するときにブロックされるということです。以前のリクエストがすでにコミットされている(競合エラーが発生していない) 場合にのみ、新しいリクエストが再開されます。ロックが取得されるまで、要求は競合することはありません。
ほとんどのリクエストは正常に機能します。
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
これは私の問題を解決しますが、私はまだ知りたいので、それを答えとして入れていません:直前に新しいトランザクションを開始しないと、競合エラーが発生するのはなぜですか?