Example #1
0
 def __dump(self, storage, sorted=sorted):
     return {
         u64(t.tid): sorted(
             (u64(o.oid), o.data_txn and u64(o.data_txn),
              None if o.data is None else makeChecksum(o.data)) for o in t)
         for t in storage.iterator()
     }
Example #2
0
 def _deleteRange(self, partition, min_tid=None, max_tid=None):
     sql = " WHERE `partition`=%d" % partition
     if min_tid:
         sql += " AND %d < tid" % util.u64(min_tid)
     if max_tid:
         sql += " AND tid <= %d" % util.u64(max_tid)
     q = self.query
     q("DELETE FROM trans" + sql)
     sql = " FROM obj" + sql
     data_id_list = [x for x, in q("SELECT DISTINCT data_id" + sql) if x]
     q("DELETE" + sql)
     self._pruneData(data_id_list)
Example #3
0
 def checkTIDRange(self, partition, length, min_tid, max_tid):
     # XXX: SQLite's GROUP_CONCAT is slow (looks like quadratic)
     count, tids, max_tid = self.query("""\
         SELECT COUNT(*), GROUP_CONCAT(tid), MAX(tid)
         FROM (SELECT tid FROM trans
               WHERE partition=? AND ?<=tid AND tid<=?
               ORDER BY tid ASC LIMIT ?) AS t""",
         (partition, util.u64(min_tid), util.u64(max_tid),
          -1 if length is None else length)).fetchone()
     if count:
         return count, sha1(tids).digest(), util.p64(max_tid)
     return 0, ZERO_HASH, ZERO_TID
Example #4
0
 def checkTIDRange(self, partition, length, min_tid, max_tid):
     # XXX: SQLite's GROUP_CONCAT is slow (looks like quadratic)
     count, tids, max_tid = self.query("""\
         SELECT COUNT(*), GROUP_CONCAT(tid), MAX(tid)
         FROM (SELECT tid FROM trans
               WHERE partition=? AND ?<=tid AND tid<=?
               ORDER BY tid ASC LIMIT ?) AS t""",
         (partition, util.u64(min_tid), util.u64(max_tid),
          -1 if length is None else length)).fetchone()
     if count:
         return count, sha1(tids).digest(), util.p64(max_tid)
     return 0, ZERO_HASH, ZERO_TID
Example #5
0
 def deleteObject(self, oid, serial=None):
     oid = util.u64(oid)
     sql = " FROM obj WHERE partition=? AND oid=?"
     args = [self._getPartition(oid), oid]
     if serial:
         sql += " AND tid=?"
         args.append(util.u64(serial))
     q = self.query
     data_id_list = [x for x, in q("SELECT DISTINCT data_id" + sql, args)
                       if x]
     q("DELETE" + sql, args)
     self._pruneData(data_id_list)
Example #6
0
 def deleteObject(self, oid, serial=None):
     oid = util.u64(oid)
     sql = " FROM obj WHERE partition=? AND oid=?"
     args = [self._getPartition(oid), oid]
     if serial:
         sql += " AND tid=?"
         args.append(util.u64(serial))
     q = self.query
     data_id_list = [x for x, in q("SELECT DISTINCT data_id" + sql, args)
                       if x]
     q("DELETE" + sql, args)
     self._pruneData(data_id_list)
Example #7
0
 def _deleteRange(self, partition, min_tid=None, max_tid=None):
     sql = " WHERE `partition`=%d" % partition
     if min_tid:
         sql += " AND %d < tid" % util.u64(min_tid)
     if max_tid:
         sql += " AND tid <= %d" % util.u64(max_tid)
     q = self.query
     q("DELETE FROM trans" + sql)
     sql = " FROM obj" + sql
     data_id_list = [x for x, in q("SELECT DISTINCT data_id" + sql) if x]
     q("DELETE" + sql)
     self._pruneData(data_id_list)
Example #8
0
 def getLastIds(self, params):
     """
       Get last ids.
     """
     assert not params
     ptid, backup_tid, truncate_tid = self.neoctl.getRecovery()
     if backup_tid:
         ltid = self.neoctl.getLastTransaction()
         r = "backup_tid = 0x%x" % u64(backup_tid)
     else:
         loid, ltid = self.neoctl.getLastIds()
         r = "last_oid = 0x%x" % u64(loid)
     return r + "\nlast_tid = 0x%x\nlast_ptid = %u" % (u64(ltid), ptid)
Example #9
0
 def getLastIds(self, params):
     """
       Get last ids.
     """
     assert not params
     ptid, backup_tid, truncate_tid = self.neoctl.getRecovery()
     if backup_tid:
         ltid = self.neoctl.getLastTransaction()
         r = "backup_tid = 0x%x" % u64(backup_tid)
     else:
         loid, ltid = self.neoctl.getLastIds()
         r = "last_oid = 0x%x" % u64(loid)
     return r + "\nlast_tid = 0x%x\nlast_ptid = %u" % (u64(ltid), ptid)
Example #10
0
 def _nextTID(orig, tm, ttid=None, divisor=None):
     n = self._next_tid
     self._next_tid = n + 1
     n = str(n).rjust(3, '-')
     if ttid:
         t = u64('T%s%s-' % (n, ttid[1:4]))
         m = (u64(ttid) - t) % divisor
         assert m < 211, (p64(t), divisor)
         t = p64(t + m)
     else:
         t = 'T%s----' % n
     assert tm._last_tid < t, (tm._last_tid, t)
     tm._last_tid = t
     return t
Example #11
0
 def getLastIds(self, params):
     """
       Get last ids.
     """
     assert not params
     ptid, backup_tid, truncate_tid = self.neoctl.getRecovery()
     if backup_tid:
         ltid = self.neoctl.getLastTransaction()
         r = "backup_tid = 0x%x (%s)" % (u64(backup_tid),
                                         timeStringFromTID(backup_tid))
     else:
         loid, ltid = self.neoctl.getLastIds()
         r = "last_oid = 0x%x" % (u64(loid))
     return r + "\nlast_tid = 0x%x (%s)\nlast_ptid = %s" % \
                                 (u64(ltid), timeStringFromTID(ltid), ptid)
Example #12
0
 def pack(self, tid, updateObjectDataForPack):
     # TODO: unit test (along with updatePackFuture)
     p64 = util.p64
     tid = util.u64(tid)
     updatePackFuture = self._updatePackFuture
     getPartition = self._getPartition
     q = self.query
     self._setPackTID(tid)
     for count, oid, max_serial in q("SELECT COUNT(*) - 1, oid, MAX(tid)"
                                     " FROM obj WHERE tid <= %d GROUP BY oid"
                                     % tid):
         partition = getPartition(oid)
         if q("SELECT 1 FROM obj WHERE `partition` = %d"
              " AND oid = %d AND tid = %d AND data_id IS NULL"
              % (partition, oid, max_serial)):
             max_serial += 1
         elif not count:
             continue
         # There are things to delete for this object
         data_id_set = set()
         sql = ' FROM obj WHERE `partition`=%d AND oid=%d' \
             ' AND tid<%d' % (partition, oid, max_serial)
         for serial, data_id in q('SELECT tid, data_id' + sql):
             data_id_set.add(data_id)
             new_serial = updatePackFuture(oid, serial, max_serial)
             if new_serial:
                 new_serial = p64(new_serial)
             updateObjectDataForPack(p64(oid), p64(serial),
                                     new_serial, data_id)
         q('DELETE' + sql)
         data_id_set.discard(None)
         self._pruneData(data_id_set)
     self.commit()
Example #13
0
 def _deleteRange(self, partition, min_tid=None, max_tid=None):
     sql = " WHERE partition=?"
     args = [partition]
     if min_tid:
         sql += " AND ? < tid"
         args.append(util.u64(min_tid))
     if max_tid:
         sql += " AND tid <= ?"
         args.append(util.u64(max_tid))
     q = self.query
     q("DELETE FROM trans" + sql, args)
     sql = " FROM obj" + sql
     data_id_list = [x for x, in q("SELECT DISTINCT data_id" + sql, args)
                       if x]
     q("DELETE" + sql, args)
     self._pruneData(data_id_list)
Example #14
0
 def getLastObjectTID(self, oid):
     oid = util.u64(oid)
     r = self.query("SELECT tid FROM obj"
                    " WHERE partition=? AND oid=?"
                    " ORDER BY tid DESC LIMIT 1",
                    (self._getReadablePartition(oid), oid)).fetchone()
     return r and util.p64(r[0])
Example #15
0
    def testTweakVsReplication(self, cluster, done=False):
        S = cluster.storage_list
        cluster.start(S[:1])
        t, c = cluster.getTransaction()
        ob = c.root()[''] = PCounterWithResolution()
        t.commit()
        self.assertEqual(1, u64(ob._p_oid))
        for s in S[1:]:
            s.start()
        self.tic()

        def tweak():
            self.tic()
            self.assertFalse(delay_list)
            self.assertPartitionTable(cluster, 'UU|UO')
            f.delayAskFetchObjects()
            cluster.enableStorageList(S[2:])
            cluster.neoctl.tweakPartitionTable()
            self.tic()
            self.assertPartitionTable(cluster, 'UU..|F.OO')
        with ConnectionFilter() as f, cluster.moduloTID(1), \
             Patch(S[1].replicator,
                   _nextPartitionSortKey=lambda orig, offset: offset):
            delay_list = [1, 0]
            delay = (f.delayNotifyReplicationDone if done else
                     f.delayAnswerFetchObjects)(lambda _: delay_list.pop())
            cluster.enableStorageList((S[1], ))
            cluster.neoctl.tweakPartitionTable()
            ob._p_changed = 1
            if done:
                tweak()
                t.commit()
            else:
                t2, c2 = cluster.getTransaction()
                c2.root()['']._p_changed = 1
                l = threading.Lock()
                l.acquire()
                TransactionalResource(t2, 0, tpc_vote=lambda _: l.release())
                t2 = self.newPausedThread(t2.commit)
                self.tic()

                @TransactionalResource(t, 0)
                def tpc_vote(_):
                    t2.start()
                    l.acquire()
                    f.remove(delay)
                    tweak()

                t.commit()
                t2.join()
            cluster.neoctl.dropNode(S[2].uuid)
            cluster.neoctl.dropNode(S[3].uuid)
            cluster.neoctl.tweakPartitionTable()
            if done:
                f.remove(delay)
            self.tic()
            self.assertPartitionTable(cluster, 'UU|UO')
        self.tic()
        self.assertPartitionTable(cluster, 'UU|UU')
        self.checkReplicas(cluster)
Example #16
0
 def pack(self, tid, updateObjectDataForPack):
     # TODO: unit test (along with updatePackFuture)
     p64 = util.p64
     tid = util.u64(tid)
     updatePackFuture = self._updatePackFuture
     getPartition = self._getReadablePartition
     q = self.query
     self._setPackTID(tid)
     for count, oid, max_serial in q(
             "SELECT COUNT(*) - 1, oid, MAX(tid)"
             " FROM obj WHERE tid<=? GROUP BY oid", (tid, )):
         partition = getPartition(oid)
         if q(
                 "SELECT 1 FROM obj WHERE partition=?"
                 " AND oid=? AND tid=? AND data_id IS NULL",
             (partition, oid, max_serial)).fetchone():
             max_serial += 1
         elif not count:
             continue
         # There are things to delete for this object
         data_id_set = set()
         sql = " FROM obj WHERE partition=? AND oid=? AND tid<?"
         args = partition, oid, max_serial
         for serial, data_id in q("SELECT tid, data_id" + sql, args):
             data_id_set.add(data_id)
             new_serial = updatePackFuture(oid, serial, max_serial)
             if new_serial:
                 new_serial = p64(new_serial)
             updateObjectDataForPack(p64(oid), p64(serial), new_serial,
                                     data_id)
         q("DELETE" + sql, args)
         data_id_set.discard(None)
         self._pruneData(data_id_set)
     self.commit()
Example #17
0
    def changePartitionTable(self, ptid, num_replicas, cell_list, reset=False):
        my_nid = self.getUUID()
        pt = dict(self.iterAssignedCells())
        # In backup mode, the last transactions of a readable cell may be
        # incomplete.
        backup_tid = self.getBackupTID()
        if backup_tid:
            backup_tid = util.u64(backup_tid)

        def outofdate_tid(offset):
            tid = pt.get(offset, 0)
            if tid >= 0:
                return tid
            return -tid in READABLE and (backup_tid or max(
                self._getLastIDs(offset)[0], self._getLastTID(offset))) or 0

        cell_list = [
            (offset, nid,
             (None
              if state == CellStates.DISCARDED else -state if nid != my_nid
              or state != CellStates.OUT_OF_DATE else outofdate_tid(offset)))
            for offset, nid, state in cell_list
        ]
        self._changePartitionTable(cell_list, reset)
        self._updateReadable(reset)
        assert isinstance(ptid, (int, long)), ptid
        self._setConfiguration('ptid', str(ptid))
        self._setConfiguration('replicas', str(num_replicas))
Example #18
0
 def iterCellNextTIDs(self):
     p64 = util.p64
     backup_tid = self.getBackupTID()
     if backup_tid:
         next_tid = util.u64(backup_tid)
         if next_tid:
             next_tid += 1
     for offset, tid in self.iterAssignedCells():
         if tid >= 0:  # OUT_OF_DATE
             yield offset, p64(tid and tid + 1)
         elif -tid in READABLE:
             if backup_tid:
                 # An UP_TO_DATE cell does not have holes so it's fine to
                 # resume from the last found records.
                 tid = self._getLastTID(offset)
                 yield offset, (
                     # For trans, a transaction can't be partially
                     # replicated, so replication can resume from the next
                     # possible tid.
                     p64(max(next_tid, tid + 1) if tid else next_tid),
                     # For obj, the last transaction may be partially
                     # replicated so it must be checked again (i.e. no +1).
                     p64(max(next_tid,
                             self._getLastIDs(offset)[0])))
             else:
                 yield offset, None
Example #19
0
 def _deleteRange(self, partition, min_tid=None, max_tid=None):
     sql = " WHERE partition=?"
     args = [partition]
     if min_tid:
         sql += " AND ? < tid"
         args.append(util.u64(min_tid))
     if max_tid:
         sql += " AND tid <= ?"
         args.append(util.u64(max_tid))
     q = self.query
     q("DELETE FROM trans" + sql, args)
     sql = " FROM obj" + sql
     data_id_list = [x for x, in q("SELECT DISTINCT data_id" + sql, args)
                       if x]
     q("DELETE" + sql, args)
     self._pruneData(data_id_list)
Example #20
0
 def getLastObjectTID(self, oid):
     oid = util.u64(oid)
     r = self.query("SELECT tid FROM obj"
                    " WHERE partition=? AND oid=?"
                    " ORDER BY tid DESC LIMIT 1",
                    (self._getPartition(oid), oid)).fetchone()
     return r and util.p64(r[0])
Example #21
0
 def checkTIDRange(self, partition, length, min_tid, max_tid):
     count, tid_checksum, max_tid = self.query(
         """SELECT COUNT(*), SHA1(GROUP_CONCAT(tid SEPARATOR ",")), MAX(tid)
            FROM (SELECT tid FROM trans
                  WHERE `partition` = %(partition)s
                    AND tid >= %(min_tid)d
                    AND tid <= %(max_tid)d
                  ORDER BY tid ASC %(limit)s) AS t""" % {
         'partition': partition,
         'min_tid': util.u64(min_tid),
         'max_tid': util.u64(max_tid),
         'limit': '' if length is None else 'LIMIT %u' % length,
     })[0]
     if count:
         return count, a2b_hex(tid_checksum), util.p64(max_tid)
     return 0, ZERO_HASH, ZERO_TID
Example #22
0
 def getLastObjectTID(self, oid):
     oid = util.u64(oid)
     r = self.query("SELECT tid FROM obj"
                    " WHERE `partition`=%d AND oid=%d"
                    " ORDER BY tid DESC LIMIT 1"
                    % (self._getPartition(oid), oid))
     return util.p64(r[0][0]) if r else None
Example #23
0
 def checkTIDRange(self, partition, length, min_tid, max_tid):
     count, tid_checksum, max_tid = self.query(
         """SELECT COUNT(*), SHA1(GROUP_CONCAT(tid SEPARATOR ",")), MAX(tid)
            FROM (SELECT tid FROM trans
                  WHERE `partition` = %(partition)s
                    AND tid >= %(min_tid)d
                    AND tid <= %(max_tid)d
                  ORDER BY tid ASC %(limit)s) AS t""" % {
             'partition': partition,
             'min_tid': util.u64(min_tid),
             'max_tid': util.u64(max_tid),
             'limit': '' if length is None else 'LIMIT %u' % length,
         })[0]
     if count:
         return count, a2b_hex(tid_checksum), util.p64(max_tid)
     return 0, ZERO_HASH, ZERO_TID
Example #24
0
 def getLastObjectTID(self, oid):
     oid = util.u64(oid)
     r = self.query("SELECT tid FROM obj FORCE INDEX(PRIMARY)"
                    " WHERE `partition`=%d AND oid=%d"
                    " ORDER BY tid DESC LIMIT 1" %
                    (self._getReadablePartition(oid), oid))
     return util.p64(r[0][0]) if r else None
Example #25
0
 def pack(self, tid, updateObjectDataForPack):
     # TODO: unit test (along with updatePackFuture)
     p64 = util.p64
     tid = util.u64(tid)
     updatePackFuture = self._updatePackFuture
     getPartition = self._getReadablePartition
     q = self.query
     self._setPackTID(tid)
     for count, oid, max_serial in q("SELECT COUNT(*) - 1, oid, MAX(tid)"
                                     " FROM obj FORCE INDEX(PRIMARY)"
                                     " WHERE tid <= %d GROUP BY oid" % tid):
         partition = getPartition(oid)
         if q("SELECT 1 FROM obj WHERE `partition` = %d"
              " AND oid = %d AND tid = %d AND data_id IS NULL" %
              (partition, oid, max_serial)):
             max_serial += 1
         elif not count:
             continue
         # There are things to delete for this object
         data_id_set = set()
         sql = ' FROM obj WHERE `partition`=%d AND oid=%d' \
             ' AND tid<%d' % (partition, oid, max_serial)
         for serial, data_id in q('SELECT tid, data_id' + sql):
             data_id_set.add(data_id)
             new_serial = updatePackFuture(oid, serial, max_serial)
             if new_serial:
                 new_serial = p64(new_serial)
             updateObjectDataForPack(p64(oid), p64(serial), new_serial,
                                     data_id)
         q('DELETE' + sql)
         data_id_set.discard(None)
         self._pruneData(data_id_set)
     self.commit()
Example #26
0
    def testTruncate(self):
        calls = [0, 0]

        def dieFirst(i):
            def f(orig, *args, **kw):
                calls[i] += 1
                if calls[i] == 1:
                    sys.exit()
                return orig(*args, **kw)

            return f

        cluster = NEOCluster(replicas=1)
        try:
            cluster.start()
            t, c = cluster.getTransaction()
            r = c.root()
            tids = []
            for x in xrange(4):
                r[x] = None
                t.commit()
                tids.append(r._p_serial)
            truncate_tid = tids[2]
            r["x"] = PCounter()
            s0, s1 = cluster.storage_list
            with Patch(s0.tm, unlock=dieFirst(0)), Patch(s1.dm, truncate=dieFirst(1)):
                t.commit()
                cluster.neoctl.truncate(truncate_tid)
                self.tic()
                getClusterState = cluster.neoctl.getClusterState
                # Unless forced, the cluster waits all nodes to be up,
                # so that all nodes are truncated.
                self.assertEqual(getClusterState(), ClusterStates.RECOVERING)
                self.assertEqual(calls, [1, 0])
                s0.resetNode()
                s0.start()
                # s0 died with unfinished data, and before processing the
                # Truncate packet from the master.
                self.assertFalse(s0.dm.getTruncateTID())
                self.assertEqual(s1.dm.getTruncateTID(), truncate_tid)
                self.tic()
                self.assertEqual(calls, [1, 1])
                self.assertEqual(getClusterState(), ClusterStates.RECOVERING)
            s1.resetNode()
            with Patch(s1.dm, truncate=dieFirst(1)):
                s1.start()
                self.assertEqual(s0.dm.getLastIDs()[0], truncate_tid)
                self.assertEqual(s1.dm.getLastIDs()[0], r._p_serial)
                self.tic()
                self.assertEqual(calls, [1, 2])
                self.assertEqual(getClusterState(), ClusterStates.RUNNING)
            t.begin()
            self.assertEqual(r, dict.fromkeys(xrange(3)))
            self.assertEqual(r._p_serial, truncate_tid)
            self.assertEqual(1, u64(c._storage.new_oid()))
            for s in cluster.storage_list:
                self.assertEqual(s.dm.getLastIDs()[0], truncate_tid)
        finally:
            cluster.stop()
Example #27
0
 def __init__(self, storage, oid=0, **kw):
     self.oid = int(oid)
     self.mountpoints = {k: int(v) for k, v in kw.iteritems()}
     self.connect(storage)
     self.ltid = util.u64(self.lastTransaction())
     if not self.ltid:
         raise DatabaseFailure("Can not import empty storage: %s" % storage)
     self.mapping = {}
Example #28
0
 def getFinalTID(self, ttid):
     ttid = util.u64(ttid)
     # As of SQLite 3.8.7.1, 'tid>=ttid' would ignore the index on tid,
     # even though ttid is a constant.
     for tid, in self.query("SELECT tid FROM trans"
             " WHERE partition=? AND tid>=? AND ttid=? LIMIT 1",
             (self._getPartition(ttid), ttid, ttid)):
         return util.p64(tid)
Example #29
0
 def abortTransaction(self, ttid):
     ttid = util.u64(ttid)
     q = self.query
     sql = " FROM tobj WHERE tid=%s" % ttid
     data_id_list = [x for x, in q("SELECT data_id" + sql) if x]
     q("DELETE" + sql)
     q("DELETE FROM ttrans WHERE ttid=%s" % ttid)
     self.releaseData(data_id_list, True)
Example #30
0
 def getFinalTID(self, ttid):
     ttid = util.u64(ttid)
     # MariaDB is smart enough to realize that 'ttid' is constant.
     r = self.query("SELECT tid FROM trans"
         " WHERE `partition`=%s AND tid>=ttid AND ttid=%s LIMIT 1"
         % (self._getPartition(ttid), ttid))
     if r:
         return util.p64(r[0][0])
Example #31
0
 def abortTransaction(self, ttid):
     args = util.u64(ttid),
     q = self.query
     sql = " FROM tobj WHERE tid=?"
     data_id_list = [x for x, in q("SELECT data_id" + sql, args) if x]
     q("DELETE" + sql, args)
     q("DELETE FROM ttrans WHERE ttid=?", args)
     self.releaseData(data_id_list, True)
Example #32
0
 def __init__(self, storage, oid=0, **kw):
     self.oid = int(oid)
     self.mountpoints = {k: int(v) for k, v in kw.iteritems()}
     self.connect(storage)
     self.ltid = util.u64(self.lastTransaction())
     if not self.ltid:
         raise DatabaseFailure("Can not import empty storage: %s" % storage)
     self.mapping = {}
Example #33
0
 def getFinalTID(self, ttid):
     ttid = util.u64(ttid)
     # As of SQLite 3.8.7.1, 'tid>=ttid' would ignore the index on tid,
     # even though ttid is a constant.
     for tid, in self.query("SELECT tid FROM trans"
             " WHERE partition=? AND tid>=? AND ttid=? LIMIT 1",
             (self._getReadablePartition(ttid), ttid, ttid)):
         return util.p64(tid)
Example #34
0
 def testTruncate(self):
     calls = [0, 0]
     def dieFirst(i):
         def f(orig, *args, **kw):
             calls[i] += 1
             if calls[i] == 1:
                 sys.exit()
             return orig(*args, **kw)
         return f
     cluster = NEOCluster(replicas=1)
     try:
         cluster.start()
         t, c = cluster.getTransaction()
         r = c.root()
         tids = []
         for x in xrange(4):
             r[x] = None
             t.commit()
             tids.append(r._p_serial)
         truncate_tid = tids[2]
         r['x'] = PCounter()
         s0, s1 = cluster.storage_list
         with Patch(s0.tm, unlock=dieFirst(0)), \
              Patch(s1.dm, truncate=dieFirst(1)):
             t.commit()
             cluster.neoctl.truncate(truncate_tid)
             self.tic()
             getClusterState = cluster.neoctl.getClusterState
             # Unless forced, the cluster waits all nodes to be up,
             # so that all nodes are truncated.
             self.assertEqual(getClusterState(), ClusterStates.RECOVERING)
             self.assertEqual(calls, [1, 0])
             s0.resetNode()
             s0.start()
             # s0 died with unfinished data, and before processing the
             # Truncate packet from the master.
             self.assertFalse(s0.dm.getTruncateTID())
             self.assertEqual(s1.dm.getTruncateTID(), truncate_tid)
             self.tic()
             self.assertEqual(calls, [1, 1])
             self.assertEqual(getClusterState(), ClusterStates.RECOVERING)
         s1.resetNode()
         with Patch(s1.dm, truncate=dieFirst(1)):
             s1.start()
             self.assertEqual(s0.dm.getLastIDs()[0], truncate_tid)
             self.assertEqual(s1.dm.getLastIDs()[0], r._p_serial)
             self.tic()
             self.assertEqual(calls, [1, 2])
             self.assertEqual(getClusterState(), ClusterStates.RUNNING)
         t.begin()
         self.assertEqual(r, dict.fromkeys(xrange(3)))
         self.assertEqual(r._p_serial, truncate_tid)
         self.assertEqual(1, u64(c._storage.new_oid()))
         for s in cluster.storage_list:
             self.assertEqual(s.dm.getLastIDs()[0], truncate_tid)
     finally:
         cluster.stop()
Example #35
0
 def getFinalTID(self, ttid):
     ttid = util.u64(ttid)
     # MariaDB is smart enough to realize that 'ttid' is constant.
     r = self.query(
         "SELECT tid FROM trans"
         " WHERE `partition`=%s AND tid>=ttid AND ttid=%s LIMIT 1" %
         (self._getReadablePartition(ttid), ttid))
     if r:
         return util.p64(r[0][0])
Example #36
0
    def test_findUndoTID(self):
        self.setNumPartitions(4, True)
        db = self.db
        tid1 = self.getNextTID()
        tid2 = self.getNextTID()
        tid3 = self.getNextTID()
        tid4 = self.getNextTID()
        tid5 = self.getNextTID()
        oid1 = p64(1)
        foo = db.holdData("3" * 20, oid1, 'foo', 0)
        bar = db.holdData("4" * 20, oid1, 'bar', 0)
        db.releaseData((foo, bar))
        db.storeTransaction(tid1, ((oid1, foo, None), ), None, temporary=False)

        # Undoing oid1 tid1, OK: tid1 is latest
        # Result: current tid is tid1, data_tid is None (undoing object
        # creation)
        self.assertEqual(db.findUndoTID(oid1, tid5, tid4, tid1, None),
                         (tid1, None, True))

        # Store a new transaction
        db.storeTransaction(tid2, ((oid1, bar, None), ), None, temporary=False)

        # Undoing oid1 tid2, OK: tid2 is latest
        # Result: current tid is tid2, data_tid is tid1
        self.assertEqual(db.findUndoTID(oid1, tid5, tid4, tid2, None),
                         (tid2, tid1, True))

        # Undoing oid1 tid1, Error: tid2 is latest
        # Result: current tid is tid2, data_tid is -1
        self.assertEqual(db.findUndoTID(oid1, tid5, tid4, tid1, None),
                         (tid2, None, False))

        # Undoing oid1 tid1 with tid2 being undone in same transaction,
        # OK: tid1 is latest
        # Result: current tid is tid1, data_tid is None (undoing object
        # creation)
        # Explanation of transaction_object: oid1, no data but a data serial
        # to tid1
        self.assertEqual(
            db.findUndoTID(oid1, tid5, tid4, tid1, (u64(oid1), None, tid1)),
            (tid1, None, True))

        # Store a new transaction
        db.storeTransaction(tid3, ((oid1, None, tid1), ),
                            None,
                            temporary=False)

        # Undoing oid1 tid1, OK: tid3 is latest with tid1 data
        # Result: current tid is tid2, data_tid is None (undoing object
        # creation)
        self.assertEqual(db.findUndoTID(oid1, tid5, tid4, tid1, None),
                         (tid3, None, True))
Example #37
0
 def __init__(self, app):
     EventQueue.__init__(self)
     self.read_queue = EventQueue()
     self._app = app
     self._transaction_dict = {}
     self._store_lock_dict = {}
     self._load_lock_dict = {}
     self._replicated = {}
     self._replicating = set()
     from neo.lib.util import u64
     np = app.pt.getPartitions()
     self.getPartition = lambda oid: u64(oid) % np
Example #38
0
    def testCommitVsDiscardedCell(self, cluster):
        s0, s1 = cluster.storage_list
        cluster.start((s0, ))
        t, c = cluster.getTransaction()
        ob = c.root()[''] = PCounterWithResolution()
        t.commit()
        self.assertEqual(1, u64(ob._p_oid))
        s1.start()
        self.tic()
        nonlocal_ = []
        with ConnectionFilter() as f:
            delay = f.delayNotifyReplicationDone()
            cluster.enableStorageList((s1, ))
            cluster.neoctl.tweakPartitionTable()
            self.tic()
            self.assertPartitionTable(cluster, 'U.|FO')
            t2, c2 = cluster.getTransaction()
            c2.root()[''].value += 3
            l = threading.Lock()
            l.acquire()

            @TransactionalResource(t2, 0)
            def tpc_vote(_):
                self.tic()
                l.release()

            t2 = self.newPausedThread(t2.commit)

            @TransactionalResource(
                t, 0, tpc_finish=lambda _: f.remove(nonlocal_.pop(0)))
            def tpc_vote(_):
                t2.start()
                l.acquire()
                nonlocal_.append(f.delayNotifyPartitionChanges())
                f.remove(delay)
                self.tic()
                self.assertPartitionTable(cluster, 'U.|.U', cluster.master)
                nonlocal_.append(cluster.master.pt.getID())

            ob.value += 2
            t.commit()
            t2.join()
        self.tic()
        self.assertPartitionTable(cluster, 'U.|.U')
        self.assertEqual(cluster.master.pt.getID(), nonlocal_.pop())
        t.begin()
        self.assertEqual(ob.value, 5)
        # get the second to last tid (for which ob=2)
        tid2 = s1.dm.getObject(ob._p_oid, None, ob._p_serial)[0]
        # s0 must not have committed anything for partition 1
        with s0.dm.replicated(1):
            self.assertFalse(s0.dm.getObject(ob._p_oid, tid2))
Example #39
0
 def getTransaction(self, tid, all=False):
     tid = util.u64(tid)
     q = self.query
     r = q("SELECT oids, user, description, ext, packed, ttid"
           " FROM trans WHERE partition=? AND tid=?",
           (self._getReadablePartition(tid), tid)).fetchone()
     if not r and all:
         r = q("SELECT oids, user, description, ext, packed, ttid"
               " FROM ttrans WHERE tid=?", (tid,)).fetchone()
     if r:
         oids, user, description, ext, packed, ttid = r
         return splitOIDField(tid, oids), str(user), \
             str(description), str(ext), packed, util.p64(ttid)
Example #40
0
 def _nextTID(self, ttid=None, divisor=None):
     """
     Compute the next TID based on the current time and check collisions.
     Also, if ttid is not None, divisor is mandatory adjust it so that
         tid % divisor == ttid % divisor
     while preserving
         min_tid < tid
     If ttid is None, divisor is ignored.
     When constraints allow, prefer decreasing generated TID, to avoid
     fast-forwarding to future dates.
     """
     tid = tidFromTime(time())
     min_tid = self._last_tid
     if tid <= min_tid:
         tid  = addTID(min_tid, 1)
         # We know we won't have room to adjust by decreasing.
         try_decrease = False
     else:
         try_decrease = True
     if ttid is not None:
         assert isinstance(ttid, basestring), repr(ttid)
         assert isinstance(divisor, (int, long)), repr(divisor)
         ref_remainder = u64(ttid) % divisor
         remainder = u64(tid) % divisor
         if ref_remainder != remainder:
             if try_decrease:
                 new_tid = addTID(tid, ref_remainder - divisor - remainder)
                 assert u64(new_tid) % divisor == ref_remainder, (dump(new_tid),
                     ref_remainder)
                 if new_tid <= min_tid:
                     new_tid = addTID(new_tid, divisor)
             else:
                 if ref_remainder > remainder:
                     ref_remainder += divisor
                 new_tid = addTID(tid, ref_remainder - remainder)
             assert min_tid < new_tid, (dump(min_tid), dump(tid), dump(new_tid))
             tid = new_tid
     self._last_tid = tid
     return self._last_tid
Example #41
0
 def getTransaction(self, tid, all=False):
     tid = util.u64(tid)
     q = self.query
     r = q("SELECT oids, user, description, ext, packed, ttid"
           " FROM trans WHERE `partition` = %d AND tid = %d" %
           (self._getReadablePartition(tid), tid))
     if not r and all:
         r = q("SELECT oids, user, description, ext, packed, ttid"
               " FROM ttrans WHERE tid = %d" % tid)
     if r:
         oids, user, desc, ext, packed, ttid = r[0]
         oid_list = splitOIDField(tid, oids)
         return oid_list, user, desc, ext, bool(packed), util.p64(ttid)
Example #42
0
 def getObjectHistory(self, oid, offset, length):
     # FIXME: This method doesn't take client's current transaction id as
     # parameter, which means it can return transactions in the future of
     # client's transaction.
     p64 = util.p64
     oid = util.u64(oid)
     return [(p64(tid), length or 0) for tid, length in self.query("""\
         SELECT tid, LENGTH(value)
             FROM obj LEFT JOIN data ON obj.data_id = data.id
             WHERE partition=? AND oid=? AND tid>=?
             ORDER BY tid DESC LIMIT ?,?""",
         (self._getPartition(oid), oid, self._getPackTID(), offset, length))
         ] or None
Example #43
0
 def getObjectHistory(self, oid, offset, length):
     # FIXME: This method doesn't take client's current transaction id as
     # parameter, which means it can return transactions in the future of
     # client's transaction.
     p64 = util.p64
     oid = util.u64(oid)
     return [(p64(tid), length or 0) for tid, length in self.query(
         """\
         SELECT tid, LENGTH(value)
             FROM obj LEFT JOIN data ON obj.data_id = data.id
             WHERE partition=? AND oid=? AND tid>=?
             ORDER BY tid DESC LIMIT ?,?""", (self._getReadablePartition(
             oid), oid, self._getPackTID(), offset, length))] or None
Example #44
0
 def _nextTID(self, ttid=None, divisor=None):
     """
     Compute the next TID based on the current time and check collisions.
     Also, if ttid is not None, divisor is mandatory adjust it so that
         tid % divisor == ttid % divisor
     while preserving
         min_tid < tid
     If ttid is None, divisor is ignored.
     When constraints allow, prefer decreasing generated TID, to avoid
     fast-forwarding to future dates.
     """
     tid = tidFromTime(time())
     min_tid = self._last_tid
     if tid <= min_tid:
         tid  = addTID(min_tid, 1)
         # We know we won't have room to adjust by decreasing.
         try_decrease = False
     else:
         try_decrease = True
     if ttid is not None:
         assert isinstance(ttid, basestring), repr(ttid)
         assert isinstance(divisor, (int, long)), repr(divisor)
         ref_remainder = u64(ttid) % divisor
         remainder = u64(tid) % divisor
         if ref_remainder != remainder:
             if try_decrease:
                 new_tid = addTID(tid, ref_remainder - divisor - remainder)
                 assert u64(new_tid) % divisor == ref_remainder, (dump(new_tid),
                     ref_remainder)
                 if new_tid <= min_tid:
                     new_tid = addTID(new_tid, divisor)
             else:
                 if ref_remainder > remainder:
                     ref_remainder += divisor
                 new_tid = addTID(tid, ref_remainder - remainder)
             assert min_tid < new_tid, (dump(min_tid), dump(tid), dump(new_tid))
             tid = new_tid
     self._last_tid = tid
     return self._last_tid
Example #45
0
 def testStorageFailureDuringTpcFinish(self):
     def answerTransactionFinished(conn, packet):
         if isinstance(packet, Packets.AnswerTransactionFinished):
             raise StoppedOperation
     cluster = NEOCluster()
     try:
         cluster.start()
         t, c = cluster.getTransaction()
         c.root()['x'] = PCounter()
         with cluster.master.filterConnection(cluster.client) as m2c:
             m2c.add(answerTransactionFinished)
             # After a storage failure during tpc_finish, the client
             # reconnects and checks that the transaction was really
             # committed.
             t.commit()
         # Also check that the master reset the last oid to a correct value.
         t.begin()
         self.assertEqual(1, u64(c.root()['x']._p_oid))
         self.assertFalse(cluster.client.new_oid_list)
         self.assertEqual(2, u64(cluster.client.new_oid()))
     finally:
         cluster.stop()
Example #46
0
 def getTransaction(self, tid, all=False):
     tid = util.u64(tid)
     q = self.query
     r = q("SELECT oids, user, description, ext, packed, ttid"
           " FROM trans WHERE partition=? AND tid=?",
           (self._getPartition(tid), tid)).fetchone()
     if not r and all:
         r = q("SELECT oids, user, description, ext, packed, ttid"
               " FROM ttrans WHERE tid=?", (tid,)).fetchone()
     if r:
         oids, user, description, ext, packed, ttid = r
         return splitOIDField(tid, oids), str(user), \
             str(description), str(ext), packed, util.p64(ttid)
Example #47
0
 def getTransaction(self, tid, all = False):
     tid = util.u64(tid)
     q = self.query
     r = q("SELECT oids, user, description, ext, packed, ttid"
           " FROM trans WHERE `partition` = %d AND tid = %d"
           % (self._getPartition(tid), tid))
     if not r and all:
         r = q("SELECT oids, user, description, ext, packed, ttid"
               " FROM ttrans WHERE tid = %d" % tid)
     if r:
         oids, user, desc, ext, packed, ttid = r[0]
         oid_list = splitOIDField(tid, oids)
         return oid_list, user, desc, ext, bool(packed), util.p64(ttid)
Example #48
0
 def getObjectHistory(self, oid, offset, length):
     # FIXME: This method doesn't take client's current ransaction id as
     # parameter, which means it can return transactions in the future of
     # client's transaction.
     oid = util.u64(oid)
     p64 = util.p64
     r = self.query("SELECT tid, IF(compression < 128, LENGTH(value),"
         "  CAST(CONV(HEX(SUBSTR(value, 5, 4)), 16, 10) AS INT))"
         " FROM obj LEFT JOIN data ON (obj.data_id = data.id)"
         " WHERE `partition` = %d AND oid = %d AND tid >= %d"
         " ORDER BY tid DESC LIMIT %d, %d" %
         (self._getPartition(oid), oid, self._getPackTID(), offset, length))
     if r:
         return [(p64(tid), length or 0) for tid, length in r]
Example #49
0
 def setup(self, zodb_dict, shift_oid=0):
     self.shift_oid = shift_oid
     self.next_oid = util.u64(self.new_oid())
     shift_oid += self.next_oid
     for mp, oid in self.mountpoints.iteritems():
         mp = zodb_dict[mp]
         new_oid = mp.oid
         try:
             new_oid += mp.shift_oid
         except AttributeError:
             new_oid += shift_oid
             shift_oid = mp.setup(zodb_dict, shift_oid)
         self.mapping[oid] = new_oid
     del self.mountpoints
     return shift_oid
Example #50
0
 def setup(self, zodb_dict, shift_oid=0):
     self.shift_oid = shift_oid
     self.next_oid = util.u64(self.new_oid())
     shift_oid += self.next_oid
     for mp, oid in self.mountpoints.iteritems():
         mp = zodb_dict[mp]
         new_oid = mp.oid
         try:
             new_oid += mp.shift_oid
         except AttributeError:
             new_oid += shift_oid
             shift_oid = mp.setup(zodb_dict, shift_oid)
         self.mapping[oid] = new_oid
     del self.mountpoints
     return shift_oid
Example #51
0
 def __str__(self):
     oid = u64(self.oid)
     tid = u64(self.tid)
     args = (oid, tid, len(self.data), self.data_txn)
     return 'Record %s:%s: %s (%s)' % args
Example #52
0
 def deleteTransaction(self, tid):
     tid = util.u64(tid)
     getPartition = self._getPartition
     self.query("DELETE FROM trans WHERE `partition`=%s AND tid=%s" %
         (self._getPartition(tid), tid))
Example #53
0
 def testVerificationCommitUnfinishedTransactions(self):
     """ Verification step should commit locked transactions """
     def delayUnlockInformation(conn, packet):
         return isinstance(packet, Packets.NotifyUnlockInformation)
     def onLockTransaction(storage, die=False):
         def lock(orig, *args, **kw):
             if die:
                 sys.exit()
             orig(*args, **kw)
             storage.master_conn.close()
         return Patch(storage.tm, lock=lock)
     cluster = NEOCluster(partitions=2, storage_count=2)
     try:
         cluster.start()
         s0, s1 = cluster.sortStorageList()
         t, c = cluster.getTransaction()
         r = c.root()
         r[0] = PCounter()
         tids = [r._p_serial]
         with onLockTransaction(s0), onLockTransaction(s1):
             t.commit()
         self.assertEqual(r._p_state, GHOST)
         self.tic()
         t.begin()
         x = r[0]
         self.assertEqual(x.value, 0)
         cluster.master.tm._last_oid = x._p_oid
         tids.append(r._p_serial)
         r[1] = PCounter()
         c.readCurrent(x)
         with cluster.moduloTID(1):
             with onLockTransaction(s0), onLockTransaction(s1):
                 t.commit()
             self.tic()
             t.begin()
             # The following line checks that s1 moved the transaction
             # metadata to final place during the verification phase.
             # If it didn't, a NEOStorageError would be raised.
             self.assertEqual(3, len(c.db().history(r._p_oid, 4)))
             y = r[1]
             self.assertEqual(y.value, 0)
             self.assertEqual([u64(o._p_oid) for o in (r, x, y)], range(3))
             r[2] = 'ok'
             with cluster.master.filterConnection(s0) as m2s:
                 m2s.add(delayUnlockInformation)
                 t.commit()
                 x.value = 1
                 # s0 will accept to store y (because it's not locked) but will
                 # never lock the transaction (packets from master delayed),
                 # so the last transaction will be dropped.
                 y.value = 2
                 di0 = s0.getDataLockInfo()
                 with onLockTransaction(s1, die=True):
                     self.commitWithStorageFailure(cluster.client, t)
     finally:
         cluster.stop()
     cluster.reset()
     (k, v), = set(s0.getDataLockInfo().iteritems()
                   ).difference(di0.iteritems())
     self.assertEqual(v, 1)
     k, = (k for k, v in di0.iteritems() if v == 1)
     di0[k] = 0 # r[2] = 'ok'
     self.assertEqual(di0.values(), [0, 0, 0, 0, 0])
     di1 = s1.getDataLockInfo()
     k, = (k for k, v in di1.iteritems() if v == 1)
     del di1[k] # x.value = 1
     self.assertEqual(di1.values(), [0])
     try:
         cluster.start()
         t, c = cluster.getTransaction()
         r = c.root()
         self.assertEqual(r[0].value, 0)
         self.assertEqual(r[1].value, 0)
         self.assertEqual(r[2], 'ok')
         self.assertEqual(di0, s0.getDataLockInfo())
         self.assertEqual(di1, s1.getDataLockInfo())
     finally:
         cluster.stop()
Example #54
0
    def load(self, oid, tid=None, before_tid=None):
        """
        Internal method which manage load, loadSerial and loadBefore.
        OID and TID (serial) parameters are expected packed.
        oid
            OID of object to get.
        tid
            If given, the exact serial at which OID is desired.
            before_tid should be None.
        before_tid
            If given, the excluded upper bound serial at which OID is desired.
            serial should be None.

        Return value: (3-tuple)
        - Object data (None if object creation was undone).
        - Serial of given data.
        - Next serial at which object exists, or None. Only set when tid
          parameter is not None.

        Exceptions:
            NEOStorageError
                technical problem
            NEOStorageNotFoundError
                object exists but no data satisfies given parameters
            NEOStorageDoesNotExistError
                object doesn't exist
            NEOStorageCreationUndoneError
                object existed, but its creation was undone

        Note that loadSerial is used during conflict resolution to load
        object's current version, which is not visible to us normaly (it was
        committed after our snapshot was taken).
        """
        # TODO:
        # - rename parameters (here? and in handlers & packet definitions)

        # When not bound to a ZODB Connection, load() may be the
        # first method called and last_tid may still be None.
        # This happens, for example, when opening the DB.
        if not (tid or before_tid) and self.last_tid:
            # Do not get something more recent than the last invalidation
            # we got from master.
            before_tid = p64(u64(self.last_tid) + 1)
        acquire = self._cache_lock_acquire
        release = self._cache_lock_release
        # XXX: Consider using a more fine-grained lock.
        self._load_lock_acquire()
        try:
            acquire()
            try:
                result = self._loadFromCache(oid, tid, before_tid)
                if result:
                    return result
                self._loading_oid = oid
            finally:
                release()
            data, tid, next_tid, _ = self._loadFromStorage(oid, tid, before_tid)
            acquire()
            try:
                if self._loading_oid:
                    # Common case (no race condition).
                    self._cache.store(oid, data, tid, next_tid)
                elif self._loading_invalidated:
                    # oid has just been invalidated.
                    if not next_tid:
                        next_tid = self._loading_invalidated
                    self._cache.store(oid, data, tid, next_tid)
                # Else, we just reconnected to the master.
            finally:
                release()
        finally:
            self._load_lock_release()
        return data, tid, next_tid
Example #55
0
    def undo(self, undone_tid, txn, tryToResolveConflict):
        txn_context = self._txn_container.get(txn)
        txn_info, txn_ext = self._getTransactionInformation(undone_tid)
        txn_oid_list = txn_info['oids']

        # Regroup objects per partition, to ask a minimum set of storage.
        partition_oid_dict = {}
        for oid in txn_oid_list:
            partition = self.pt.getPartition(oid)
            try:
                oid_list = partition_oid_dict[partition]
            except KeyError:
                oid_list = partition_oid_dict[partition] = []
            oid_list.append(oid)

        # Ask storage the undo serial (serial at which object's previous data
        # is)
        getCellList = self.pt.getCellList
        getCellSortKey = self.cp.getCellSortKey
        getConnForCell = self.cp.getConnForCell
        queue = self._thread_container.queue
        ttid = txn_context['ttid']
        undo_object_tid_dict = {}
        snapshot_tid = p64(u64(self.last_tid) + 1)
        for partition, oid_list in partition_oid_dict.iteritems():
            cell_list = getCellList(partition, readable=True)
            # We do want to shuffle before getting one with the smallest
            # key, so that all cells with the same (smallest) key has
            # identical chance to be chosen.
            shuffle(cell_list)
            storage_conn = getConnForCell(min(cell_list, key=getCellSortKey))
            storage_conn.ask(Packets.AskObjectUndoSerial(ttid,
                snapshot_tid, undone_tid, oid_list),
                queue=queue, undo_object_tid_dict=undo_object_tid_dict)

        # Wait for all AnswerObjectUndoSerial. We might get OidNotFoundError,
        # meaning that objects in transaction's oid_list do not exist any
        # longer. This is the symptom of a pack, so forbid undoing transaction
        # when it happens.
        try:
            self.waitResponses(queue)
        except NEOStorageNotFoundError:
            self.dispatcher.forget_queue(queue)
            raise UndoError('non-undoable transaction')

        # Send undo data to all storage nodes.
        for oid in txn_oid_list:
            current_serial, undo_serial, is_current = undo_object_tid_dict[oid]
            if is_current:
                data = None
            else:
                # Serial being undone is not the latest version for this
                # object. This is an undo conflict, try to resolve it.
                try:
                    # Load the latest version we are supposed to see
                    data = self.load(oid, current_serial)[0]
                    # Load the version we were undoing to
                    undo_data = self.load(oid, undo_serial)[0]
                except NEOStorageNotFoundError:
                    raise UndoError('Object not found while resolving undo '
                        'conflict')
                # Resolve conflict
                try:
                    data = tryToResolveConflict(oid, current_serial,
                        undone_tid, undo_data, data)
                except ConflictError:
                    raise UndoError('Some data were modified by a later ' \
                        'transaction', oid)
                undo_serial = None
            self._store(txn_context, oid, current_serial, data, undo_serial)

        return None, txn_oid_list
Example #56
0
    def test_findUndoTID(self):
        self.setNumPartitions(4, True)
        db = self.db
        tid1 = self.getNextTID()
        tid2 = self.getNextTID()
        tid3 = self.getNextTID()
        tid4 = self.getNextTID()
        tid5 = self.getNextTID()
        oid1 = self.getOID(1)
        foo = db.holdData("3" * 20, 'foo', 0)
        bar = db.holdData("4" * 20, 'bar', 0)
        db.releaseData((foo, bar))
        db.storeTransaction(
            tid1, (
                (oid1, foo, None),
            ), None, temporary=False)

        # Undoing oid1 tid1, OK: tid1 is latest
        # Result: current tid is tid1, data_tid is None (undoing object
        # creation)
        self.assertEqual(
            db.findUndoTID(oid1, tid5, tid4, tid1, None),
            (tid1, None, True))

        # Store a new transaction
        db.storeTransaction(
            tid2, (
                (oid1, bar, None),
            ), None, temporary=False)

        # Undoing oid1 tid2, OK: tid2 is latest
        # Result: current tid is tid2, data_tid is tid1
        self.assertEqual(
            db.findUndoTID(oid1, tid5, tid4, tid2, None),
            (tid2, tid1, True))

        # Undoing oid1 tid1, Error: tid2 is latest
        # Result: current tid is tid2, data_tid is -1
        self.assertEqual(
            db.findUndoTID(oid1, tid5, tid4, tid1, None),
            (tid2, None, False))

        # Undoing oid1 tid1 with tid2 being undone in same transaction,
        # OK: tid1 is latest
        # Result: current tid is tid1, data_tid is None (undoing object
        # creation)
        # Explanation of transaction_object: oid1, no data but a data serial
        # to tid1
        self.assertEqual(
            db.findUndoTID(oid1, tid5, tid4, tid1,
                (u64(oid1), None, tid1)),
            (tid1, None, True))

        # Store a new transaction
        db.storeTransaction(
            tid3, (
                (oid1, None, tid1),
            ), None, temporary=False)

        # Undoing oid1 tid1, OK: tid3 is latest with tid1 data
        # Result: current tid is tid2, data_tid is None (undoing object
        # creation)
        self.assertEqual(
            db.findUndoTID(oid1, tid5, tid4, tid1, None),
            (tid3, None, True))
Example #57
0
 def __str__(self):
     return 'Transaction #%s: %s %s' \
         % (u64(self.tid), self.user, self.status)
Example #58
0
 def __dump(self, storage):
     return {u64(t.tid): [(u64(o.oid), o.data_txn and u64(o.data_txn),
                           None if o.data is None else makeChecksum(o.data))
                          for o in t]
             for t in storage.iterator()}