本节我们来分析stale进程,stale进程也是一个pipeline,其作用在于处理联盟中有被分配节点由于某些原因没有处理bakclog
中的事务的问题,主要做法是对该事务的被分配节点进行重新分配
# bigchaindb/pipelines/stale.py
def create_pipeline(timeout=5, backlog_reassign_delay=5):
stm = StaleTransactionMonitor(timeout=timeout,
backlog_reassign_delay=backlog_reassign_delay)
monitor_pipeline = Pipeline([
Node(stm.check_transactions),
Node(stm.reassign_transactions)
])
return monitor_pipeline
def start(timeout=5, backlog_reassign_delay=None):
pipeline = create_pipeline(timeout=timeout,
backlog_reassign_delay=backlog_reassign_delay)
pipeline.start()
return pipeline
stale进程的pipeline如上,该pipeline中只有两个Node:check_transactions->reassign_transactions
check_transactions
每一个pipeline的Node调用了一个无限循环着的target程序,对于本Node来说,意味着check_transactions
每休眠self.timeout
后将会被再次调用。而在每一次调用时,将会从backlog
表中找到时间戳比当前时间减去分配时延还要小的记录,并将这些记录通过生成器抛出给下一个Node。这些记录的时间戳小于当前时间减去分配时延,意味着该记录本应该已经被分配,但是却并没有被分配节点所处理(因为还留在backlog表中),所以已经过时了,应当被重新分配节点来处理
# bigchaindb/pipelines/stale.py
def check_transactions(self):
sleep(self.timeout)
for tx in self.bigchain.get_stale_transactions():
yield tx
# bigchaindb/backend/mongodb/query.py
@register_query(MongoDBConnection)
def get_stale_transactions(conn, reassign_delay):
return conn.run(
conn.collection('backlog')
.find({'assignment_timestamp': {'$lt': time() - reassign_delay}},
projection={'_id': False}))
reassign_transactions
事务传输到本Node,代表着事务没有被之前的分配节点来处理,所以应该重新分配。重新分配的流程为:找到联盟中除之前分配节点外的其他节点,从中随机抽取一个节点作为新的被分配节点,更新backlog
表中事务的assignee
与assignment_timestamp
项
# bigchaindb/pipelines/stale.py
def reassign_transactions(self, tx):
logger.info('Reassigning transaction with id %s', tx['id'])
self.bigchain.reassign_transaction(tx)
return tx
# bigchaindb/core.py
def reassign_transaction(self, transaction):
other_nodes = tuple(
self.federation.difference([transaction['assignee']])
)
new_assignee = random.choice(other_nodes) if other_nodes else self.me
return backend.query.update_transaction(
self.connection, transaction['id'],
{'assignee': new_assignee, 'assignment_timestamp': time()})
# bigchaindb/backend/mongodb/query.py
@register_query(MongoDBConnection)
def update_transaction(conn, transaction_id, doc):
# with mongodb we need to add update operators to the doc
doc = {'$set': doc}
return conn.run(
conn.collection('backlog')
.find_one_and_update(
{'id': transaction_id},
doc,
return_document=ReturnDocument.AFTER))