-
Notifications
You must be signed in to change notification settings - Fork 97
[doc] Add page about latencies #1561
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,3 +2,4 @@ flatten_single_child_sections: true | |
| nav: | ||
| - "Introduction": index.md | ||
| - "Sizing": sizing.md | ||
| - "Latency": latency.md | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,293 @@ | ||||||
| # Latency | ||||||
|
|
||||||
| The DSS keeps its data across all instances of a DSS pool using a single | ||||||
| distributed database cluster (e.g. CockroachDB, Yugabyte). | ||||||
|
|
||||||
| Because the cluster is distributed and strongly consistent, the physical | ||||||
| distance between nodes directly affects how fast the DSS can answer. Latency is | ||||||
| therefore not a detail: it drives the responsiveness and the throughput of the | ||||||
| whole service. | ||||||
|
|
||||||
| ## Impact on performance | ||||||
|
|
||||||
| ### Time to answer a request | ||||||
|
|
||||||
| When a call is made to the DSS, the core service queries the database. The | ||||||
| database is not a single node: it is a cluster spread across DSS participants. | ||||||
| To stay strongly consistent, a write must reach a quorum of nodes before it is | ||||||
| acknowledged, and a read may also need to contact other nodes depending on where | ||||||
| the data lives. | ||||||
|
|
||||||
| Each of these internal hops adds to the round-trip latency between nodes. A single | ||||||
| API call can trigger more than one database operation, so the latencies stack | ||||||
| up. If the nodes are far apart, every consensus round pays that distance, and | ||||||
| the time to answer grows accordingly. | ||||||
|
|
||||||
|  | ||||||
| /// caption | ||||||
| A generic request. Each | ||||||
| arrow adds cumulative latency | ||||||
| /// | ||||||
|
|
||||||
| ### Bandwidth | ||||||
|
|
||||||
| Latency and bandwidth interact. With high latency, each operation (assuming no parallelism) takes longer | ||||||
| to complete, so fewer operations finish per second, which lowers effective | ||||||
| throughput even when raw bandwidth is available. Synchronization traffic between | ||||||
| nodes also competes for that bandwidth. | ||||||
|
|
||||||
| This matters for high-volume cases such as many subscriptions or large query | ||||||
| results: the more data must be transferred and synchronized between distant | ||||||
| nodes, the more latency limits how much can realistically be moved in a given | ||||||
| time window. There is a practical ceiling on how much can be transferred before | ||||||
|
Comment on lines
+40
to
+42
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question here as above: why does latency limit how much (how much what?) can be realistically moved in a given time window? For instance, geostationary satellites (not Starlink) have massive latency, but also pretty massive bandwidth -- they can move a lot of data in a given time window even though they have very poor latency. As written, it seems like this paragraph of explanation would also apply to Raftstore -- it is subject to the same latency as CRDB when deployed into the same substrate, yet Raftstore achieves much higher bandwidth. So, it seems like the dominant factor in achievable bandwidth isn't latency.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Dues to the Bandwidth-delay product quoted bellow. That not the same setup as starlink, we're using single machine, with tcp, somewhat limited buffers, etc. And that also to be seeing in a "fixed settings", even with higher parallelism, you will hit thoses kind of limits.
That not something we measured, we measured q/s on the client side, not actual bandwidth they achieve on their internal links. |
||||||
| responsiveness degrades. | ||||||
|
|
||||||
|  | ||||||
| /// caption | ||||||
| **Simulated** throughput vs | ||||||
| latency on a virtual link of a generic TCP connexion captured with [iperf3](https://software.es.net/iperf/), practical throughput is even lower. | ||||||
| /// | ||||||
|
Comment on lines
+45
to
+49
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a graph of the BD product using the 65k vanilla TCP limit? If so, is this limit actually relevant to our use case? I.e., is 100 Mbps insufficient to achieve order of 100 QPS? If not, what is the cause of the falloff?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the BD: Yes, that basically the point of this section. We should probably link the page. It's relevant, because depending on data moved, you will start to hit those limits, especially with raft. In a 9 node setup, with payload side of 35Kb (observed with lot of OVNs), that 220Mbits/s to send in total to others nodes. |
||||||
|
|
||||||
| ### Example of latency vs performance | ||||||
|
|
||||||
| The following graph shows, in a controlled environment, the impact of latency on | ||||||
| simple requests. | ||||||
|
|
||||||
| Tests have been done with DSS version v0.22.0, CockroachDB, 3 USS with one node, | ||||||
| on a virtual machine with ample resources. | ||||||
|
|
||||||
| Latency is injected with | ||||||
| `tc qdisc add dev eth0 root netem delay Xms 2ms distribution paretonormal` ([documentation](https://man7.org/linux/man-pages/man8/tc-netem.8.html)), with | ||||||
| X ranging from 0 to 50, in steps of 5ms. No loss is applied, nor latency between | ||||||
| a DSS and its datastore. | ||||||
|
|
||||||
| Performance is measured by creating and deleting RID ISAs, without any | ||||||
| subscriptions, as it performs simple queries and doesn't create congestion | ||||||
| issues. | ||||||
|
|
||||||
| Queries are done against all DSS, with 3 processes in parallel (for a total | ||||||
| of 9) and at the same time to remove as many variations as possible. Notice | ||||||
| there is still some expected variance, the goal here is to show global trends. | ||||||
|
|
||||||
|  | ||||||
| /// caption | ||||||
| Results of testing RID ISA calls with | ||||||
| various latencies. | ||||||
| /// | ||||||
|
Comment on lines
+72
to
+76
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The time to respond trend here makes perfect sense to me: when it takes longer to exchange information between internal nodes, of course the overall request will take longer to fulfill. In fact, we can even impute an estimate for the number of node traversals each request takes: 45ms node latency increases request latency by about 2200ms, putting the number of node-edge traversals per request at around 50. However, I don't understand why there should fundamentally be any decrease in throughput due to latency. It certainly could be that queries interfere with each other and therefore have to wait, so contention + latency = decrease in throughput, but above we write that the queries were chosen not to create congestion issues which I take as intending to rule out this mechanism. There is a BD limit for TCP communication, but it seems like lots of little hops between nodes rather than one big hop should rule this out as a mechanism as well. What is our theory for why throughput on this graph drops with latency?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, there are no contention. However, there are always some penalty for sync / data to be transferred around to get results and that have some impact.
Directly in that case, yes, we can probably exclude DB.
On top of previous part (needing to get data), since we're in a "fix number of queries in //", there will always be limit dues to the fixed number of queries that can be processed at the time. Assuming 9 processes doing queries, if one query individually take 10 time more, you will still get 10 time less q/s globally. |
||||||
|
|
||||||
| This is just an example of one call, but it shows that even with a simple | ||||||
| operation, there is a incompressible limit on how fast queries can be processed. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need to be precise here: what does "how fast queries can be processed" mean? Certainly latency controls how fast a query can be processed (and therefore how fast queries can be processed), but I don't see the incompressibility of how fast a batch of queries can be processed due to latency. If we mean the former:
Suggested change
...but I don't see how that is very interesting since the thing that is really mysterious here to me is how latency leads to an incompressible limit on bandwidth. And, if latency actually leads to an incompressible limit on bandwidth, what is the mechanism and how to Raftstore overcome it?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, that more for a single query, but also no, because you cannot have infinie-sized batch of queries to have unlimited queries processing. Simple limits would be how much you can keep in memory, how much you receive from clients (and how long do you wait to regroup them), or even number of open connection that will at least be limited to at most 65k.
Maybe the bandwidth part is a bit confusing: the section above was meant to show 'data transfer limits' and here I don't think 'bandwidth' in term of data / s is reverent.
The main difference is in the number of 'synchronization step'. In a very simplified way, raft only need one round trip exchange per "operation|http query", whereas in cockroachdb do that per "sql query" and they can me multiple ones. |
||||||
|
|
||||||
| ## Examples | ||||||
|
|
||||||
| This part showcases the effect of latency on a sample deployment to | ||||||
| illustrate the effects of latency. | ||||||
|
|
||||||
| !!! warning | ||||||
| Notice that values here are only a snapshot from when they were run. DSS code | ||||||
| has improved and will improve in the future, cloud providers' network resources | ||||||
| will evolve with time, and even the placement of your virtual machine could | ||||||
| randomly impact latency and performance. | ||||||
|
|
||||||
| Results have to be interpreted relative to each other in various situations, | ||||||
| not as the performance to be expected from the DSS. | ||||||
|
|
||||||
| The performance baseline will be described in another document. | ||||||
|
|
||||||
| Locust instances are always located next to the DSS being tested, on the same | ||||||
| machine. The test ran is: | ||||||
|
|
||||||
| ```docker run -e AUTH_SPEC="DummyOAuth(http://172.17.0.1:8085/token,localhost)" -p 8089:8089 -v .:/app/ interuss/monitoring-dev uv run locust -f loadtest/locust_files/ISA.py -H http://172.17.0.1 -u 10 --uss-base-url http://dss.localututm```. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Why
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had issues running locust with the non-dev image, but maybe that something not actual anymore? |
||||||
|
|
||||||
| The test has been chosen to be light and to be able to run it across a wide | ||||||
| number of configurations, with the possibility of adding one subscription to | ||||||
| force 'synchronization' between nodes. Others, more complex tests (like | ||||||
| FlightsInSubs which create one implicit subscription for each call) generate too | ||||||
| many contentions with version v0.22.0 to be meaningful. | ||||||
|
|
||||||
| ### Constraints: identifying the latency impact with a stable environment | ||||||
|
|
||||||
| These examples measure the impact of **latency alone**. Every environment | ||||||
| parameter is held constant across the three scenarios; the only variable is | ||||||
| geographic distance, and therefore inter-node latency. Any difference in the | ||||||
| results is attributable to that latency as much as | ||||||
| practically possible. | ||||||
|
|
||||||
| Held constant: | ||||||
|
|
||||||
| * **DSS version**: v0.22.0 in every run. | ||||||
| * **Topology**: one node per location, three nodes across two providers. | ||||||
| * **Machine resources**: ample CPU and memory; compute is never the bottleneck. | ||||||
| * **CockroachDB encryption**: off. | ||||||
| * **Client placement**: Locust runs on the same machine as the DSS under test, | ||||||
| so client↔DSS latency is negligible. Only *inter-node* latency varies. | ||||||
| * **Load profile**: the same light test, fixed at 10 users (`-u 10`), with no | ||||||
| parallelization: each user sends one request, waits for the full response, | ||||||
| then sends the next. | ||||||
| * **Datastore connections**: left to default value (4). | ||||||
|
|
||||||
| The serial load is what makes latency directly visible in throughput: each | ||||||
| user's rate is capped by the round-trip time, so once latency dominates, total | ||||||
| throughput falls as roughly `users / latency`. Read the q/s columns as a readout | ||||||
| of latency, not of DSS capacity. | ||||||
|
Comment on lines
+129
to
+132
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes a lot of sense to me: adding latency causes each individual query to take longer, but throughput is only affected if parallelism is capped. It seems like (to a first approximation) throughput should be able to scale linearly with parallelism (regardless of latency) until the throughput limit is hit, and the throughput limit is entirely separate from latency. This seems in contradiction to many places above, like the "Bandwidth" section.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But parallelism is capped no? I don't see how you can achieve infinite parallelism. |
||||||
|
|
||||||
| ### US-West | ||||||
|
|
||||||
| Cluster has been deployed on the west coast, in two providers. Regions between | ||||||
| providers are close, and the two machines in the same provider are in the same | ||||||
| region, but different availability zone. | ||||||
|
|
||||||
| Latency measured by CockroachDB is as follows: | ||||||
|
|
||||||
|  | ||||||
|
|
||||||
| Results after 2 minutes are: | ||||||
|
|
||||||
| | Node | q/s | 50th latency | 95th latency | | ||||||
| | ---- | ----- | ------------ | ------------ | | ||||||
| | N1 | 19.35 | 2ms | 11ms | | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this document is intended as a description of a consistent phenomenon/behavior rather than a specific test report, so we should limit the information displayed to that which illustrates the point and is expected to be reasonably reproducible under comparable conditions and procedure. So, for instance, it seems like the QPS should be reported with zero decimals (19 q/s, 19 q/s, 18 q/s) -- otherwise, the reader may be confused looking for the semantic significance of the difference between 19.35 and 19.01. Also, there seems to be a substantial difference in 95th percentile latency -- is this significant? If so, let's narrate that significance. If not, let's omit it to avoid distracting the reader from the significant information, or else add narration explaining why the difference isn't significant.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes ok, I will do a pass of rounding.
We should probably narrate it, but one important part of the 95th is to check if some queries timeout when it reach 10000ms. |
||||||
| | N2 | 19.01 | 3ms | 14ms | | ||||||
| | N3 | 18.2 | 17ms | 97ms | | ||||||
|
|
||||||
| showing good performances in general. | ||||||
|
|
||||||
| Notice primary CockroachDB node is probably located in 'Provider 1'. Other test | ||||||
| runs showed swapped latency, but that is to be expected. | ||||||
|
|
||||||
| With one subscription added in the database, forcing a synchronized update: | ||||||
|
|
||||||
| | Node | q/s | 50th latency | 95th latency | Failures/s | | ||||||
| | ---- | ----- | ------------ | ------------ | ---------- | | ||||||
| | N1 | 19.1 | 2ms | 17ms | 0 | | ||||||
| | N2 | 18.81 | 3ms | 14ms | 0 | | ||||||
| | N3 | 17.53 | 16ms | 120ms | 0 | | ||||||
|
|
||||||
| While this shows a slight decrease in performance (approximately 2 percent for | ||||||
| q/s and 50th latency), the extra, synchronized step needed is almost invisible: | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we think 2 percent for QPS and 50th percentile latency is a true difference that would persist at approximately that level if we were to repeat the exact same experiment? If the resolving power of this experiment is that fine, why does N3 latency decrease by 6%?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think so yes. For N3 specifically (and in others situation bellow): N3 is more sensitive to more queries, as it need to synchronize with it peers in the other provider, where the leader (N1) is. |
||||||
| with nodes this close, the synchronization round-trip costs only a few | ||||||
| milliseconds. | ||||||
|
|
||||||
| ### Across US | ||||||
|
|
||||||
| Cluster has been deployed across the US, in two providers: one node in the west, | ||||||
| one node in the east and, in a different provider, in the center. | ||||||
|
|
||||||
| Latency measured by CockroachDB is as follows: | ||||||
|
|
||||||
|  | ||||||
|
|
||||||
| Results after 2 minutes are: | ||||||
|
|
||||||
| | Node | q/s | 50th latency | 95th latency | | ||||||
| | ---- | ----- | ------------ | ------------ | | ||||||
| | N1 | 17.42 | 2ms | 120ms | | ||||||
| | N2 | 17.08 | 64ms | 350ms | | ||||||
| | N3 | 17.95 | 35ms | 170ms | | ||||||
|
|
||||||
| That's a 7% loss in performances, 700% increased latency for the 50th percentile | ||||||
| and more than 1000% of increase for the 95% percentile. Without a synchronized | ||||||
| step, latency is still low enough that the client cadence, not latency, sets the | ||||||
| rate - hence the small q/s loss. | ||||||
|
|
||||||
| With one subscription added in the database, forcing a synchronized update: | ||||||
|
|
||||||
| | Node | q/s | 50th latency | 95th latency | Failures/s | | ||||||
| | ---- | ---- | ------------ | ------------ | ---------- | | ||||||
| | N1 | 17.6 | 2ms | 92ms | 0 | | ||||||
| | N2 | 4.2 | 2200ms | 2500ms | 0 | | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems extremely surprising; what mechanism would explain N2 latency increasing to 34x while N3 only increased to 1.5x? It seems like that is the most significant feature of this data and would need to be explained before we could draw any other conclusion -- averaging 34x and 1.5x to get 24x seems to be ignoring whether the 1.5-34x range is reasonable (and therefore whether an average is appropriate).
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering if I made a typo when recording the 2200ms (220ms?), because that would feet the *4 ratio between N2 and N3, so I simulated the same setup locally (with modified scripts - having the option at some point would be great ^^'). I'm getting the same kind of results (explainable by the fact that it's on my machine, latencies are simulated, etc..): N1 at high Q/S (around 17), ~35ms for 50th latency, ~350ms for 95th
(Note: Graphs are cut to the end of the test - I refreshed them to be sure to have correct value but not directly after - leaving a big empty space) Network matrix (N2/N3 swapped dues to initialization order but I stay consistent everywhere else):
When I tested (I lost the graph but it was not that bad), N2 was probably on the edge of being still functional, with high but still working latencies. (That just a feeling because I don't have data, but I also suspect cockroach's range are spread differently, leading to worse performances, especially since now N1 have one RTT of latency) Now for the potential explanation: N2 has 2 time the latency to the leader (N1) than N3. I would think that it's making it less 'capable' of making it transactions passing, as they are longer. N1 mainly have to talk to it self and can process/apply transaction very quickly, whereas N2 has to wait for a reply and have a very higher penalty to retry them. Seeing that was probably just bellow the limit, I think the 2200ms is possible.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is also the diff I used to simulate that: forcedlat.patch |
||||||
| | N3 | 12.3 | 54ms | 100ms | 0 | | ||||||
|
|
||||||
| That's a 40% loss in performances, 24000% increased latency for the 50th | ||||||
| percentile and more than 6000% of increase for the 95% percentile, compared to | ||||||
| the same step in previous scenario. | ||||||
|
|
||||||
| The impact is way more visible there - and it is pure latency, not capacity: on | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is confusing: the table shows a substantial drop in throughput, yet the narration is that the impact is pure latency. I don't disagree with the explanation, but I think that means we are not presenting the data as clearly as practical. If QPS and latency are directly linked through an arithmetic relationship, there is certainly no reason to show both. If QPS and latency are sometimes linked through an arithmetic relationship and sometimes not, it seems like we should be able to come up with a different metric that is orthogonal and report that instead of one of the sometimes-linked metrics.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes: there are two latency: the one between the nodes and the ones of request. It need to be rewritten (specially since the number is the other latency in the same sentence). |
||||||
| N2 the synchronized round-trip costs ~2200ms, and since each user must wait it | ||||||
| out before its next request, q/s falls to ~4 by simple arithmetic. The | ||||||
| CockroachDB leader (near N1) pays no such round-trip and keeps performing well. | ||||||
|
|
||||||
| ### Across Atlantic Ocean | ||||||
|
|
||||||
| Cluster has been deployed across the US and in Europe, in two providers: one | ||||||
| node in the west, one in Europe and, in a different provider, in the center. | ||||||
|
|
||||||
| Latency measured by CockroachDB is as follows: | ||||||
|
|
||||||
|  | ||||||
|
|
||||||
| Results after 2 minutes are: | ||||||
|
|
||||||
| | Node | q/s | 50th latency | 95th latency | | ||||||
| | ---- | ---- | ------------ | ------------ | | ||||||
| | N1 | 18.4 | 2ms | 180ms | | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you know how 2ms can be possible? It seems like a database operation cannot be complete until a majority of the Raft group has adopted it, and the operation cannot have been adopted by any other node in 2ms when communication requires a minimum of 30ms.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That a good question. N1 is definitely the leaded in that case, and I think the reasonable explanation is that it's doing optimizations / caching, knowing it is the leader and don't wait for adoption? I can investigate more if you want, I never noticed that strange, but I think that consistent in general with what I saw. |
||||||
| | N2 | 14.2 | 150ms | 630ms | | ||||||
| | N3 | 18.4 | 34ms | 240ms | | ||||||
|
|
||||||
| That's a 9% loss in performances, 1650% increased latency for the 50th | ||||||
| percentile and more than 2000% of increase for the 95% percentile. | ||||||
|
|
||||||
| With one subscription added in the database, forcing a synchronized update: | ||||||
|
|
||||||
| | Node | q/s | 50th latency | 95th latency | Failures/s | | ||||||
| | ---- | ---- | ------------ | ------------ | ---------- | | ||||||
| | N1 | 10.4 | 570ms | 4600ms | 0.15 | | ||||||
| | N2 | 1.85 | 10000ms | 10000ms | 0.5 | | ||||||
| | N3 | 5.45 | 1200ms | 10000ms | 0.18 | | ||||||
|
|
||||||
| That's a 69% loss in performances, 122000% increased latency for the 50th | ||||||
| percentile and more than 35000% of increase for the 95% percentile, compared to | ||||||
| the same step in the first scenario. Compared to the non-synchronized case, we | ||||||
| lost about 63% of queries per second. | ||||||
|
|
||||||
| Here the cross-Atlantic synchronization round-trip approaches the request | ||||||
| timeout (10000ms): each serial user spends nearly the whole timeout waiting on | ||||||
| quorum, so throughput collapses and requests start to fail - the only scenario | ||||||
| with failures. This is the latency of synchronizing across an ocean, not a | ||||||
| limitation of the DSS: even the leader region (N1) is slowed purely by the cost | ||||||
| of reaching quorum. At this distance, synchronized operations are latency-bound | ||||||
| to the point of timing out, especially away from the leader. | ||||||
|
|
||||||
| ### Summary | ||||||
|
|
||||||
|  | ||||||
|
|
||||||
| The DSS runs on a 3-node CockroachDB cluster, tested across 3 geographic setups | ||||||
| with **all environment parameters held constant** - version, topology, machine | ||||||
| resources, encryption, client placement, and the serial 10-user load. The only | ||||||
| variable is geographic distance, hence inter-node latency (8.9ms -> 38.9ms -> | ||||||
| 94ms): every difference in the results is attributable to it, and under the | ||||||
| serial load, q/s is a direct readout of latency, not of DSS capacity. | ||||||
|
|
||||||
| The data shows: | ||||||
|
|
||||||
| * Without a synchronized step, latency stays below the client cadence, so q/s | ||||||
| drops only ~7% (across US) to ~9% (Atlantic). | ||||||
| * With a synchronized step (one subscription), each request pays a consensus | ||||||
| round-trip that grows with distance: invisible in US-West, ~750ms and −40% | ||||||
| q/s coast-to-coast, and near the 10000ms timeout across the Atlantic - q/s | ||||||
| down to ~6 (−69%), with failures (up to 0.5/s) appearing only there. | ||||||
|
|
||||||
| The conclusion is about latency, not the DSS. In no run is the DSS or database | ||||||
| saturated; they are waiting on quorum round-trips whose duration is set by | ||||||
| distance - ultimately by the speed of light - and no DSS or query optimization | ||||||
| can remove that. | ||||||
|
|
||||||
| Finally, this test was done on endpoints performing relatively simple queries. | ||||||
| More complex ones, like SCD, involve more synchronized steps per operation and | ||||||
| are therefore even more sensitive to inter-node latency. | ||||||
|
|
||||||
| ## Conclusion | ||||||
|
|
||||||
| To minimize the round-trip cost of consensus and synchronization, keep nodes as | ||||||
| close as possible, ideally within the same region. | ||||||
|
|
||||||
| At the same time, you should probably keep distinct areas, datacenters and | ||||||
| providers for redundancy. A set of servers in a single rack will be very fast, | ||||||
| but have very low redundancy, for example if the rack loses power. On the | ||||||
| opposite side of the options, servers spread throughout the world will offer | ||||||
| maximum redundancy, for example against country-level issues, but will be very | ||||||
| slow. The goal is a balance: geographically close enough for low latency, | ||||||
| diverse enough for redundancy. | ||||||
|
|
||||||
| Think and plan for the failures you want to handle, as a pool, and then spread | ||||||
| nodes across failure domains accordingly for resilience. | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| digraph time_to_answer { | ||
| rankdir=LR; | ||
| node [shape=circle, colorscheme=paired8, width=2.0, fixedsize=true]; | ||
| graph [dpi = 300]; | ||
|
|
||
| N1 [label="N1\nProvider 1\nRegion W",color=1] | ||
| N2 [label="N2\nProvider 1\nRegion E",color=2] | ||
| N3 [label="N3\nProvider 2\nRegion C",color=3] | ||
|
|
||
| N1 -> N2 [label="~58.8ms",dir=both] | ||
| N1 -> N3 [label="~31.1ms",dir=both] | ||
| N2 -> N3 [label="~26.7ms",dir=both] | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| digraph time_to_answer { | ||
| rankdir=LR; | ||
| node [shape=circle, colorscheme=paired8, width=2.0, fixedsize=true]; | ||
| graph [dpi = 300]; | ||
|
|
||
| N1 [label="N1\nProvider 1\nRegion W",color=1] | ||
| N2 [label="N2\nProvider 1\nRegion E",color=2] | ||
| N3 [label="N3\nProvider 2\nRegion C",color=3] | ||
|
|
||
| N1 -> N2 [label="~141.9ms",dir=both] | ||
| N1 -> N3 [label="~31.2ms",dir=both] | ||
| N2 -> N3 [label="~109ms",dir=both] | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| digraph time_to_answer { | ||
| rankdir=LR; | ||
| node [shape=box, colorscheme=paired8]; | ||
| graph [dpi = 300]; | ||
|
|
||
| client [label="Client"]; | ||
| core [label="DSS"]; | ||
|
|
||
| subgraph cluster_db { | ||
| label="Distributed DB cluster"; | ||
| style=dashed; | ||
| leader [label="Node\n(leaseholder)"]; | ||
| peer1 [label="Peer node"]; | ||
| peer2 [label="Peer node"]; | ||
| } | ||
|
|
||
| client -> core [label="API call"]; | ||
| core -> leader [label="query"]; | ||
|
|
||
| leader -> peer1 [label="consensus\nround-trip"]; | ||
| leader -> peer2 [label="consensus\nround-trip"]; | ||
| peer1 -> leader [label="ack"]; | ||
| peer2 -> leader [label="ack (quorum)"]; | ||
|
|
||
| leader -> core [label="result"]; | ||
| core -> client [label="response"]; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| digraph time_to_answer { | ||
| rankdir=LR; | ||
| node [shape=circle, colorscheme=paired8, width=2.0, fixedsize=true]; | ||
| graph [dpi = 300]; | ||
|
|
||
| N1 [label="N1\nProvider 1\nRegion 1\nZone 1",color=1] | ||
| N2 [label="N2\nProvider 1\nRegion 1\nZone 2",color=2] | ||
| N3 [label="N3\nProvider 2\nRegion 1",color=3] | ||
|
|
||
| N1 -> N2 [label="~0.78ms",dir=both] | ||
| N1 -> N3 [label="~13.4ms",dir=both] | ||
| N2 -> N3 [label="~12.6ms",dir=both] | ||
|
|
||
| } |




There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you help me understand why we would assume no parallelism? Of course latency and bandwidth are effectively different expressions of the same quantity when there is no (or fixed) parallelism, but it seems like that is not a very interesting nor relevant case. With parallelism (which represents real-world conditions), it seems like we could have any combination of {high and low} {latency and bandwidth} (for instance, carrier pigeons with micro SD cards would be extremely high latency but also high bandwidth). I am confused why this section seems to say high latency necessarily leads to lower bandwidth/throughput -- what is the mechanism by which high latency necessarily lowers throughput?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, there is always some parallelism and it can be increased up to certain limits, but the idea is to measure the impact in a fixed setting.
Also parallelism has it's own limits, it can be increased to a certain point, but not to infinity, and the same, relative graph will occur. (and practically that the Bandwidth-delay product you quoted bellow).