def checkUndoMultipleConflictResolution(self, reverse=False): from .ConflictResolution import PCounter db = DB(self._storage) cn = db.open() try: cn.root.x = PCounter() transaction.commit() for i in range(4): with db.transaction() as conn: conn.transaction_manager.get().note( (str if PY3 else unicode)(i)) conn.root.x.inc() ids = [l['id'] for l in db.undoLog(1, 3)] if reverse: ids.reverse() db.undoMultiple(ids) transaction.commit() self.assertEqual(cn.root.x._value, 2) finally: cn.close() db.close()
def checkUndoMultipleConflictResolution(self, reverse=False): from .ConflictResolution import PCounter db = DB(self._storage) cn = db.open() cn.root.x = PCounter() transaction.commit() for i in range(4): with db.transaction() as conn: conn.transaction_manager.get().note(str(i)) conn.root.x.inc() ids = [l['id'] for l in db.undoLog(1, 3)] if reverse: ids.reverse() db.undoMultiple(ids) transaction.commit() self.assertEqual(cn.root.x._value, 2) cn.close()
def _gen_testdb(outfs_path, zext): xtime_reset() ext = ext4subj if not zext: def ext(subj): return {} logging.basicConfig() # generate random changes to objects hooked to top-level root by a/b/c/... key random.seed(0) namev = [_ for _ in "abcdefg"] Niter = 2 for i in range(Niter): stor = FileStorage(outfs_path, create=(i == 0)) db = DB(stor) conn = db.open() root = conn.root() assert root._p_oid == p64(0), repr(root._p_oid) for j in range(25): name = random.choice(namev) if name in root: obj = root[name] else: root[name] = obj = Object(None) obj.value = "%s%i.%i" % (name, i, j) commit(u"user%i.%i" % (i,j), u"step %i.%i" % (i, j), ext(name)) # undo a transaction one step before a latest one a couple of times for j in range(2): # XXX undoLog, despite what its interface says: # https://github.com/zopefoundation/ZODB/blob/2490ae09/src/ZODB/interfaces.py#L472 # just returns log of all transactions in specified range: # https://github.com/zopefoundation/ZODB/blob/2490ae09/src/ZODB/FileStorage/FileStorage.py#L1008 # https://github.com/zopefoundation/ZODB/blob/2490ae09/src/ZODB/FileStorage/FileStorage.py#L2103 # so we retry undoing next log's txn on conflict. for ul in db.undoLog(1, 20): try: db.undo(ul["id"]) commit(u"root%i.%i\nYour\nMagesty " % (i, j), u"undo %i.%i\nmore detailed description\n\nzzz ..." % (i, j) + "\t"*(i+j), ext("undo %s" % ul["id"])) except UndoError: transaction.abort() continue break # delete an object name = random.choice(list(root.keys())) obj = root[name] root[name] = Object("%s%i*" % (name, i)) # NOTE user/ext are kept empty on purpose - to also test this case commit(u"", u"predelete %s" % unpack64(obj._p_oid), {}) # XXX obj in db could be changed by above undo, but ZODB does not automatically # propagate undo changes to live objects - so obj._p_serial can be stale. # Get serial via history. obj_tid_lastchange = db.history(obj._p_oid)[0]['tid'] txn = precommit(u"root%i\nYour\nRoyal\nMagesty' " % i + ''.join(chr(_) for _ in range(32)), # <- NOTE all control characters u"delete %i\nalpha beta gamma'delta\"lambda\n\nqqq ..." % i, ext("delete %s" % unpack64(obj._p_oid))) # at low level stor requires ZODB.IStorageTransactionMetaData not txn (ITransaction) txn_stormeta = TransactionMetaData(txn.user, txn.description, txn.extension) stor.tpc_begin(txn_stormeta) stor.deleteObject(obj._p_oid, obj_tid_lastchange, txn_stormeta) stor.tpc_vote(txn_stormeta) # TODO different txn status vvv # XXX vvv it does the thing, but py fs iterator treats this txn as EOF #if i != Niter-1: # stor.tpc_finish(txn_stormeta) stor.tpc_finish(txn_stormeta) # close db & rest not to get conflict errors after we touched stor # directly a bit. everything will be reopened on next iteration. conn.close() db.close() stor.close()