[ZODB-Dev] Clearing sticky RCEs after a transaction abort

Chris McDonough chrism at plope.com
Wed Sep 15 22:05:47 EDT 2004


Errr.. hi again Tim.

Michael Dunstan and I worked out that a transaction terminated via
"Transaction.abort()" when a previous "Transaction.commit()" has not
been called on the same transaction does not appear to properly clear
its associated jars' "sticky read conflict" states.  The main symptom of
this bug are bogus read conflict errors during a *subsequent*
transaction.commit().

It appears that the only place that the _conflicts set associated with a
connection is cleared wholesale are during a Connection's tpc_abort or
tpc_finish methods, but no tpc_ methods are called when a transaction is
aborted, so the _conflicts dict is never cleared for aborted
transactions.  Since the connection is reused, this is meaningful.  I'm
not sure of the best place to put a fix.  The obvious place is to put a
self._conflicts.clear() in Connection's "abort" method.  This indeed
does seem to fix the problem, but I'm not sure if it's optimal.

Here's a unit test method that can be stitched into the
ZODB.tests.testZODB.ZODBTests class for 3.2 that I *think* demonstrates
the problem.

    def checkReadConflictErrorClearedDuringAbort(self):

        # When a transaction is aborted, the "memory" of which
        # objects were the cause of a ReadConflictError during
        # that transaction should be unconditionally cleared.

        root = self._db.open().root()
        root["data"] = data = PersistentMapping(
            {'d':PersistentMapping({1:1})}
            )
        get_transaction().commit()

        # cause a ReadConflictError
        cn2 = self._db.open()
        cn2.setLocalTransaction()
        r2 = cn2.root()
        data2 = r2["data"]

        data['d'][1] = 2
        get_transaction().commit()

        try:
            del data2['d'][1]
        except ReadConflictError:
            pass
        else:
            self.fail("No conflict occurred")

        # abort the transaction
        cn2.getTransaction().abort()
        get_transaction().abort()

        # the connections should retain no memory of the read
        # conflicts encountered during the transaction after a
        # Transaction.abort()

        data_conflicts = data._p_jar._conflicts
        data2_conflicts = data2._p_jar._conflicts

        self.failIf(data_conflicts)
        self.failIf(data2_conflicts)

Hopefully this test is correct.

- C




More information about the ZODB-Dev mailing list