Loadbalancer: Refactor socket/factory Map into Lists + bugfix.#112
Loadbalancer: Refactor socket/factory Map into Lists + bugfix.#112NiteshKant merged 6 commits intomasterfrom
Conversation
**Problem** The loadbalancer uses internally 2 maps, one for the list of `ReactiveSocketFactory` it can use for creating a new `ReactiveSocket`, and one for the active `ReactiveSocket`. The link is made between the 2 maps through the `SocketAddress` of the associated remote server. This is kind of clunky and requires explicit knowledge of the `SocketAddress` (result type of `remote()`). Also, when we select a new factory to connect to or when we select the slowest ReactiveSocket to quick out, the sorting is made via the `sorted()` method on the java stream. The comparison function is actually unstable because a tcp socket can be closed while we do the sorting. **Solution** Use two queues, one for factories and one for ReactiveSocket. Selecting a factory is made using the "Power of 2 Choices" technique, and we also "age" the unselected factories by moving them at the end of the queue. The WeightedSocket now contains a reference to the associated factory, and closing it add back the factory to the factory queue. **Modification** Move the `WeightedSocket` class inside the `LoadBalancer`, no I created a proper constructor to `LoadBalancer` with javadoc explaining all parameters the algorithm requires. The `LatencySubscriber` inside the LoadBalancer treats some exceptions particularly: - `TimeoutException` latency is used for predicating the next latency - `TransportException` & `ClosedChannelException` removes the ReactiveSocket from the active list. `Publishers.onError` now also `cancel` the subscription. I created a StressTest file, which stress most of the part of the Client: - Adding/Removing new servers - High request concurrency - Dealing with failing/black-hole servers
|
Apologies for some basic questions, I'm getting up to speed on the code, but will ask anyway.
|
|
No worries about your basic questions.
|
| } | ||
|
|
||
| List<ReactiveSocketFactory<SocketAddress>> factories = | ||
| List<ReactiveSocketFactory<?>> factories = |
There was a problem hiding this comment.
This isn't related, but do we need a type argument for ReactiveSocketFactory?
There was a problem hiding this comment.
The type argument represents the identifier of a server, which is, in our implementation, always SocketAddress.
I can refactor the code to remove it (later).
- Add type parameter to loadBalancer (matching ReactiveSocketFactory) - Move from a queue implementation to a list - Refactor the queue/aging into proper "Power of Two Choices" Fix Bug: Computing the server list delta (added/removed) was wrong.
|
Here's a summary of the last commit: Address comments:
Fix Bug:
|
| int n = numberOfNewSocket; | ||
| if (n > activeFactories.size()) { | ||
| n = activeFactories.size(); | ||
| logger.info("addSockets(" + numberOfNewSocket |
There was a problem hiding this comment.
nit: May be we can use templated messages here?
logger.info("addSockets({}) restricted", numberOfNewSocket)
Problem
The loadbalancer uses internally 2 maps, one for the list of
ReactiveSocketFactoryit can use for creating a new
ReactiveSocket, and one for the activeReactiveSocket.The link is made between the 2 maps through the
SocketAddressof the associatedremote server. This is kind of clunky and requires explicit knowledge of
the
SocketAddress(result type ofremote()).Also, when we select a new factory to connect to or when we select the slowest
ReactiveSocket to quick out, the sorting is made via the
sorted()methodon the java stream. The comparison function is actually unstable because a
tcp socket can be closed while we do the sorting.
Solution
Use two lists, one for factories and one for ReactiveSocket. Selecting a factory
is made using the "Power of 2 Choices" technique. The WeightedSocket now contains
a reference to the associated factory, and closing it add back the factory to the
factory list.
Modification
Move the
WeightedSocketclass inside theLoadBalancer, noI created a proper constructor to
LoadBalancerwith javadoc explaining allparameters the algorithm requires.
The
LatencySubscriberinside the LoadBalancer treats some exceptionsparticularly:
TimeoutExceptionlatency is used for predicating the next latencyTransportException&ClosedChannelExceptionremoves the ReactiveSocketfrom the active list.
Publishers.onErrornow alsocancelthe subscription.I created a StressTest file, which stresses most of the part of the Client: