Commit 6b003959 authored by Jeremy Hylton's avatar Jeremy Hylton

Simplify transactional undo implementation.

Use the file position stored in the transaction_id whenever possible.
It is possible when _check_txn_pos() returns true; i.e. when the file
position does point to the transaction record header.

Remove ostloc and here arguments to _txn_undo_write(), as they can be
computed after the call.

Omit redundant tests in _txn_undo_write().  All the paths that lead
here verify that the file position is valid.

Remove some attribute lookup optimizations.
parent 0e8427c9
...@@ -115,7 +115,7 @@ ...@@ -115,7 +115,7 @@
# may have a back pointer to a version record or to a non-version # may have a back pointer to a version record or to a non-version
# record. # record.
# #
__version__='$Revision: 1.88 $'[11:-2] __version__='$Revision: 1.89 $'[11:-2]
import struct, time, os, string, base64, sys import struct, time, os, string, base64, sys
from struct import pack, unpack from struct import pack, unpack
...@@ -1068,14 +1068,9 @@ class FileStorage(BaseStorage.BaseStorage, ...@@ -1068,14 +1068,9 @@ class FileStorage(BaseStorage.BaseStorage,
raise UndoError( raise UndoError(
'Undo is currently disabled for database maintenance.<p>') 'Undo is currently disabled for database maintenance.<p>')
pos = self._pos pos = self._pos
# BAW: Why 39 please? This makes no sense (see also below).
if pos < 39:
return []
file=self._file
seek=file.seek
read=file.read
r = [] r = []
i = 0 i = 0
# BAW: Why 39 please? This makes no sense (see also below).
while i < last and pos > 39: while i < last and pos > 39:
self._file.seek(pos - 8) self._file.seek(pos - 8)
pos = pos - U64(self._file.read(8)) - 8 pos = pos - U64(self._file.read(8)) - 8
...@@ -1097,7 +1092,7 @@ class FileStorage(BaseStorage.BaseStorage, ...@@ -1097,7 +1092,7 @@ class FileStorage(BaseStorage.BaseStorage,
e = loads(read(el)) e = loads(read(el))
except: except:
pass pass
next = read(8) next = self._file.read(8)
# next is either the redundant txn length - 8, or an oid # next is either the redundant txn length - 8, or an oid
if next == tl: if next == tl:
# There were no objects in this txn # There were no objects in this txn
...@@ -1136,64 +1131,72 @@ class FileStorage(BaseStorage.BaseStorage, ...@@ -1136,64 +1131,72 @@ class FileStorage(BaseStorage.BaseStorage,
self._lock_acquire() self._lock_acquire()
try: try:
return self._transactional_undo(transaction_id) return self._txn_undo(transaction_id)
finally: finally:
self._lock_release() self._lock_release()
def _transactional_undo(self, transaction_id): def _txn_undo(self, transaction_id):
# As seen in undoLog() below, transaction_id encodes the tid and # Find the right transaction to undo and call _txn_undo_write().
# possibly the oid of the first object in the transaction record.
# transaction_id will be of length 16 if there were objects
# affected by the txn, and 8 if there weren't (e.g. abortVersion()
# and commitVersion()). In the latter case, we could still find
# the transaction through an expensive search of the file, but
# we're punting on that for now.
transaction_id = base64.decodestring(transaction_id + '\n') transaction_id = base64.decodestring(transaction_id + '\n')
tid = transaction_id[:8] tid = transaction_id[:8]
pos = U64(transaction_id[8:16]) tpos = U64(transaction_id[8:16])
# XXX if not self._check_txn_pos(tpos, tid):
oid = transaction_id[16:] # If the pos and tid don't match, we must use the oid to
if oid == '' or not self._index.has_key(oid): # find the transaction record. Find the file position for
# We can't get the position of the transaction easily. # the current revision of this object, and search back for
# Note that there is a position encoded in the # the beginning of its transaction record
# transaction_id at [8:16], but it can't be used reliably oid = transaction_id[16:]
# across multiple file storages and thus breaks if oid == '' or not self._index.has_key(oid):
# transactional integrity. # XXX Is this the right error message?
raise UndoError, 'Undoing a non-object affecting transaction' raise UndoError('Undoing a non-object affecting transaction')
# Find the file position for the current revision of this object, pos = self._index[oid]
# and search back for the beginning of its transaction record while 1:
pos = self._index[oid] self._file.seek(pos)
ostloc = p64(self._pos) h = self._file.read(DATA_HDR_LEN)
here = self._pos + (self._tfile.tell() + self._thl) doid, serial, prev, tpos, vlen, plen = \
while 1: unpack('>8s8s8s8sH8s', h)
self._file.seek(pos) tpos = U64(tpos)
h = self._file.read(DATA_HDR_LEN) self._file.seek(tpos)
doid, serial, prev, tpos, vlen, plen = \ # Read transaction id to see if we've got a match
unpack('>8s8s8s8sH8s', h) thistid = self._file.read(8)
tpos = U64(tpos) if thistid == tid:
self._file.seek(tpos) break # Yeee ha!
# Read transaction id to see if we've got a match # Keep looking
thistid = self._file.read(8) pos = U64(prev)
if thistid == tid: if not pos:
break # Yeee ha! # We never found the right transaction
# Keep looking raise UndoError('Invalid undo transaction id')
pos = U64(prev) tindex = self._txn_undo_write(tpos, tid)
if not pos:
# We never found the right transaction
raise UndoError('Invalid undo transaction id')
# We're sitting at the transaction we want to undo, but let's move
# the file pointer back to the start of the txn record.
tindex = self._txn_undo_write(tpos, tid, ostloc, here)
self._tindex.update(tindex) self._tindex.update(tindex)
return tindex.keys() return tindex.keys()
def _txn_undo_write(self, tpos, tid, ostloc, here): def _check_txn_pos(self, pos, tid):
"Return true if pos is location of the transaction record for tid."
self._file.seek(pos)
this_tid = self._file.read(8)
if this_tid != tid:
return 0
# be extra cautious: Check the record length makes sense, to
# guard against a random file location that happens to have
# the right 8-byte pattern.
stlen = self._file.read(8)
tlen = U64(stlen)
self._file.seek(tlen, 1)
redundant_stlen = self._file.read(8)
if len(redundant_stlen) != 8:
return 0
if redundant_stlen != stlen:
return 0
return 1
def _txn_undo_write(self, tpos, tid):
# a helper function to write the data records for transactional undo # a helper function to write the data records for transactional undo
ostloc = p64(self._pos)
here = self._pos + (self._tfile.tell() + self._thl)
# Let's move the file pointer back to the start of the txn record.
self._file.seek(tpos) self._file.seek(tpos)
h = self._file.read(TRANS_HDR_LEN) h = self._file.read(TRANS_HDR_LEN)
# XXX jer: don't think the second test is needed at this point
if len(h) != TRANS_HDR_LEN or h[:8] != tid:
raise UndoError('Invalid undo transaction id')
if h[16] == 'u': if h[16] == 'u':
return return
if h[16] != ' ': if h[16] != ' ':
...@@ -1230,7 +1233,7 @@ class FileStorage(BaseStorage.BaseStorage, ...@@ -1230,7 +1233,7 @@ class FileStorage(BaseStorage.BaseStorage,
# Don't fail right away. We may be redeemed later! # Don't fail right away. We may be redeemed later!
failures[oid] = v failures[oid] = v
else: else:
plen =len(p) plen = len(p)
self._tfile.write(pack(">8s8s8s8sH8s", self._tfile.write(pack(">8s8s8s8sH8s",
oid, self._serial, p64(ipos), oid, self._serial, p64(ipos),
ostloc, len(v), p64(plen))) ostloc, len(v), p64(plen)))
......
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