def triggerBackup(self, node): tid_list = self.tid_list tid = self.app.getLastTransaction() replicate_list = [] for offset, cell in self.app.pt.iterNodeCell(node): max_tid = tid_list[offset] if max_tid and self.primary_partition_dict[offset] is node and \ max(cell.backup_tid, cell.replicating) < max_tid[-1]: cell.replicating = tid replicate_list.append(offset) if not replicate_list: return getCellList = self.pt.getCellList source_dict = {} address_set = set() for offset in replicate_list: cell_list = getCellList(offset, readable=True) random.shuffle(cell_list) assert cell_list, offset for cell in cell_list: addr = cell.getAddress() if addr in address_set: break else: address_set.add(addr) source_dict[offset] = addr logging.debug("ask %s to replicate partition %u up to %s from %r", uuid_str(node.getUUID()), offset, dump(tid), addr) node.getConnection().notify(Packets.Replicate( tid, self.name, source_dict))
def run(self): try: super(ServerNode, self).run() finally: self._afterRun() logging.debug('stopping %r', self) self.em.epoll.exit()
def lockObject(self, ttid, serial, oid, unlock=False): """ Take a write lock on given object, checking that "serial" is current. Raises: DelayedError ConflictError """ # check if the object if locked locking_tid = self._store_lock_dict.get(oid) if locking_tid == ttid and unlock: logging.info('Deadlock resolution on %r:%r', dump(oid), dump(ttid)) # A duplicate store means client is resolving a deadlock, so # drop the lock it held on this object, and drop object data for # consistency. del self._store_lock_dict[oid] data_id = self._transaction_dict[ttid].delObject(oid) if data_id: self._app.dm.pruneData((data_id,)) # Give a chance to pending events to take that lock now. self._app.executeQueuedEvents() # Attemp to acquire lock again. locking_tid = self._store_lock_dict.get(oid) if locking_tid is None: previous_serial = None elif locking_tid == ttid: # If previous store was an undo, next store must be based on # undo target. previous_serial = self._transaction_dict[ttid].getObject(oid)[2] if previous_serial is None: # XXX: use some special serial when previous store was not # an undo ? Maybe it should just not happen. logging.info('Transaction %s storing %s more than once', dump(ttid), dump(oid)) elif locking_tid < ttid: # We have a bigger TTID than locking transaction, so we are younger: # enter waiting queue so we are handled when lock gets released. # We also want to delay (instead of conflict) if the client is # so faster that it is committing another transaction before we # processed UnlockInformation from the master. logging.info('Store delayed for %r:%r by %r', dump(oid), dump(ttid), dump(locking_tid)) raise DelayedError else: # We have a smaller TTID than locking transaction, so we are older: # this is a possible deadlock case, as we might already hold locks # the younger transaction is waiting upon. Make client release # locks & reacquire them by notifying it of the possible deadlock. logging.info('Possible deadlock on %r:%r with %r', dump(oid), dump(ttid), dump(locking_tid)) raise ConflictError(ZERO_TID) if previous_serial is None: previous_serial = self._app.dm.getLastObjectTID(oid) if previous_serial is not None and previous_serial != serial: logging.info('Resolvable conflict on %r:%r', dump(oid), dump(ttid)) raise ConflictError(previous_serial) logging.debug('Transaction %s storing %s', dump(ttid), dump(oid)) self._store_lock_dict[oid] = ttid
def __init__(self, config): super(Application, self).__init__( config.getSSL(), config.getDynamicMasterList()) self.tm = TransactionManager(self.onTransactionCommitted) self.name = config.getCluster() self.server = config.getBind() self.autostart = config.getAutostart() self.storage_readiness = set() for master_address in config.getMasters(): self.nm.createMaster(address=master_address) logging.debug('IP address is %s, port is %d', *self.server) # Partition table replicas, partitions = config.getReplicas(), config.getPartitions() if replicas < 0: raise RuntimeError, 'replicas must be a positive integer' if partitions <= 0: raise RuntimeError, 'partitions must be more than zero' self.pt = PartitionTable(partitions, replicas) logging.info('Configuration:') logging.info('Partitions: %d', partitions) logging.info('Replicas : %d', replicas) logging.info('Name : %s', self.name) self.listening_conn = None self.primary = None self.primary_master_node = None self.cluster_state = None uuid = config.getUUID() if uuid: self.uuid = uuid # election related data self.unconnected_master_node_set = set() self.negotiating_master_node_set = set() self.master_address_dict = weakref.WeakKeyDictionary() self._current_manager = None # backup upstream_cluster = config.getUpstreamCluster() if upstream_cluster: if upstream_cluster == self.name: raise ValueError("upstream cluster name must be" " different from cluster name") self.backup_app = BackupApplication(self, upstream_cluster, config.getUpstreamMasters()) self.administration_handler = administration.AdministrationHandler( self) self.secondary_master_handler = secondary.SecondaryMasterHandler(self) self.client_service_handler = client.ClientServiceHandler(self) self.storage_service_handler = storage.StorageServiceHandler(self) registerLiveDebugger(on_log=self.log)
def query(self, query): printable_char_list = [] for c in query.split('\n', 1)[0][:70]: if c not in string.printable or c in '\t\x0b\x0c\r': c = '\\x%02x' % ord(c) printable_char_list.append(c) logging.debug('querying %s...', ''.join(printable_char_list)) return self.conn.execute(query)
def register(self, conn, ttid): """ Register a transaction, it may be already registered """ if ttid not in self._transaction_dict: uuid = conn.getUUID() logging.debug('Register TXN %s for %s', dump(ttid), uuid_str(uuid)) self._transaction_dict[ttid] = Transaction(uuid, ttid)
def _connectToPrimaryNode(self): """ Lookup for the current primary master node """ logging.debug('connecting to primary master...') self.start() index = -1 ask = self._ask handler = self.primary_bootstrap_handler while 1: # Get network connection to primary master while 1: if self.primary_master_node is not None: # If I know a primary master node, pinpoint it. self.trying_master_node = self.primary_master_node self.primary_master_node = None else: # Otherwise, check one by one. master_list = self.nm.getMasterList() index = (index + 1) % len(master_list) self.trying_master_node = master_list[index] # Connect to master conn = MTClientConnection(self, self.notifications_handler, node=self.trying_master_node, dispatcher=self.dispatcher) # Query for primary master node if conn.getConnector() is None: # This happens if a connection could not be established. logging.error('Connection to master node %s failed', self.trying_master_node) continue try: ask(conn, Packets.RequestIdentification( NodeTypes.CLIENT, self.uuid, None, self.name), handler=handler) except ConnectionClosed: continue # If we reached the primary master node, mark as connected if self.primary_master_node is not None and \ self.primary_master_node is self.trying_master_node: break logging.info('Connected to %s', self.primary_master_node) try: # Request identification and required informations to be # operational. Might raise ConnectionClosed so that the new # primary can be looked-up again. logging.info('Initializing from master') ask(conn, Packets.AskNodeInformation(), handler=handler) ask(conn, Packets.AskPartitionTable(), handler=handler) ask(conn, Packets.AskLastTransaction(), handler=handler) if self.pt.operational(): break except ConnectionClosed: logging.error('Connection to %s lost', self.trying_master_node) self.primary_master_node = None logging.info("Connected and ready") return conn
def abortFor(self, uuid): """ Abort any non-locked transaction of a node """ logging.debug('Abort for %s', uuid_str(uuid)) # abort any non-locked transaction of this node for transaction in self._transaction_dict.values(): if transaction.getUUID() == uuid: self.abort(transaction.getTTID())
def abort(self, ttid, uuid): """ Abort a transaction """ logging.debug('Abort TXN %s for %s', dump(ttid), uuid_str(uuid)) if self[ttid].isPrepared(): raise ProtocolError("commit already requested for ttid %s" % dump(ttid)) del self[ttid]
def commit(self): logging.debug('committing...') self._commit() # Instead of cancelling a timeout that would be set to defer a commit, # we simply use to a boolean so that _deferredCommit() does nothing. # IOW, epoll may wait wake up for nothing but that should be rare, # because most immediate commits are usually quickly followed by # deferred commits. self._deferred = 0
def unlock(self, ttid): """ Unlock transaction """ tid = self._transaction_dict[ttid].getTID() logging.debug('Unlock TXN %s (ttid=%s)', dump(tid), dump(ttid)) dm = self._app.dm dm.unlockTransaction(tid, ttid) self._app.em.setTimeout(time() + 1, dm.deferCommit()) self.abort(ttid, even_if_locked=True)
def broadcastPartitionChanges(self, cell_list): """Broadcast a Notify Partition Changes packet.""" logging.debug('broadcastPartitionChanges') if cell_list: self.pt.log() ptid = self.pt.setNextID() packet = Packets.NotifyPartitionChanges(ptid, cell_list) for node in self.nm.getIdentifiedList(): if node.isRunning() and not node.isMaster(): node.notify(packet)
def lock(self, ttid, uuid): """ Set that a node has locked the transaction. If transaction is completely locked, calls function given at instanciation time. """ logging.debug('Lock TXN %s for %s', dump(ttid), uuid_str(uuid)) if self[ttid].lock(uuid) and self._queue[0] == ttid: # all storage are locked and we unlock the commit queue self._unlockPending()
def register(self, uuid, ttid): """ Register a transaction, it may be already registered """ logging.debug('Register TXN %s for %s', dump(ttid), uuid_str(uuid)) transaction = self._transaction_dict.get(ttid, None) if transaction is None: transaction = Transaction(uuid, ttid) self._uuid_dict.setdefault(uuid, set()).add(transaction) self._transaction_dict[ttid] = transaction return transaction
def checkRange(self, conn, *args): if self.conn_dict.get(conn, self) != conn.getPeerId(): # Ignore answers to old requests, # because we did nothing to cancel them. logging.info("ignored AnswerCheck*Range%r", args) return self.conn_dict[conn] = args answer_set = set(self.conn_dict.itervalues()) if len(answer_set) > 1: for answer in answer_set: if type(answer) is not tuple: return # TODO: Automatically tell corrupted cells to fix their data # if we know a good source. # For the moment, tell master to put them in CORRUPTED state # and keep up checking if useful. uuid = self.app.uuid args = None if self.source is None else self.conn_dict[ None if self.source.getUUID() == uuid else self.source.getConnection()] uuid_list = [] for conn, answer in self.conn_dict.items(): if answer != args: del self.conn_dict[conn] if conn is None: uuid_list.append(uuid) else: uuid_list.append(conn.getUUID()) self.app.closeClient(conn) p = Packets.NotifyPartitionCorrupted(self.partition, uuid_list) self.app.master_conn.notify(p) if len(self.conn_dict) <= 1: logging.warning("check of partition %u aborted", self.partition) self.queue.clear() self._nextPartition() return try: count, _, max_tid = args except ValueError: # AnswerCheckSerialRange count, _, self.next_tid, _, max_oid = args if count < CHECK_COUNT: logging.debug("partition %u checked from %s to %s", self.partition, dump(self.min_tid), dump(self.max_tid)) self._nextPartition() return self.next_oid = add64(max_oid, 1) else: # AnswerCheckTIDRange if count < CHECK_COUNT: self.next_tid = self.min_tid self.next_oid = ZERO_OID else: self.next_tid = add64(max_tid, 1) self._nextRange()
def finish(): if tid: self.storeTransaction(tid, object_list, ( (x[0] for x in object_list), str(txn.user), str(txn.description), cPickle.dumps(txn.extension), False, tid), False) self.releaseData(data_id_list) logging.debug("TXN %s imported (user=%r, desc=%r, len(oid)=%s)", util.dump(tid), txn.user, txn.description, len(object_list)) del object_list[:], data_id_list[:] if self._last_commit + 1 < time.time(): self.commit() self.zodb_tid = u64(tid)
def _dropConnections(self): """Drop connections.""" for conn in self.connection_dict.values(): # Drop first connection which looks not used with conn.lock: if not conn.pending() and \ not self.app.dispatcher.registered(conn): del self.connection_dict[conn.getUUID()] conn.setReconnectionNoDelay() conn.close() logging.debug('_dropConnections: connection to ' 'storage node %s:%d closed', *conn.getAddress()) if len(self.connection_dict) <= self.max_pool_size: break
def finish(self): offset = self.current_partition tid = self.replicate_tid del self.current_partition, self.replicate_tid p = self.partition_dict[offset] p.next_obj = add64(tid, 1) self.updateBackupTID() if not p.max_ttid: p = Packets.NotifyReplicationDone(offset, tid) self.app.master_conn.notify(p) logging.debug("partition %u replicated up to %s from %r", offset, dump(tid), self.current_node) self.getCurrentConnection().setReconnectionNoDelay() self._nextPartition()
def stop(self): logging.debug("stopping %s", self) client = self.__dict__.get("client") client is None or self.__dict__.pop("db", client).close() node_list = self.admin_list + self.storage_list + self.master_list for node in node_list: node.stop() try: node_list.append(client.poll_thread) except AttributeError: # client is None or thread is already stopped pass self.join(node_list) logging.debug("stopped %s", self) self._unpatch()
def vote(self, ttid, txn_info=None): """ Store transaction information received from client node """ logging.debug('Vote TXN %s', dump(ttid)) transaction = self._transaction_dict[ttid] object_list = transaction.getObjectList() if txn_info: user, desc, ext, oid_list = txn_info txn_info = oid_list, user, desc, ext, False, ttid transaction.has_trans = True # store metadata to temporary table dm = self._app.dm dm.storeTransaction(ttid, object_list, txn_info) dm.commit()
def begin(self, node, tid=None): """ Generate a new TID """ if tid is None: # No TID requested, generate a temporary one tid = self._nextTID() else: # Use of specific TID requested, queue it immediately and update # last TID. self._queue.append(tid) self.setLastTID(tid) txn = self._ttid_dict[tid] = Transaction(node, tid) logging.debug('Begin %s', txn) return tid
def notifyPartitionChanges(self, conn, ptid, cell_list): """This is very similar to Send Partition Table, except that the information is only about changes from the previous.""" app = self.app if ptid <= app.pt.getID(): # Ignore this packet. logging.debug('ignoring older partition changes') return # update partition table in memory and the database app.pt.update(ptid, cell_list, app.nm) app.dm.changePartitionTable(ptid, cell_list) # Check changes for replications app.replicator.notifyPartitionChanges(cell_list)
def abortFor(self, uuid): """ Abort any non-locked transaction of a node """ logging.debug('Abort for %s', uuid_str(uuid)) # BUG: Discarding voted transactions must only be a decision of the # master, and for this, we'll need to review how transactions are # aborted. As a workaround, we rely on the fact that lock() will # disconnect from the master in case of LockInformation. # abort any non-locked transaction of this node for ttid in [x.getTTID() for x in self._uuid_dict.get(uuid, [])]: self.abort(ttid) # cleanup _uuid_dict if no transaction remains for this node transaction_set = self._uuid_dict.get(uuid) if transaction_set is not None and not transaction_set: del self._uuid_dict[uuid]
def lock(self, ttid, tid): """ Lock a transaction """ logging.debug('Lock TXN %s (ttid=%s)', dump(tid), dump(ttid)) try: transaction = self._transaction_dict[ttid] except KeyError: raise ProtocolError("unknown ttid %s" % dump(ttid)) # remember that the transaction has been locked transaction.lock() self._load_lock_dict.update( dict.fromkeys(transaction.getOIDList(), ttid)) # commit transaction and remember its definitive TID if transaction.has_trans: self._app.dm.lockTransaction(tid, ttid) transaction.setTID(tid)
def setupDB(self, clear_databases=True): if self.adapter == 'MySQL': setupMySQLdb(self.db_list, self.db_user, self.db_password, clear_databases) elif self.adapter == 'SQLite': if clear_databases: for db in self.db_list: if db is None: continue db = self.db_template(db) try: os.remove(db) except OSError, e: if e.errno != errno.ENOENT: raise else: logging.debug('%r deleted', db)
def prepare(self, ttid, divisor, oid_list, uuid_list, msg_id): """ Prepare a transaction to be finished """ txn = self[ttid] # maybe not the fastest but _queue should be often small if ttid in self._queue: tid = ttid else: tid = self._nextTID(ttid, divisor) self._queue.append(ttid) logging.debug('Finish TXN %s for %s (was %s)', dump(tid), txn.getNode(), dump(ttid)) txn.prepare(tid, oid_list, uuid_list, msg_id) # check if greater and foreign OID was stored if oid_list: self.setLastOID(max(oid_list)) return tid
def _initNodeConnection(self, node): """Init a connection to a given storage node.""" app = self.app logging.debug('trying to connect to %s - %s', node, node.getState()) conn = MTClientConnection(app, app.storage_event_handler, node, dispatcher=app.dispatcher) p = Packets.RequestIdentification(NodeTypes.CLIENT, app.uuid, None, app.name) try: app._ask(conn, p, handler=app.storage_bootstrap_handler) except ConnectionClosed: logging.error('Connection to %r failed', node) except NodeNotReady: logging.info('%r not ready', node) else: logging.info('Connected %r', node) return conn self.notifyFailure(node)
def undoLog(self, first, last, filter=None, block=0): # XXX: undoLog is broken if last < 0: # See FileStorage.py for explanation last = first - last # First get a list of transactions from all storage nodes. # Each storage node will return TIDs only for UP_TO_DATE state and # FEEDING state cells queue = self._thread_container.queue packet = Packets.AskTIDs(first, last, INVALID_PARTITION) tid_set = set() for storage_node in self.pt.getNodeSet(True): conn = self.cp.getConnForNode(storage_node) if conn is None: continue conn.ask(packet, queue=queue, tid_set=tid_set) # Wait for answers from all storages. self.waitResponses(queue) # Reorder tids ordered_tids = sorted(tid_set, reverse=True) logging.debug("UndoLog tids %s", map(dump, ordered_tids)) # For each transaction, get info undo_info = [] append = undo_info.append for tid in ordered_tids: (txn_info, txn_ext) = self._getTransactionInformation(tid) if filter is None or filter(txn_info): txn_info.pop('packed') txn_info.pop("oids") self._insertMetadata(txn_info, txn_ext) append(txn_info) if len(undo_info) >= last - first: break # Check we return at least one element, otherwise call # again but extend offset if len(undo_info) == 0 and not block: undo_info = self.undoLog(first=first, last=last*5, filter=filter, block=1) return undo_info
def tpc_vote(self, transaction, tryToResolveConflict): """Store current transaction.""" txn_context = self._txn_container.get(transaction) result = self.waitStoreResponses(txn_context, tryToResolveConflict) ttid = txn_context['ttid'] # Store data on each node assert not txn_context['data_dict'], txn_context packet = Packets.AskStoreTransaction(ttid, str(transaction.user), str(transaction.description), dumps(transaction._extension), txn_context['cache_dict']) queue = txn_context['queue'] trans_nodes = [] for node, conn in self.cp.iterateForObject(ttid): logging.debug("voting transaction %s on %s", dump(ttid), dump(conn.getUUID())) try: conn.ask(packet, queue=queue) except ConnectionClosed: continue trans_nodes.append(node) # check at least one storage node accepted if trans_nodes: involved_nodes = txn_context['involved_nodes'] packet = Packets.AskVoteTransaction(ttid) for node in involved_nodes.difference(trans_nodes): conn = self.cp.getConnForNode(node) if conn is not None: try: conn.ask(packet, queue=queue) except ConnectionClosed: pass involved_nodes.update(trans_nodes) self.waitResponses(queue) txn_context['voted'] = None # We must not go further if connection to master was lost since # tpc_begin, to lower the probability of failing during tpc_finish. if 'error' in txn_context: raise NEOStorageError(txn_context['error']) return result logging.error('tpc_vote failed') raise NEOStorageError('tpc_vote failed')
def __init__(self, config): super(Application, self).__init__( config.getSSL(), config.getDynamicMasterList()) for address in config.getMasters(): self.nm.createMaster(address=address) self.name = config.getCluster() self.server = config.getBind() logging.debug('IP address is %s, port is %d', *self.server) # The partition table is initialized after getting the number of # partitions. self.pt = None self.uuid = config.getUUID() self.request_handler = MasterRequestEventHandler(self) self.master_event_handler = MasterEventHandler(self) self.cluster_state = None self.reset() registerLiveDebugger(on_log=self.log)
def answerTIDsFrom(self, conn, tid_list): logging.debug('Get %u TIDs from %r', len(tid_list), conn) self.app.setHandlerData(tid_list)
def run(self): """ Recover the status about the cluster. Obtain the last OID, the last TID, and the last Partition Table ID from storage nodes, then get back the latest partition table or make a new table from scratch, if this is the first time. A new primary master may also arise during this phase. """ logging.info('begin the recovery of the status') app = self.app pt = app.pt = app.newPartitionTable() app.changeClusterState(ClusterStates.RECOVERING) self.try_secondary = True # collect the last partition table available poll = app.em.poll while 1: if self.try_secondary: # Keep trying to connect to all other known masters, # to make sure there is a challege between each pair # of masters in the cluster. If we win, all connections # opened here will be closed. self.try_secondary = False node_list = [] for node in app.nm.getMasterList(): if not (node is app._node or node.isConnected(True)): # During recovery, master nodes are not put back in # DOWN state by handlers. This is done # entirely in this method (here and after this poll # loop), to minimize the notification packets. if not node.isDown(): node.setDown() node_list.append(node) ClientConnection(app, app.election_handler, node) if node_list: app.broadcastNodesInformation(node_list) poll(1) if pt.filled(): # A partition table exists, we are starting an existing # cluster. node_list = pt.getOperationalNodeSet() if app._startup_allowed: node_list = [ node for node in node_list if node.isPending() ] elif node_list: # we want all nodes to be there if we're going to truncate if app.truncate_tid: node_list = pt.getNodeSet() if not all(node.isPending() for node in node_list): continue elif app._startup_allowed or app.autostart: # No partition table and admin allowed startup, we are # creating a new cluster out of all pending nodes. node_list = app.nm.getStorageList(only_identified=True) if not app._startup_allowed and len(node_list) < app.autostart: continue else: continue if node_list and not any(node.getConnection().isPending() for node in node_list): if pt.filled(): if app.truncate_tid: node_list = app.nm.getIdentifiedList( pool_set={ uuid for uuid, tid in self.truncate_dict.iteritems() if not tid or app.truncate_tid < tid }) if node_list: truncate = Packets.Truncate(app.truncate_tid) for node in node_list: conn = node.getConnection() conn.send(truncate) self.handlerSwitched(conn, False) continue node_list = pt.getConnectedNodeList() break logging.info('startup allowed') for node in node_list: assert node.isPending(), node node.setRunning() for node in app.nm.getMasterList(): if not (node is app._node or node.isIdentified()): if node.isConnected(True): node.getConnection().close() assert node.isDown(), node elif not node.isDown(): assert self.try_secondary, node node.setDown() node_list.append(node) app.broadcastNodesInformation(node_list) if pt.getID() is None: logging.info('creating a new partition table') pt.make(node_list) self._notifyAdmins( Packets.SendPartitionTable(pt.getID(), pt.getReplicas(), pt.getRowList())) else: cell_list = pt.outdate() if cell_list: self._notifyAdmins( Packets.NotifyPartitionChanges(pt.setNextID(), pt.getReplicas(), cell_list)) if app.backup_tid: pt.setBackupTidDict(self.backup_tid_dict) app.backup_tid = pt.getBackupTid() logging.debug('cluster starts this partition table:') pt.log()
def answerNodeInformation(self, conn): # XXX: This will no more exists when the initialization module will be # implemented for factorize code (as done for bootstrap) logging.debug("answerNodeInformation")
def _nextPartition(self): app = self.app def connect(node, uuid=app.uuid, name=app.name): if node.getUUID() == app.uuid: return if node.isConnected(connecting=True): conn = node.getConnection() conn.asClient() else: conn = ClientConnection(app, StorageOperationHandler(app), node) conn.ask( Packets.RequestIdentification(NodeTypes.STORAGE, uuid, app.server, name, app.id_timestamp)) self.conn_dict[conn] = node.isIdentified() conn_set = set(self.conn_dict) conn_set.discard(None) try: self.conn_dict.clear() while True: try: partition, (name, source), min_tid, max_tid = \ self.queue.popleft() except IndexError: return cell = app.pt.getCell(partition, app.uuid) if cell is None or cell.isOutOfDate(): msg = "discarded or out-of-date" else: try: for cell in app.pt.getCellList(partition): # XXX: Ignore corrupted cells for the moment # because we're still unable to fix them # (see also AdministrationHandler of master) if cell.isReadable(): #if not cell.isOutOfDate(): connect(cell.getNode()) if source: node = app.nm.getByAddress(source) if name: source = app.nm.createStorage(address=source) \ if node is None else node connect(source, None, name) elif (node.getUUID() == app.uuid or node.isConnected(connecting=True) and node.getConnection() in self.conn_dict): source = node else: msg = "unavailable source" if self.conn_dict: break msg = "no replica" except ConnectionClosed: msg = "connection closed" finally: conn_set.update(self.conn_dict) self.conn_dict.clear() logging.error("Failed to start checking partition %u (%s)", partition, msg) conn_set.difference_update(self.conn_dict) finally: for conn in conn_set: app.closeClient(conn) logging.debug("start checking partition %u from %s to %s", partition, dump(min_tid), dump(max_tid)) self.min_tid = self.next_tid = min_tid self.max_tid = max_tid self.next_oid = None self.partition = partition self.source = source def start(): if app.tm.isLockedTid(max_tid): app.tm.read_queue.queueEvent(start) return args = partition, CHECK_COUNT, min_tid, max_tid p = Packets.AskCheckTIDRange(*args) for conn, identified in self.conn_dict.items(): self.conn_dict[conn] = conn.ask(p) if identified else None self.conn_dict[None] = app.dm.checkTIDRange(*args) start()
def store(self, oid, serial, data, version, transaction): """Store object.""" logging.debug('storing oid %s serial %s', dump(oid), dump(serial)) if not serial: # BBB serial = ZERO_TID self._store(self._txn_container.get(transaction), oid, serial, data)