示例#1
0
 def verify_undoable(self, cursor, undo_tid):
     """Raise UndoError if it is not safe to undo the specified txn."""
     raise UndoError("Undo is not supported by this storage")
示例#2
0
文件: app.py 项目: pyzh/neoppod
    def undo(self, undone_tid, txn):
        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
        getConnForNode = self.cp.getConnForNode
        queue = self._thread_container.queue
        ttid = txn_context.ttid
        undo_object_tid_dict = {}
        snapshot_tid = p64(u64(self.last_tid) + 1)
        kw = {
            'queue': queue,
            'partition_oid_dict': partition_oid_dict,
            'undo_object_tid_dict': undo_object_tid_dict,
        }
        while partition_oid_dict:
            for partition, oid_list in partition_oid_dict.iteritems():
                cell_list = [
                    cell for cell in getCellList(partition, readable=True)
                    # Exclude nodes that may have missed previous resolved
                    # conflicts. For example, if a network failure happened
                    # only between the client and the storage, the latter would
                    # still be readable until we commit.
                    if txn_context.involved_nodes.get(cell.getUUID(), 0) < 2
                ]
                storage_conn = getConnForNode(
                    min(cell_list, key=getCellSortKey).getNode())
                storage_conn.ask(Packets.AskObjectUndoSerial(
                    ttid, snapshot_tid, undone_tid, oid_list),
                                 partition=partition,
                                 **kw)

            # 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
                    if current_serial == ttid:
                        # XXX: see TODO below
                        data = txn_context.cache_dict[oid]
                    else:
                        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 = txn_context.Storage.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
                # TODO: The situation is similar to deadlock avoidance.
                #       Reenable the cache size limit to avoid OOM when there's
                #       a huge amount conflicting data, and get the data back
                #       from the storage when it's not in cache_dict anymore.
                txn_context.cache_size = -float('inf')
            self._store(txn_context, oid, current_serial, data, undo_serial)

        self.waitStoreResponses(txn_context)
        return None, txn_oid_list