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)}