Align routing to specification #105
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This update provides a more solid routing implementation and a clear separation of routing code into a distinct module. Coupled with full test coverage over this module, this provides a thorough source for updates to the internal routing specification document.
Two files here should be of particular interest to the reviewer:
routing.py
andtest_routing.py
. These provide the routing implementation and tests. A summary of the main classes follows:routing.py
A module containing three classes that together implement the routing mechanism used by
RoutingDriver
(itself still located insession.py
). These are:RoundRobinSet
- A generic collection that essentially consists of an ordered set paired with a perpetually-looping iterator. This means that elements maintain their original insertion order and duplicates are not permitted. The iterator allows these elements to be selected in turn (usingnext
) and will restart at the top after completing each cycle. This collection class is used to store lists of routers, readers and writers within aRoutingTable
.RoutingTable
- A store for routing information, constructed from the output of thegetServers
procedure. This holds three collections of servers: routers, readers and writers, as well as alast_updated_time
and attl
. Storing these last two values separately, instead of as a combinedexpiry_time
, gives extra detail on the freshness of the information and makes testing simpler.RoutingConnectionPool
- An extension of the plainConnectionPool
class that also holds aRoutingTable
. This is used by theRoutingDriver
instead of the simpler version of the class and is described in more detail in the section below.RoutingConnectionPool
The main class for coördination of all routing functionality. Method descriptions follow:
fetch_routing_info(address)
- Fetch the raw routing info from a given address. The main responsibility of this method is to connect to a router and obtain a raw set of routing records; their correctness is not checked. This method also triggers removal of routers that are unavailable for connection. There are three possible outcomes: a list of routing records,None
if the server cannot be contacted orServiceUnavailable
if the server exists but does not provide a valid routing service.fetch_routing_table(address)
- Wrapper forfetch_routing_info
that parses the received routing records, checks them for correctness and builds and returns a newRoutingTable
instance. If thefetch_routing_info
method returnsNone
, so does this method. If the records cannot be parsed, aProtocolError
is raised. If there is only one router and no writers, aServiceUnavailable
is raised.update_routing_table()
- This method steps through all known routers, attempting to fetch a routing table from each. The first to respond correctly will immediately be used to update the main routing table adn the method will exit. If no routing table can be obtained after querying all servers,ServiceUnavailable
is raised.refresh_routing_table()
- Conditionally callupdate_routing_table
if the current table is stale. This method checks freshness twice: one immediately and once after obtaining therefresh_lock
. This ensures that concurrent refreshes do not interfere with each other and no unnecessary parallel calls are made.acquire_for_read
- Performs a refresh followed by acquisition of a connection to one of the addresses in the readers table.acquire_for_write
- Performs a refresh followed by acquisition of a connection to one of the addresses in the writers table.remove
- Remove an adddress from the routing table and from the connection pool itself (viaConnectionPool.remove
).