def tpc_abort(self, transaction): """Abort current transaction.""" txn_context = self._txn_container.pop(transaction) if txn_context is None: return ttid = txn_context['ttid'] p = Packets.AbortTransaction(ttid) getConnForNode = self.cp.getConnForNode # cancel transaction one all those nodes nodes = txn_context['involved_nodes'] nodes |= txn_context['checked_nodes'] for node in nodes: conn = getConnForNode(node) if conn is None: continue try: conn.notify(p) except: logging.exception( 'Exception in tpc_abort while notifying' 'storage node %r of abortion, ignoring.', conn) conn = self.master_conn if conn is not None: conn.notify(p) # We don't need to flush queue, as it won't be reused by future # transactions (deleted on next line & indexed by transaction object # instance). self.dispatcher.forget_queue(txn_context['queue'], flush_queue=False)
def notifyTransactionAborted(self, ttid, uuids): uuid_set = self.getStorageReadySet() uuid_set.intersection_update(uuids) if uuid_set: p = Packets.AbortTransaction(ttid, ()) getByUUID = self.nm.getByUUID for uuid in uuid_set: getByUUID(uuid).send(p)
def tpc_abort(self, transaction): """Abort current transaction.""" txn_context = self._txn_container.pop(transaction) if txn_context is None: return # We want that the involved nodes abort a transaction after any # other packet sent by the client for this transaction. IOW, if we # already have a connection with a storage node, potentially with # a pending write, aborting only via the master may lead to a race # condition. The consequence would be that storage nodes lock oids # forever. p = Packets.AbortTransaction(txn_context.ttid, ()) for conn in txn_context.conn_dict.itervalues(): if conn is not None: try: conn.send(p) except ConnectionClosed: pass # Because we want to be sure that the involved nodes are notified, # we still have to send the full list to the master. Most of the # time, the storage nodes get 2 AbortTransaction packets, and the # second one is rarely useful. Another option would be that the # storage nodes keep a list of aborted transactions, but the # difficult part would be to avoid a memory leak. try: notify = self.master_conn.send except AttributeError: pass else: try: notify( Packets.AbortTransaction(txn_context.ttid, list(txn_context.conn_dict))) except ConnectionClosed: pass # We don't need to flush queue, as it won't be reused by future # transactions (deleted on next line & indexed by transaction object # instance). self.dispatcher.forget_queue(txn_context.queue, flush_queue=False)
def abortTransaction(self, conn, tid, uuid_list): # Consider a failure when the connection between the storage and the # client breaks while the answer to the first write is sent back. # In other words, the client can not know the exact set of nodes that # know this transaction, and it sends us all nodes it considered for # writing. # We must also add those that are waiting for this transaction to be # finished (returned by tm.abort), because they may have join the # cluster after that the client started to abort. app = self.app involved = app.tm.abort(tid, conn.getUUID()) involved.update(uuid_list) involved.intersection_update(app.getStorageReadySet()) if involved: p = Packets.AbortTransaction(tid, ()) getByUUID = app.nm.getByUUID for involved in involved: getByUUID(involved).send(p)