[Zope] Fixed - Re: Versions problem in 2.6.1

John Eikenberry jae@kavi.com
Thu, 20 Feb 2003 15:38:21 -0800


--HlL+5n6rz5pIUxbD
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline


This is in relation to the thread referred to in the subject:

http://mail.zope.org/pipermail/zope/2003-February/131336.html

We recently came across this same problem, and after a bit of digging I've
got it fixed (for us at least).

What I did was comment out the break at around line 576 in
ZODB/FileStorage.py. This break was added between versions 2.6.0 and
2.6.1. For grepping purposes, here's a copy of the break and its preceeding
comment:

                # Once we've found the data we are looking for,
                # we can stop chasing backpointers.
                break

Just comment out that break and all should be good to go.

I've attached a Hotfix style patch for the lazy. :)

-- 

John Eikenberry [jae@kavi.com]
______________________________________________________________
"A society that will trade a little liberty for a little order
 will deserve neither and lose both."
                                          --B. Franklin

--HlL+5n6rz5pIUxbD
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="VersionCommit_HotFix.py"

# Fixes a bug introduced in zope-2.6.1 in ZODB/FileStorage.py
# Problem is that new files added in a version don't get committed when the
# version is saved/committed. The version is emptied but the locks are still
# around. Bad mojo.
#
# I'm going to submit a bug report on this to zope.org. I'll add the bug number
# once its in their system.
# Bug #817: http://collector.zope.org/Zope/817

from ZODB import FileStorage
import struct
from ZODB.utils import p64, U64

# constants copied from FileStorage
z64='\0'*8
# the struct formats for the headers
TRANS_HDR = ">8s8scHHH"
DATA_HDR = ">8s8s8s8sH8s"
# constants to support various header sizes
TRANS_HDR_LEN = 23
DATA_HDR_LEN = 42
DATA_VERSION_HDR_LEN = 58

if 1: # keep indent level consistent with original code
    def _commitVersion(self, src, dest, transaction, abort=None):
        # call after checking arguments and acquiring lock
        srcpos = self._vindex_get(src, 0)
        spos = p64(srcpos)
        # middle holds bytes 16:34 of a data record:
        #    pos of transaction, len of version name, data length
        #    commit version never writes data, so data length is always 0
        middle = struct.pack(">8sH8s", p64(self._pos), len(dest), z64)

        if dest:
            sd = p64(self._vindex_get(dest, 0))
            heredelta = 66 + len(dest)
        else:
            sd = ''
            heredelta = 50

        here = self._pos + (self._tfile.tell() + self._thl)
        oids = []
        current_oids = {}
        t = None
        tstatus = ' '
        if abort is None:
            newserial = self._serial

        while srcpos:
            self._file.seek(srcpos)
            h = self._file.read(DATA_VERSION_HDR_LEN)
            # h -> oid, serial, prev(oid), tloc, vlen, plen, pnv, pv
            oid = h[:8]
            pnv = h[-16:-8]
            if abort:
                # If we are aborting, the serialno in the new data
                # record should be the same as the serialno in the last
                # non-version data record.
                # XXX This might be the only time that the serialno
                # of a data record does not match the transaction id.
                self._file.seek(U64(pnv))
                h_pnv = self._file.read(DATA_VERSION_HDR_LEN)
                newserial = h_pnv[8:16]
            
            if self._index.get(oid) == srcpos:
                # This is a current record!
                self._tindex[oid] = here
                oids.append(oid)
                self._tfile.write(oid + newserial + spos + middle)
                if dest:
                    self._tvindex[dest] = here
                    self._tfile.write(pnv + sd + dest)
                    sd = p64(here)

                self._tfile.write(abort and pnv or spos)
                # data backpointer to src data
                here += heredelta

                current_oids[oid] = 1
# XXX jae - changed this
#                # Once we've found the data we are looking for,
#                # we can stop chasing backpointers.
#                break

            else:
                # Hm.  This is a non-current record.  Is there a
                # current record for this oid?
                if not current_oids.has_key(oid):
                    # Nope. We're done *if* this transaction wasn't undone.
                    tloc = h[24:32]
                    if t != tloc:
                        # We haven't checked this transaction before,
                        # get it's status.
                        t = tloc
                        self._file.seek(U64(t) + 16)
                        tstatus = self._file.read(1)
                    if tstatus != 'u':
                        # Yee ha! We can quit
                        break

            spos = h[-8:]
            srcpos = U64(spos)
        return oids

# apply hotfix
from zLOG import LOG, INFO
LOG('Hotfix', INFO, 'Applying %s.' % 'Version commit Fix (2.6.1)')
FileStorage.FileStorage._commitVersion = _commitVersion


--HlL+5n6rz5pIUxbD--