def test_regr1(self): # test that if we send a Learner different accept messages for the same # proposal, it only accepts a majority vote n = self.node n.quorum_size = 3 i = n.create_instance(1) i['status'] = "polling" n.recv_acceptnotify( Msg({ 'prop_num': (1, ''), 'prop_value': 1, 'uid': 1 }), i) n.recv_acceptnotify( Msg({ 'prop_num': (1, ''), 'prop_value': 1, 'uid': 2 }), i) n.recv_acceptnotify( Msg({ 'prop_num': (2, ''), 'prop_value': 2, 'uid': 3 }), i) self.assertEqual(i['status'], "polling") n.recv_acceptnotify( Msg({ 'prop_num': (1, ''), 'prop_value': 1, 'uid': 4 }), i) self.assertEqual(i['status'], "completed") self.assertEqual(i['value'], 1)
def recv_prepare(self, msg, instance): """Update instance state appropriately based upon msg. (b) If an acceptor receives a prepare request with number n greater than that of any prepare request to which it has already responded, then it responds to the request with a promise not to accept any more proposals numbered less than n and with the highest-numbered proposal (if any) that it has accepted. """ if msg.prop_num > instance['acceptor_prepare_prop_num']: self.writeMessage( msg['uid'], Msg({ 'msg_type': 'promise', 'prop_num': msg['prop_num'], 'prev_prop_num': instance['acceptor_cur_prop_num'], 'prev_prop_value': instance['acceptor_cur_prop_value'], 'instance_id': instance['instance_id'], })) instance['acceptor_prepare_prop_num'] = msg.prop_num else: if config.NACKS_ENABLED: d = { 'msg_type': 'nack_promise', 'prop_num': msg['prop_num'], 'instance_id': instance['instance_id'], } if config.NACKS_ENABLED == 2: d['prev_prop_num'] = instance['acceptor_cur_prop_num'] d['prev_prop_value'] = instance['acceptor_cur_prop_value'] self.writeMessage(msg['uid'], Msg(d))
def recv_acceptrequest(self, msg, instance): """Update instance state appropriately based upon msg. (b) If an acceptor receives an accept request for a proposal numbered n, it accepts the proposal unless it has already responded to a prepare request having a number greater than n. """ if msg['prop_num'] >= instance['acceptor_prepare_prop_num']: dbprint("Instance %d: accepting prop %s (current is %s)" % (instance['instance_id'], msg['prop_num'], instance['acceptor_cur_prop_num']), level=4) instance['acceptor_cur_prop_num'] = msg.prop_num instance['acceptor_cur_prop_value'] = msg.prop_value self.writeAll( Msg({ "msg_type": "acceptnotify", "prop_num": msg.prop_num, "prop_value": msg.prop_value, "instance_id": instance['instance_id'] })) else: if config.NACKS_ENABLED: d = { 'msg_type': 'nack_acceptrequest', 'prop_num': msg['prop_num'], 'instance_id': instance['instance_id'], } if config.NACKS_ENABLED == 2: d['prev_prop_num'] = instance['acceptor_cur_prop_num'] d['prev_prop_value'] = instance['acceptor_cur_prop_value'] self.writeMessage(msg['uid'], Msg(d))
def test_promise2(self): """Test that we ignore lower numbered proposals than one we've already accepted""" i = self.node.create_instance(1) self.node.recv_prepare(Msg({'prop_num': (2, ''), "uid": 1}), i) self.transport.clear() self.node.recv_prepare(Msg({'prop_num': (1, ''), "uid": 1}), i) self.assertEqual('', self.transport.value())
def test_prepare_timeout_responses(self): n = self.node i = n.create_instance(1) n.quorum_size = 2 n.uid = '' n.proposer_start(i, "foo") n.recv_promise( Msg({ 'prop_num': (1, ''), 'prev_prop_num': None, 'prev_prop_value': None, 'uid': 1 }), i) n.recv_promise( Msg({ 'prop_num': (1, ''), 'prev_prop_num': None, 'prev_prop_value': None, 'uid': 2 }), i) self.assertEqual(i['our_val'], "foo") self.assertEqual(i['status'], "polling") self.assertEqual(i['last_tried'], (1, ''))
def test_promise4(self): """Test that we respond with any proposal we've accepted""" i = self.node.create_instance(1) self.node.recv_prepare(Msg({'prop_num': (1, ''), "uid": 1}), i) res = Msg({ 'prev_prop_value': None, 'msg_type': 'promise', 'prop_num': (1, ''), 'prev_prop_num': 0 }) self.assertMsg(res, parse_message(self.transport.value())) self.transport.clear() self.node.recv_acceptrequest( Msg({ 'prop_num': (1, ''), 'prop_value': 2, "uid": 1 }), i) self.transport.clear() self.node.recv_prepare(Msg({'prop_num': (2, ''), "uid": 1}), i) res = Msg({ 'prev_prop_value': 2, 'msg_type': 'promise', 'prop_num': (2, ''), 'prev_prop_num': (1, '') }) self.assertMsg(res, parse_message(self.transport.value()))
def test_promise_onevalue(self): # promises - one value - send orig value n = self.node i = n.create_instance(1) i['status'] = "trying" i['last_tried'] = (2, '') n.quorum_size = 2 n.recv_promise( Msg({ 'prop_num': (2, ''), 'prev_prop_num': None, 'prev_prop_value': None, 'uid': 1 }), i) self.assertEqual('', self.transport.value()) n.recv_promise( Msg({ 'prop_num': (2, ''), 'prev_prop_num': 1, 'prev_prop_value': 1, 'uid': 2 }), i) self.assertMsg( Msg({ "msg_type": "acceptrequest", "prop_num": (2, ''), "prop_value": 1 }), parse_message(self.transport.msgs[0])) self.assertMsg( Msg({ "msg_type": "acceptrequest", "prop_num": (2, ''), "prop_value": 1 }), parse_message(self.transport.msgs[1]))
def test_regr2(self): # test that if we send a Learner a bunch of accept messages from the same # acceptor, it only accepts a majority vote n = self.node n.quorum_size = 3 i = n.create_instance(1) i['status'] = "polling" n.recv_acceptnotify( Msg({ 'prop_num': (1, ''), 'prop_value': 1, 'uid': 1 }), i) n.recv_acceptnotify( Msg({ 'prop_num': (1, ''), 'prop_value': 1, 'uid': 1 }), i) n.recv_acceptnotify( Msg({ 'prop_num': (2, ''), 'prop_value': 2, 'uid': 1 }), i) self.assertEqual(i['status'], "polling")
def test_receive_ehlo(self): self.node.recv_ehlo(Msg({"uid": 1}), None) self.assertMsg( Msg({ "msg_type": "notify", "hosts": self.node.hosts, "instance_id": None }), parse_message(self.transport.value()))
def test_promise(self): i = self.node.create_instance(1) self.node.recv_prepare(Msg({'prop_num': (1, ''), "uid": 1}), i) res = Msg({ 'prev_prop_value': None, 'msg_type': 'promise', 'prop_num': (1, ''), 'prev_prop_num': 0 }) self.assertMsg(res, parse_message(self.transport.value()))
def test_errors(self): self.assertRaises(InvalidMessageException, parse_message, Msg({ "msg_type": "foobar", "proposal": "1,2" })) self.assertRaises(InvalidMessageException, parse_message, Msg({ "msg_type": "promise", "proposal": "1" }))
def discoverNetwork(self): if config.STARTUP == "old": if self.bootstrap is not None: m = Msg({"msg_type": "ehlo", "uid": self.uid, "instance_id": None}) self.transport.write(m.serialize(), self.bootstrap) else: if self.bootstrap is not None: m = Msg({"msg_type": "hi", "uid": self.uid, "instance_id": None}) self.transport.write(m.serialize(), self.bootstrap) else: self.master = ("localhost", self.transport.getHost().port) self.hosts[self.uid] = self.master
def test_receive_notify(self): self.node.recv_notify( Msg({ 'msg_type': "notify", "uid": 1, "hosts": { "foo": "bar" } }), None) self.assertEqual(self.node.hosts["foo"], "bar") self.assertMsg(Msg({ "msg_type": "ehlo", "instance_id": None }), parse_message(self.transport.value()))
def test_autodiscover(self): self.node.bootstrap = object() self.node.discoverNetwork() self.assertMsg(Msg({ "msg_type": "ehlo", "instance_id": None }), parse_message(self.transport.value()))
def recv_ping(self, msg, instance): """Reply to a PING with a PONG (as a heartbeat)""" self.writeMessage(msg['uid'], Msg({ "msg_type": "pong", "instance_id": None }))
def send_acceptrequest(self, uid, prop_num, value, instance): d = { "msg_type": "acceptrequest", "prop_num": prop_num, "prop_value": value, "instance_id": instance['instance_id'] } self.writeMessage(uid, Msg(d))
def test_accept(self): """Test that on proposal acceptance the Node notifies all other nodes about its decision""" i = self.node.create_instance(1) self.node.recv_acceptrequest( Msg({ 'prop_num': (1, ''), 'prop_value': 2, "uid": 1 }), i) self.assertEqual((1, ''), i['acceptor_cur_prop_num']) for x in xrange(10): self.assertMsg( Msg({ "msg_type": "acceptnotify", "prop_num": (1, ''), "prop_value": 2 }), parse_message(self.transport.msgs[x]))
def write_who(self, uid): self.check_startup_style("new") self.writeMessage( uid, Msg({ "msg_type": "who", "hosts": self.hosts, "instance_id": None }))
def test_unknown_instance(self): self.node.datagramReceived( Msg({ 'msg_type': "prepare", 'uid': 1, 'instance_id': 500, 'prop_num': (1, ''), 'prop_value': None }).serialize(), None) self.assertTrue(self.node.instances[500])
def write_new(self, n_uid, t_uid): self.check_startup_style("new") self.writeMessage( t_uid, Msg({ "msg_type": "new", "new_uid": n_uid, "new_address": self.hosts[n_uid], "instance_id": None }))
def write_notify(self, uid): """Send a NOTIFY message with all the hosts we know about.""" self.check_startup_style("old") self.writeMessage( uid, Msg({ "msg_type": "notify", "hosts": self.hosts, "instance_id": None }))
def test_promise3(self): """Test that we accept new, higher numbered proposals than ones we've already accepted""" i = self.node.create_instance(1) self.node.recv_prepare(Msg({'prop_num': (1, ''), "uid": 1}), i) res = Msg({ 'prev_prop_value': None, 'msg_type': 'promise', 'prop_num': (1, ''), 'prev_prop_num': 0 }) self.assertMsg(res, parse_message(self.transport.value())) self.transport.clear() self.node.recv_prepare(Msg({'prop_num': (2, ''), "uid": 1}), i) res = Msg({ 'prev_prop_value': None, 'msg_type': 'promise', 'prop_num': (2, ''), 'prev_prop_num': 0 }) self.assertMsg(res, parse_message(self.transport.value()))
def test_run(self): n = self.node n.uid = 1 n.run("foo") for m in self.transport.msgs: self.assertMsg( Msg({ 'msg_type': "prepare", 'uid': 1, 'instance_id': 1, 'prop_num': (1, 1) }), parse_message(m))
def test_unknown_host(self): self.node.datagramReceived( Msg({ 'msg_type': "pong", 'uid': 100, 'instance_id': None }).serialize(), None) self.assertTrue(100 in self.node.hosts) self.assertEqual(self.node.hosts[100], None) self.node.datagramReceived( Msg({ 'msg_type': "pong", 'uid': 101, 'instance_id': None }).serialize(), None) self.node.datagramReceived( Msg({ 'msg_type': "pong", 'uid': 102, 'instance_id': None }).serialize(), None) self.assertEqual(self.node.quorum_size, 7)
def test_promise_multiple_values2(self): # promises - multiple values - send highest value # (same as test_promise_multiple_values but in reverse order) n = self.node i = n.create_instance(1) i['status'] = "trying" i['last_tried'] = (3, '') n.quorum_size = 2 n.recv_promise( Msg({ 'prop_num': (3, ''), 'prev_prop_num': (2, ''), 'prev_prop_value': 6, 'uid': 2 }), i) self.assertEqual('', self.transport.value()) n.recv_promise( Msg({ 'prop_num': (3, ''), 'prev_prop_num': (1, ''), 'prev_prop_value': 5, 'uid': 1 }), i) self.assertMsg( Msg({ "msg_type": "acceptrequest", "prop_num": (3, ''), "prop_value": 6 }), parse_message(self.transport.msgs[0])) self.assertMsg( Msg({ "msg_type": "acceptrequest", "prop_num": (3, ''), "prop_value": 6 }), parse_message(self.transport.msgs[1]))
def timeout_test(self, init=False): # If this isn't the first time we've run, do some pruning if not init: # Remove any hosts we haven't heard from yet for h in self.timeout_hosts: self.hosts.pop(h, None) # Start again self.timeout_hosts = dict(self.hosts) for h in self.hosts: self.writeMessage(h, Msg({ "msg_type": "ping", "instance_id": None })) self.reactor.callLater(self.timeout, self.timeout_test)
def proposer_start(self, instance, value, prop_num=1, restart=True): """Start an instance of Paxos! Try and complete an instance of Paxos, setting the decree to value. """ # (a) A proposer selects a proposal number n, greater than any proposal number it # has selected before, and sends a request containing n to a majority of # acceptors. This message is known as a prepare request. instance['our_val'] = value instance['status'] = "trying" p = (prop_num, self.uid) instance['last_tried'] = p instance['restart'] = restart self.writeAll( Msg({ "msg_type": "prepare", "prop_num": p, "instance_id": instance['instance_id'] })) self.reactor.callLater(self.proposer_timeout, self.handle_proposer_timeout, instance, "trying")
def discoverNetwork(self): if config.STARTUP == "old": if self.bootstrap is not None: m = Msg({ "msg_type": "ehlo", "uid": self.uid, "instance_id": None }) self.transport.write(m.serialize(), self.bootstrap) else: if self.bootstrap is not None: m = Msg({ "msg_type": "hi", "uid": self.uid, "instance_id": None }) self.transport.write(m.serialize(), self.bootstrap) else: self.master = ("localhost", self.transport.getHost().port) self.hosts[self.uid] = self.master
def test_timeout(self): self.clock.advance(self.node.timeout) self.node.recv_pong(Msg({"uid": 1}), None) self.node.recv_pong(Msg({"uid": 2}), None) self.clock.advance(self.node.timeout) self.assertEqual(self.node.hosts, {1: 1, 2: 2})
def test_receive_ping(self): self.node.recv_ping(Msg({"uid": 1}), None) self.assertMsg(Msg({"msg_type": "pong"}), parse_message(self.transport.value()))
def test_receive(self): """Test all agents can receive any message without crashing""" a = self.node a.datagramReceived( Msg({ 'msg_type': "prepare", 'uid': 1, 'instance_id': 1, 'prop_num': (1, ''), 'prop_value': None }).serialize(), None) #a.datagramReceived(Msg({'msg_type': "promise", 'uid': 1, 'prev_prop_value': None, 'prev_prop_num': 0, 'instance_id': 1, 'prop_num': (1, ''), 'prop_value': None}).serialize(), None) a.datagramReceived( Msg({ 'msg_type': "acceptnotify", 'uid': 1, 'instance_id': 1, 'prop_num': (1, ''), 'prop_value': None }).serialize(), None) a.datagramReceived( Msg({ 'msg_type': "acceptrequest", 'uid': 1, 'instance_id': 1, 'prop_num': (1, ''), 'prop_value': None }).serialize(), None) a.datagramReceived( Msg({ 'msg_type': "prepare", 'uid': 1, 'instance_id': 1, 'prop_num': (1, ''), 'prop_value': 2 }).serialize(), None) a.datagramReceived( Msg({ 'msg_type': "promise", 'uid': 1, 'instance_id': 1, 'prev_prop_value': None, 'prev_prop_num': 0, 'prop_num': (1, ''), 'prop_value': 2 }).serialize(), None) a.datagramReceived( Msg({ 'msg_type': "acceptnotify", 'uid': 1, 'instance_id': 1, 'prop_num': (1, ''), 'prop_value': 2 }).serialize(), None) a.datagramReceived( Msg({ 'msg_type': "acceptrequest", 'uid': 1, 'instance_id': 1, 'prop_num': (1, ''), 'prop_value': 2 }).serialize(), None)