def test_should_return_routing_table_on_valid_record_with_extra_role(self):
     table = RoutingTable.parse_routing_info(VALID_ROUTING_RECORD_WITH_EXTRA_ROLE["servers"],
                                             VALID_ROUTING_RECORD_WITH_EXTRA_ROLE["ttl"])
     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
Beispiel #2
0
 async def get_routing_table(self, context=None):
     try:
         result = await self.run(
             "CALL dbms.cluster.routing.getRoutingTable($context)",
             {"context": dict(context or {})})
         record = await result.single()
         if not record:
             raise BoltRoutingError(
                 "Routing table call returned "
                 "no data", self.remote_address)
         assert isinstance(record, Record)
         servers = record["servers"]
         ttl = record["ttl"]
         log.debug("[#%04X] S: <ROUTING> servers=%r ttl=%r",
                   self.local_address.port_number, servers, ttl)
         return RoutingTable.parse_routing_info(servers, ttl)
     except BoltFailure as error:
         if error.title == "ProcedureNotFound":
             raise BoltRoutingError("Server does not support "
                                    "routing",
                                    self.remote_address) from error
         else:
             raise
     except ValueError as error:
         raise BoltRoutingError("Invalid routing table",
                                self.remote_address) from error
Beispiel #3
0
 def test_should_return_all_distinct_servers_in_routing_table(self):
     routing_table = {
         "ttl":
         300,
         "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:9001", "127.0.0.1:9005"]
             },
             {
                 "role": "WRITE",
                 "addresses": ["127.0.0.1:9002"]
             },
         ],
     }
     table = RoutingTable.parse_routing_info(
         database=DEFAULT_DATABASE,
         servers=routing_table["servers"],
         ttl=routing_table["ttl"],
     )
     assert table.servers() == {('127.0.0.1', 9001), ('127.0.0.1', 9002),
                                ('127.0.0.1', 9003), ('127.0.0.1', 9005)}
Beispiel #4
0
 def test_should_be_fresh_after_update(self):
     table = RoutingTable.parse_routing_info(
         database=DEFAULT_DATABASE,
         servers=VALID_ROUTING_RECORD["servers"],
         ttl=VALID_ROUTING_RECORD["ttl"],
     )
     assert table.is_fresh(readonly=True)
     assert table.is_fresh(readonly=False)
Beispiel #5
0
 def test_should_become_stale_if_no_writers(self):
     table = RoutingTable.parse_routing_info(
         database=DEFAULT_DATABASE,
         servers=VALID_ROUTING_RECORD["servers"],
         ttl=VALID_ROUTING_RECORD["ttl"],
     )
     table.writers.clear()
     assert table.is_fresh(readonly=True)
     assert not table.is_fresh(readonly=False)
Beispiel #6
0
 def test_should_return_routing_table_on_valid_record(self):
     table = RoutingTable.parse_routing_info(
         database=DEFAULT_DATABASE,
         servers=VALID_ROUTING_RECORD["servers"],
         ttl=VALID_ROUTING_RECORD["ttl"],
     )
     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
Beispiel #7
0
    def fetch_routing_table(self, *, address, timeout, database):
        """ Fetch a routing table from a given router address.

        :param address: router address
        :param timeout: seconds
        :param database: the database name
        :type: str

        :return: a new RoutingTable instance or None if the given router is
                 currently unable to provide routing information

        :raise neo4j.exceptions.ServiceUnavailable: if no writers are available
        :raise neo4j._exceptions.BoltProtocolError: if the routing information received is unusable
        """
        new_routing_info = self.fetch_routing_info(address=address,
                                                   timeout=timeout,
                                                   database=database)
        if new_routing_info is None:
            return None
        elif not new_routing_info:
            raise BoltRoutingError("Invalid routing table", address)
        else:
            servers = new_routing_info[0]["servers"]
            ttl = new_routing_info[0]["ttl"]
            new_routing_table = RoutingTable.parse_routing_info(
                database=database, servers=servers, ttl=ttl)

        # Parse routing info and count the number of each type of server
        num_routers = len(new_routing_table.routers)
        num_readers = len(new_routing_table.readers)

        # num_writers = len(new_routing_table.writers)
        # If no writers are available. This likely indicates a temporary state,
        # such as leader switching, so we should not signal an error.

        # No routers
        if num_routers == 0:
            raise BoltRoutingError("No routing servers returned from server",
                                   address)

        # No readers
        if num_readers == 0:
            raise BoltRoutingError("No read servers returned from server",
                                   address)

        # At least one of each is fine, so return this table
        return new_routing_table
Beispiel #8
0
    def fetch_routing_table(self, address):
        """ Fetch a routing table from a given router address.

        :param address: router address
        :return: a new RoutingTable instance or None if the given router is
                 currently unable to provide routing information
        :raise ServiceUnavailable: if no writers are available
        :raise ProtocolError: if the routing information received is unusable
        """
        new_routing_info = self.fetch_routing_info(address)
        if new_routing_info is None:
            return None
        elif not new_routing_info:
            raise BoltRoutingError("Invalid routing table", address)
        else:
            servers = new_routing_info[0]["servers"]
            ttl = new_routing_info[0]["ttl"]
            new_routing_table = RoutingTable.parse_routing_info(servers, ttl)

        # Parse routing info and count the number of each type of server
        num_routers = len(new_routing_table.routers)
        num_readers = len(new_routing_table.readers)
        num_writers = len(new_routing_table.writers)

        # No writers are available. This likely indicates a temporary state,
        # such as leader switching, so we should not signal an error.
        # When no writers available, then we flag we are reading in absence of writer
        self.missing_writer = (num_writers == 0)

        # No routers
        if num_routers == 0:
            raise BoltRoutingError("No routing servers returned from server",
                                   address)

        # No readers
        if num_readers == 0:
            raise BoltRoutingError("No read servers returned from server",
                                   address)

        # At least one of each is fine, so return this table
        return new_routing_table
 def test_should_become_stale_if_no_writers(self):
     table = RoutingTable.parse_routing_info(VALID_ROUTING_RECORD["servers"],
                                             VALID_ROUTING_RECORD["ttl"])
     table.writers.clear()
     assert table.is_fresh(readonly=True)
     assert not table.is_fresh(readonly=False)
 def test_should_become_stale_on_expiry(self):
     table = RoutingTable.parse_routing_info(VALID_ROUTING_RECORD["servers"],
                                             VALID_ROUTING_RECORD["ttl"])
     table.ttl = 0
     assert not table.is_fresh(readonly=True)
     assert not table.is_fresh(readonly=False)
 def test_should_be_fresh_after_update(self):
     table = RoutingTable.parse_routing_info(VALID_ROUTING_RECORD["servers"],
                                             VALID_ROUTING_RECORD["ttl"])
     assert table.is_fresh(readonly=True)
     assert table.is_fresh(readonly=False)