Details
-
New Feature
-
Status: Open (View Workflow)
-
Major
-
Resolution: Unresolved
-
None
-
None
-
None
Description
In cases where some of the servers are local and some are remote, readwritesplit will end up routing traffic to all nodes, even with slave_selection_criteria=adaptive_routing, if the concurrency is high enough. This happens because the active operation count is mixed into the query latency value which inflates the latency of the local nodes so much that a part of the traffic ends up spilling to remote nodes even if the local node would be able to handle it.
A strict latency based check that doesn't mix in the active operations into the latency value would be optimal. Amongst servers that cluster together, the normal active operation based approach could be used to select the best candidate in the cluster.
In addition to latency, some additional modes could be added like random, which picks a random server, and roundrobin which rotates reads across all servers in a predictable manner, either for each connection or globally for the router.
Here is an example simulation of how the routing currently works when the concurrency increases.
The I column shows the number of concurrent queries and the three values inside the array are the number of active operations on each of the three simulated servers.
Algorithm: least_current_operations, slot with lowest value for active operations is selected
I: 1 [1 , 0 , 0 ]
|
I: 2 [1 , 1 , 0 ]
|
I: 3 [1 , 1 , 1 ]
|
I: 4 [2 , 1 , 1 ]
|
I: 5 [2 , 2 , 1 ]
|
I: 6 [2 , 2 , 2 ]
|
I: 7 [3 , 2 , 2 ]
|
I: 8 [3 , 3 , 2 ]
|
I: 9 [3 , 3 , 3 ]
|
I: 10 [4 , 3 , 3 ]
|
I: 11 [4 , 4 , 3 ]
|
I: 12 [4 , 4 , 4 ]
|
I: 13 [5 , 4 , 4 ]
|
I: 14 [5 , 5 , 4 ]
|
I: 15 [5 , 5 , 5 ]
|
I: 16 [6 , 5 , 5 ]
|
I: 17 [6 , 6 , 5 ]
|
I: 18 [6 , 6 , 6 ]
|
I: 19 [7 , 6 , 6 ]
|
I: 20 [7 , 7 , 6 ]
|
I: 21 [7 , 7 , 7 ]
|
I: 22 [8 , 7 , 7 ]
|
I: 23 [8 , 8 , 7 ]
|
I: 24 [8 , 8 , 8 ]
|
I: 25 [9 , 8 , 8 ]
|
I: 26 [9 , 9 , 8 ]
|
I: 27 [9 , 9 , 9 ]
|
I: 28 [10, 9 , 9 ]
|
I: 29 [10, 10, 9 ]
|
I: 30 [10, 10, 10]
|
Algorithm: adaptive_routing, slot with lowest value for latency * (active operations + 1) is selected
Latencies: [0.1, 0.2, 0.4]
|
I: 1 [1 , 0 , 0 ]
|
I: 2 [2 , 0 , 0 ]
|
I: 3 [2 , 1 , 0 ]
|
I: 4 [3 , 1 , 0 ]
|
I: 5 [4 , 1 , 0 ]
|
I: 6 [4 , 2 , 0 ]
|
I: 7 [4 , 2 , 1 ]
|
I: 8 [5 , 2 , 1 ]
|
I: 9 [6 , 2 , 1 ]
|
I: 10 [6 , 3 , 1 ]
|
I: 11 [7 , 3 , 1 ]
|
I: 12 [8 , 3 , 1 ]
|
I: 13 [8 , 4 , 1 ]
|
I: 14 [8 , 4 , 2 ]
|
I: 15 [9 , 4 , 2 ]
|
I: 16 [10, 4 , 2 ]
|
I: 17 [10, 5 , 2 ]
|
I: 18 [11, 5 , 2 ]
|
I: 19 [12, 5 , 2 ]
|
I: 20 [12, 6 , 2 ]
|
I: 21 [12, 6 , 3 ]
|
I: 22 [13, 6 , 3 ]
|
I: 23 [14, 6 , 3 ]
|
I: 24 [14, 7 , 3 ]
|
I: 25 [15, 7 , 3 ]
|
I: 26 [16, 7 , 3 ]
|
I: 27 [16, 8 , 3 ]
|
I: 28 [16, 8 , 4 ]
|
I: 29 [17, 8 , 4 ]
|
I: 30 [18, 8 , 4 ]
|
Algorithm: latency, slot with lowest value for latency is selected
Latencies: [0.1, 0.2, 0.4]
|
I: 1 [1 , 0 , 0 ]
|
I: 2 [2 , 0 , 0 ]
|
I: 3 [3 , 0 , 0 ]
|
I: 4 [4 , 0 , 0 ]
|
I: 5 [5 , 0 , 0 ]
|
I: 6 [6 , 0 , 0 ]
|
I: 7 [7 , 0 , 0 ]
|
I: 8 [8 , 0 , 0 ]
|
I: 9 [9 , 0 , 0 ]
|
I: 10 [10, 0 , 0 ]
|
I: 11 [11, 0 , 0 ]
|
I: 12 [12, 0 , 0 ]
|
I: 13 [13, 0 , 0 ]
|
I: 14 [14, 0 , 0 ]
|
I: 15 [15, 0 , 0 ]
|
I: 16 [16, 0 , 0 ]
|
I: 17 [17, 0 , 0 ]
|
I: 18 [18, 0 , 0 ]
|
I: 19 [19, 0 , 0 ]
|
I: 20 [20, 0 , 0 ]
|
I: 21 [21, 0 , 0 ]
|
I: 22 [22, 0 , 0 ]
|
I: 23 [23, 0 , 0 ]
|
I: 24 [24, 0 , 0 ]
|
I: 25 [25, 0 , 0 ]
|
I: 26 [26, 0 , 0 ]
|
I: 27 [27, 0 , 0 ]
|
I: 28 [28, 0 , 0 ]
|
I: 29 [29, 0 , 0 ]
|
I: 30 [30, 0 , 0 ]
|
This demonstrates the early offloading of adaptive_routing to servers that are further away which works against it in cases where the local server would be able to server much higher concurrency numbers. In order to allow optimizing for only latency and not throughput, the latency mode would be needed.