Commit 41ec2d75 authored by Barry Warsaw's avatar Barry Warsaw

Extend iterator() to include hint about "backpointers", or in Berkeley

storage speak, lrevid pointers to shared pickle data in earlier
transactions.

Specifically,

The _Record object now has a data_txn attribute that is either None or
the id of the transaction that contains the data used by the current
record.  Example: When transactionalUndo() modifies an object, it
typical creates a new data record that points at the transaction
before the undo.  The new record contains the same logical data as the
record it refers to.  (For consistency purposes, this is a stronger
claim than that the pickles in two different data records are the
same.)

_Record.__init__(): Gets data_txn passed in from
_RecordIterator.__getitem__(), which in turn getes the lrevid from
Full._loadSerialEx().

_loadSerialEx(): Now returns the backpointer, aka lrevid but only if
it is not the same as the serial argument.  They will always be the
same (and we'll always return None) except in the face of
transactionalUndo, commitVersion, and abortVersion.
parent 8e358595
...@@ -18,7 +18,7 @@ See Minimal.py for an implementation of Berkeley storage that does not support ...@@ -18,7 +18,7 @@ See Minimal.py for an implementation of Berkeley storage that does not support
undo or versioning. undo or versioning.
""" """
__version__ = '$Revision: 1.44 $'.split()[-2:][0] __version__ = '$Revision: 1.45 $'.split()[-2:][0]
import sys import sys
import struct import struct
...@@ -641,8 +641,11 @@ class Full(BerkeleyBase, ConflictResolvingStorage): ...@@ -641,8 +641,11 @@ class Full(BerkeleyBase, ConflictResolvingStorage):
return data[:8], data[8:] return data[:8], data[8:]
def _loadSerialEx(self, oid, serial): def _loadSerialEx(self, oid, serial):
# Just like loadSerial, except that it returns both the pickle and the # Just like loadSerial, except that it returns the pickle data, the
# version this object revision is living in. # version this object revision is living in, and a backpointer. The
# backpointer is None if the lrevid for this metadata record is the
# same as the tid. If not, we have a pointer to previously existing
# data, so we return that.
self._lock_acquire() self._lock_acquire()
try: try:
# Get the pointer to the pickle (i.e. live revid, or lrevid) # Get the pointer to the pickle (i.e. live revid, or lrevid)
...@@ -657,8 +660,14 @@ class Full(BerkeleyBase, ConflictResolvingStorage): ...@@ -657,8 +660,14 @@ class Full(BerkeleyBase, ConflictResolvingStorage):
# Check for an zombification event, possible with # Check for an zombification event, possible with
# transactionalUndo. Use data==None to specify that. # transactionalUndo. Use data==None to specify that.
if lrevid == DNE: if lrevid == DNE:
return None, version return None, version, None
return self._pickles[oid+lrevid], version backpointer = None
if lrevid <> serial:
# This transaction shares its pickle data with a previous
# transaction. We need to let the caller know, esp. when it's
# the iterator code, so that it can pass this information on.
backpointer = lrevid
return self._pickles[oid+lrevid], version, backpointer
finally: finally:
self._lock_release() self._lock_release()
...@@ -1580,8 +1589,8 @@ class _RecordsIterator: ...@@ -1580,8 +1589,8 @@ class _RecordsIterator:
""" """
# Let IndexError percolate up # Let IndexError percolate up
oid = self._oids.pop() oid = self._oids.pop()
pickle, version = self._storage._loadSerialEx(oid, self.tid) data, version, lrevid = self._storage._loadSerialEx(oid, self.tid)
return _Record(oid, self.tid, version, pickle) return _Record(oid, self.tid, version, data, lrevid)
...@@ -1594,9 +1603,12 @@ class _Record: ...@@ -1594,9 +1603,12 @@ class _Record:
version = None version = None
# Data pickle # Data pickle
data = None data = None
# The pointer to the transaction containing the pickle data, if not None
data_txn = None
def __init__(self, oid, serial, version, data): def __init__(self, oid, serial, version, data, data_txn):
self.oid = oid self.oid = oid
self.serial = serial self.serial = serial
self.version = version self.version = version
self.data = data self.data = data
self.data_txn = data_txn
...@@ -18,7 +18,7 @@ See Minimal.py for an implementation of Berkeley storage that does not support ...@@ -18,7 +18,7 @@ See Minimal.py for an implementation of Berkeley storage that does not support
undo or versioning. undo or versioning.
""" """
__version__ = '$Revision: 1.44 $'.split()[-2:][0] __version__ = '$Revision: 1.45 $'.split()[-2:][0]
import sys import sys
import struct import struct
...@@ -641,8 +641,11 @@ class Full(BerkeleyBase, ConflictResolvingStorage): ...@@ -641,8 +641,11 @@ class Full(BerkeleyBase, ConflictResolvingStorage):
return data[:8], data[8:] return data[:8], data[8:]
def _loadSerialEx(self, oid, serial): def _loadSerialEx(self, oid, serial):
# Just like loadSerial, except that it returns both the pickle and the # Just like loadSerial, except that it returns the pickle data, the
# version this object revision is living in. # version this object revision is living in, and a backpointer. The
# backpointer is None if the lrevid for this metadata record is the
# same as the tid. If not, we have a pointer to previously existing
# data, so we return that.
self._lock_acquire() self._lock_acquire()
try: try:
# Get the pointer to the pickle (i.e. live revid, or lrevid) # Get the pointer to the pickle (i.e. live revid, or lrevid)
...@@ -657,8 +660,14 @@ class Full(BerkeleyBase, ConflictResolvingStorage): ...@@ -657,8 +660,14 @@ class Full(BerkeleyBase, ConflictResolvingStorage):
# Check for an zombification event, possible with # Check for an zombification event, possible with
# transactionalUndo. Use data==None to specify that. # transactionalUndo. Use data==None to specify that.
if lrevid == DNE: if lrevid == DNE:
return None, version return None, version, None
return self._pickles[oid+lrevid], version backpointer = None
if lrevid <> serial:
# This transaction shares its pickle data with a previous
# transaction. We need to let the caller know, esp. when it's
# the iterator code, so that it can pass this information on.
backpointer = lrevid
return self._pickles[oid+lrevid], version, backpointer
finally: finally:
self._lock_release() self._lock_release()
...@@ -1580,8 +1589,8 @@ class _RecordsIterator: ...@@ -1580,8 +1589,8 @@ class _RecordsIterator:
""" """
# Let IndexError percolate up # Let IndexError percolate up
oid = self._oids.pop() oid = self._oids.pop()
pickle, version = self._storage._loadSerialEx(oid, self.tid) data, version, lrevid = self._storage._loadSerialEx(oid, self.tid)
return _Record(oid, self.tid, version, pickle) return _Record(oid, self.tid, version, data, lrevid)
...@@ -1594,9 +1603,12 @@ class _Record: ...@@ -1594,9 +1603,12 @@ class _Record:
version = None version = None
# Data pickle # Data pickle
data = None data = None
# The pointer to the transaction containing the pickle data, if not None
data_txn = None
def __init__(self, oid, serial, version, data): def __init__(self, oid, serial, version, data, data_txn):
self.oid = oid self.oid = oid
self.serial = serial self.serial = serial
self.version = version self.version = version
self.data = data self.data = data
self.data_txn = data_txn
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment