class Learner (essential.Learner):
'''
This class extends the base in track which peers have accepted the final value.
on_resolution() is still called only once. At the time of the call, the
final_acceptors member variable will contain exactly quorum_size uids. Subsequent
calls to recv_accepted will add the uid of the sender if the accepted_value
matches the final_value.
'''
final_acceptors = None
def recv_accepted(self, from_uid, proposal_id, accepted_value):
'''
Called when an Accepted message is received from an acceptor
'''
if self.final_value is not None:
if accepted_value == self.final_value:
self.final_acceptors.add( from_uid )
return # already done
if self.proposals is None:
self.proposals = dict()
self.acceptors = dict()
last_pn = self.acceptors.get(from_uid)
if not proposal_id > last_pn:
return # Old message
self.acceptors[ from_uid ] = proposal_id
if last_pn is not None:
oldp = self.proposals[ last_pn ]
oldp[1].remove( from_uid )
if len(oldp[1]) == 0:
del self.proposals[ last_pn ]
if not proposal_id in self.proposals:
self.proposals[ proposal_id ] = [set(), set(), accepted_value]
t = self.proposals[ proposal_id ]
assert accepted_value == t[2], 'Value mismatch for single proposal!'
t[0].add( from_uid )
t[1].add( from_uid )
if len(t[0]) == self.quorum_size:
self.final_value = accepted_value
self.final_proposal_id = proposal_id
self.final_acceptors = t[0]
self.proposals = None
self.acceptors = None
self.messenger.on_resolution( proposal_id, accepted_value )
class Node (Proposer, Acceptor, Learner):
'''
This class supports the common model where each node on a network preforms
all three Paxos roles, Proposer, Acceptor, and Learner.
'''
def __init__(self, messenger, node_uid, quorum_size):
self.messenger = messenger
self.node_uid = node_uid
self.quorum_size = quorum_size
@property
def proposer_uid(self):
return self.node_uid
def change_quorum_size(self, quorum_size):
self.quorum_size = quorum_size
def recv_prepare(self, from_uid, proposal_id):
self.observe_proposal( from_uid, proposal_id )
return super(Node,self).recv_prepare( from_uid, proposal_id )