[ZODB-Dev] ZODB packing error
Tim Peters
tim at zope.com
Mon Apr 5 16:59:43 EDT 2004
[Tim Peters]
>> - Transaction T1 loads an object O with a reference to persistent
>> object P, and modifies some other attribute of O (it does not load
>> P itself).
>>
>> - Other transactions remove the reference from O to P, and all other
>> references to P, from the database.
>>
>> - A pack to current time is done. Now the only reference to P lives
>> in memory, inside T1's view of O.
>>
>> - T1 finally tries to commit.
>>
>> At this point, I see nothing to prevent O from having a "dangling
>> reference" to P -- T1 itself never loaded P into memory, and the
>> database no longer contains any trace of P, but O still references
>> it. That probably isn't good.
[Dieter Maurer]
> Should this not give a (Write)ConflictError?
>
> Both "T1" and the transaction removing the reference from O to
> P modify "O".
I expect the outcome depends on the details of the conflict resolution
implemented by O. If, e.g., O is a BTree, the removal of P (maybe that's
one of O's keys) probably wouldn't raise a ConflictError. But in that case,
P would also be removed from the final (post-resolution) committed state, so
no dangling reference either.
Let me try to construct an actual dangling reference ... OK, this seems
easiest with a couple ZEO clients, although the same could clearly be done
with threads (it's easier to show relative timing via switching "by hand"
among ZEO clients):
First I started a ZEO server, on localhost:8888.
Now start a ZEO client:
>>> import ZODB
>>> from ZEO import ClientStorage
>>> st = ClientStorage.ClientStorage(('localhost', 8888))
>>> db = ZODB.DB(st)
>>> conn = db.open()
>>> from BTrees.OOBTree import *
>>> O = conn.root()['tree'] = OOBTree()
>>> O[1] = P = OOBTree()
>>> get_transaction().commit()
Now a second ZEO client:
>>> import ZODB
>>> from ZEO import ClientStorage
>>> st = ClientStorage.ClientStorage(('localhost', 8888))
>>> db = ZODB.DB(st)
>>> conn = db.open()
>>> O = conn.root()['tree']
>>> P = O[1]
Back to the first client:
>>> del O[1] # so nothing current refers to P anymore
>>> get_transaction().commit()
>>> db.pack() # and everything that used to refer to P also vanishes
Back to the second client:
>>> conn.sync()
>>> O[2] = P # this client still has a ref to P in memory
>>> get_transaction().commit() # and adds it to the BTree
>>> st.close()
Back to the first client:
>>> st.close()
Now fsrefs.py complains about a dangling reference (oid 1 is O, oid 2 is
(was) P):
oid 0x1L <type 'BTrees._OOBTree.OOBTree'>.
last updated: 2004-04-05 20:44:05.960000, tid=0x3543A7C196DE8CCL
refers to invalid object:
oid 0x2L missing: 'None'
More information about the ZODB-Dev
mailing list