def _test_server_outcome(self, server_outcomes, overall_outcome): print("%r -> %r" % (server_outcomes, overall_outcome)) servers = {} routers = [] for port, outcome in enumerate(server_outcomes, 9001): if outcome is None: servers[port] = "router_no_writers.script" elif outcome is RoutingTable: servers[port] = "router.script" elif outcome is ServiceUnavailable: servers[port] = "non_router.script" else: assert False, "Unexpected server outcome %r" % outcome routers.append(("127.0.0.1", port)) with StubCluster(servers): with RoutingConnectionPool(connector, *routers) as pool: if overall_outcome is RoutingTable: pool.update_routing_table() table = pool.routing_table assert table.routers == {("127.0.0.1", 9001), ("127.0.0.1", 9002), ("127.0.0.1", 9003)} assert table.readers == {("127.0.0.1", 9004), ("127.0.0.1", 9005)} assert table.writers == {("127.0.0.1", 9006)} assert table.ttl == 300 elif overall_outcome is ServiceUnavailable: with self.assertRaises(ServiceUnavailable): pool.update_routing_table() else: assert False, "Unexpected overall outcome %r" % overall_outcome
def test_should_refresh(self): with StubCluster({9001: "router.script", 9006: "empty.script"}): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector, address) as pool: assert not pool.routing_table.is_fresh() _ = pool.acquire_for_write() assert pool.routing_table.is_fresh()
def test_should_not_fail_if_cannot_connect_but_router_already_removed( self): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector) as pool: assert address not in pool.routing_table.routers _ = pool.fetch_routing_info(address) assert address not in pool.routing_table.routers
def test_should_remove_router_if_connection_drops(self): with StubCluster({9001: "rude_router.script"}): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector, address) as pool: assert address in pool.routing_table.routers _ = pool.fetch_routing_info(address) assert address not in pool.routing_table.routers
def test_connected_to_writer(self): with StubCluster({9001: "router.script", 9006: "empty.script"}): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector, address) as pool: assert not pool.routing_table.is_fresh() connection = pool.acquire_for_write() assert connection.server.address in pool.routing_table.writers
def test_should_not_fail_if_absent(self): with StubCluster({9001: "router.script"}): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector, address) as pool: pool.refresh_routing_table() target = ("127.0.0.1", 9007) pool.remove(target)
def test_should_not_update_if_fresh(self): with StubCluster({9001: "router.script"}): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector, address) as pool: pool.refresh_routing_table() first_updated_time = pool.routing_table.last_updated_time pool.refresh_routing_table() second_updated_time = pool.routing_table.last_updated_time assert second_updated_time == first_updated_time
def test_should_remove_writer_from_routing_table_if_present(self): with StubCluster({9001: "router.script"}): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector, address) as pool: pool.refresh_routing_table() target = ("127.0.0.1", 9006) assert target in pool.routing_table.writers pool.remove(target) assert target not in pool.routing_table.writers
def test_should_get_table_from_router(self): with StubCluster({9001: "router.script"}): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector) as pool: table = pool.fetch_routing_table(address) assert table.routers == {("127.0.0.1", 9001), ("127.0.0.1", 9002), ("127.0.0.1", 9003)} assert table.readers == {("127.0.0.1", 9004), ("127.0.0.1", 9005)} assert table.writers == {("127.0.0.1", 9006)} assert table.ttl == 300
def test_should_retry_if_first_writer_fails(self): with StubCluster({ 9001: "router_with_multiple_writers.script", 9006: "fail_on_init.script", 9007: "empty.script" }): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector, address) as pool: assert not pool.routing_table.is_fresh() _ = pool.acquire_for_write() assert ("127.0.0.1", 9006) not in pool.routing_table.writers assert ("127.0.0.1", 9007) in pool.routing_table.writers
def test_should_retry_if_first_reader_fails(self): with StubCluster({ 9001: "router.script", 9004: "fail_on_init.script", 9005: "empty.script" }): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector, address) as pool: assert not pool.routing_table.is_fresh() _ = pool.acquire_for_read() assert ("127.0.0.1", 9004) not in pool.routing_table.readers assert ("127.0.0.1", 9005) in pool.routing_table.readers
def test_concurrent_refreshes_should_not_block_if_fresh(self): address = ("127.0.0.1", 9001) table = RoutingTable.parse_routing_info([VALID_ROUTING_RECORD]) with RoutingConnectionPool(connector, address) as pool: semaphore = Semaphore() class Refresher(Thread): refreshed = None def run(self): self.refreshed = pool.refresh_routing_table() class BlockingRefresher(Refresher): @classmethod def blocking_update(cls): pool.routing_table.update(table) semaphore.acquire() semaphore.release() return table def run(self): with patch.object(RoutingConnectionPool, "update_routing_table", side_effect=self.blocking_update): super(BlockingRefresher, self).run() first = BlockingRefresher() second = Refresher() assert not pool.routing_table.is_fresh() semaphore.acquire() first.start() second.start() sleep(1) assert not second.is_alive( ) # second call should return immediately without blocking second.join() semaphore.release() first.join() assert first.refreshed assert not second.refreshed assert pool.routing_table.is_fresh()
def test_should_get_info_from_router(self): with StubCluster({9001: "router.script"}): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector) as pool: result = pool.fetch_routing_info(address) assert len(result) == 1 record = result[0] assert record["ttl"] == 300 assert record["servers"] == [ { "role": "ROUTE", "addresses": ["127.0.0.1:9001", "127.0.0.1:9002", "127.0.0.1:9003"] }, { "role": "READ", "addresses": ["127.0.0.1:9004", "127.0.0.1:9005"] }, { "role": "WRITE", "addresses": ["127.0.0.1:9006"] }, ]
def test_no_readers_should_raise_protocol_error(self): with StubCluster({9001: "router_no_readers.script"}): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector) as pool: with self.assertRaises(ProtocolError): _ = pool.fetch_routing_table(address)
def test_should_remove_router_if_cannot_connect(self): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector, address) as pool: assert address in pool.routing_table.routers _ = pool.fetch_routing_info(address) assert address not in pool.routing_table.routers
def test_should_return_none_if_cannot_connect(self): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector, address) as pool: result = pool.fetch_routing_info(address) assert result is None
def test_should_return_none_if_connection_drops(self): with StubCluster({9001: "rude_router.script"}): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector, address) as pool: result = pool.fetch_routing_info(address) assert result is None
def test_should_fail_if_database_error(self): with StubCluster({9001: "broken_router.script"}): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector, address) as pool: with self.assertRaises(ServiceUnavailable): _ = pool.fetch_routing_info(address)
def test_update_with_no_routers_should_signal_service_unavailable(self): with RoutingConnectionPool(connector) as pool: with self.assertRaises(ServiceUnavailable): pool.update_routing_table()
def test_null_info_should_return_null_table(self): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector) as pool: table = pool.fetch_routing_table(address) assert table is None
def test_no_writers_should_return_null_table(self): with StubCluster({9001: "router_no_writers.script"}): address = ("127.0.0.1", 9001) with RoutingConnectionPool(connector) as pool: table = pool.fetch_routing_table(address) assert table is None
def test_should_populate_initial_router(self): with RoutingConnectionPool(connector, ("127.0.0.1", 9001)) as pool: assert pool.routing_table.routers == {("127.0.0.1", 9001)}