<div>Hello ZODB List,</div><div><br></div><div>(This is also a stackoverflow question - you might prefer the formatting there: <a href="http://stackoverflow.com/questions/9810116/zodb-database-conflict-fail">http://stackoverflow.com/questions/9810116/zodb-database-conflict-fail</a> )</div>
<div><br></div><div>I have a server, and a client.</div><div><br></div><div>A client sends a request. The request has a certain key associated with it, e.g. `a-1`, `a-2`, `b-1`, `b-4`.</div><div><br></div><div>If two requests for the same key come in at once, there will be a conflict error, as the same data structures are being modified.</div>
<div><br></div><div>I can adapt the client to simply not send two requests of the same key at once. However, I&#39;d like this system to work with multiple clients, too. It seems silly to have the clients coordinate what they send to the server. Instead, I&#39;d like the server to simply block on a request of a certain key if that key is already being modified, until the other requests with that same key are done.</div>
<div><br></div><div>To this end, I&#39;ve created a locking system. At the beginning of the function on the server, I do:</div><div><br></div><div>    key = ...</div><div>    print &quot;Acquiring %s lock...&quot; % (key,)</div>
<div>    KEY_LOCKS[key].acquire()</div><div>    print &quot;%s lock acquired.&quot; % (key,)</div><div>    def after_commit_hook(success):</div><div>        KEY_LOCKS[key].release()</div><div>        print &quot;(after %s commit): Released %s lock&quot; % ((&#39;failed&#39;, &#39;successful&#39;)[success], key)</div>
<div>    transaction.get().addAfterCommitHook(after_commit_hook)</div><div><br></div><div>where `KEY_LOCKS` is a dict mapping keys to `threading.Lock`s. Afterwards follows the code that modifies the persistent data structures.</div>
<div><br></div><div>What I assume would happen is that, if a request comes in for a key that&#39;s already being processed, it would block when acquiring the lock. Only when the earlier request **has already been committed** (thus being beyond any conflict errors), would the new request resume. The requests do nothing that would conflict until the lock is acquired.</div>
<div><br></div><div>Most of the requests work fine:</div><div><br></div><div>    Acquiring a-b lock...</div><div>    a-b lock acquired.</div><div>    (after successful commit): Released a-b lock</div><div>    Acquiring a-c lock...</div>
<div>    a-c lock acquired.</div><div>    (after successful commit): Released a-c lock</div><div><br></div><div>However, there is _still_ an issue when the same key is sent, even though the locking seems to work:</div><div>
<br></div><div>    Acquiring q-q lock...</div><div>    q-q lock acquired.</div><div>    Acquiring q-q lock...</div><div>    (after successful commit): Released q-q lock</div><div>    q-q lock acquired.</div><div>    (after failed commit): Released q-q lock</div>
<div>    repoze.retry retrying, count = 1</div><div>    Traceback (most recent call last):</div><div>    ...</div><div>    ConflictError: database conflict error (oid 0x13009b, class persistent.list.PersistentList)</div><div>
<br></div><div>And then the request retries. Note that the `q-q lock` was only acquired after the successful commit. </div><div><br></div><div>What gives? Why is this system not preventing conflict errors? Where is my assumption incorrect?</div>
<div><br></div><div>---</div><div><br></div><div>If, before the `transaction.get().addAfterCommitHook(after_commit_hook)` line I put `transaction.begin()`, it works. For the life of me I can&#39;t figure out why. Before the `transaction.begin()` line, the entirety of my code is:</div>
<div><br></div><div>    post = request.params</div><div>    if not post: return Response(&quot;No data!&quot;)</div><div><br></div><div>    data = eval(post[&#39;data&#39;])</div><div>    time_parsed = time.time() </div><div>
    my_app = request.context</div><div><br></div><div>This solves my problem but I&#39;m not putting that as an answer &#39;cause I still want to know: Why does it give conflict errors if I don&#39;t start a fresh transaction right before?</div>
<div><br></div><div>Thanks all,</div><div>- Claudiu</div>