예제 #1
0
 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))
예제 #2
0
파일: __init__.py 프로젝트: Nexedi/neoppod
 def run(self):
     try:
         super(ServerNode, self).run()
     finally:
         self._afterRun()
         logging.debug('stopping %r', self)
         self.em.epoll.exit()
예제 #3
0
 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
예제 #4
0
파일: app.py 프로젝트: Nexedi/neoppod
    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)
예제 #5
0
파일: sqlite.py 프로젝트: Nexedi/neoppod
 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)
예제 #6
0
 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)
예제 #7
0
파일: app.py 프로젝트: vpelletier/neoppod
 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
예제 #8
0
 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())
예제 #9
0
 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]
예제 #10
0
파일: manager.py 프로젝트: Nexedi/neoppod
 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
예제 #11
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)
예제 #12
0
파일: app.py 프로젝트: Nexedi/neoppod
 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)
예제 #13
0
 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()
예제 #14
0
 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
예제 #15
0
파일: checker.py 프로젝트: Nexedi/neoppod
 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()
예제 #16
0
파일: importer.py 프로젝트: Nexedi/neoppod
 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)
예제 #17
0
파일: pool.py 프로젝트: vpelletier/neoppod
 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
예제 #18
0
 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()
예제 #19
0
파일: __init__.py 프로젝트: Nexedi/neoppod
 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()
예제 #20
0
 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()
예제 #21
0
 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
예제 #22
0
파일: master.py 프로젝트: Nexedi/neoppod
    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)
예제 #23
0
 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]
예제 #24
0
 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)
예제 #25
0
파일: __init__.py 프로젝트: Nexedi/neoppod
 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)
예제 #26
0
 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
예제 #27
0
파일: pool.py 프로젝트: vpelletier/neoppod
 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)
예제 #28
0
파일: app.py 프로젝트: vpelletier/neoppod
    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
예제 #29
0
파일: app.py 프로젝트: vpelletier/neoppod
    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')
예제 #30
0
파일: app.py 프로젝트: Nexedi/neoppod
    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)
예제 #31
0
파일: storage.py 프로젝트: pyzh/neoppod
 def answerTIDsFrom(self, conn, tid_list):
     logging.debug('Get %u TIDs from %r', len(tid_list), conn)
     self.app.setHandlerData(tid_list)
예제 #32
0
    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()
예제 #33
0
 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")
예제 #34
0
파일: checker.py 프로젝트: pyzh/neoppod
    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()
예제 #35
0
파일: app.py 프로젝트: pyzh/neoppod
 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)