chadtmiller.com/knotless
git that lives in your Atmosphere account
1
10
The bench reaches the PDS over https, and counts connecting
crates/knotless/examples/bench.rs+277−170
447 lines changed: not loaded until asked for.
crates/knotless/tests/common/mod.rs+13−4
| @@ -216,10 +216,11 @@ pub fn browser(account: &Account, extra: &str) -> String { | ||
) | ||
} | ||
| ||
/// An https front for a server on `port`, at the harness's gateway | ||
/// hostname, until it is dropped. | ||
/// An https front for a server, at the port and URL it has until it is | ||
/// dropped. | ||
pub struct TlsProxy { | ||
pub url: String, | ||
pub port: u16, | ||
child: Child, | ||
} | ||
| ||
| @@ -232,9 +233,15 @@ impl Drop for TlsProxy { | ||
| ||
pub fn tls_proxy(port: u16) -> TlsProxy { | ||
let tls = &net().gateway; | ||
tls_front(&tls.cert, &tls.key, &tls.host, port) | ||
} | ||
| ||
/// An https front for a server on `port`, at `host`, with the certificate | ||
/// and key in the files named, until it is dropped. | ||
pub fn tls_front(cert: &str, key: &str, host: &str, port: u16) -> TlsProxy { | ||
let mut child = Command::new("node") | ||
.arg(root().join("harness/tls-proxy.mjs")) | ||
.args([&tls.cert, &tls.key, &port.to_string()]) | ||
.args([cert, key, &port.to_string()]) | ||
.stdin(Stdio::piped()) | ||
.stdout(Stdio::piped()) | ||
.spawn() | ||
| @@ -243,8 +250,10 @@ pub fn tls_proxy(port: u16) -> TlsProxy { | ||
BufReader::new(child.stdout.take().unwrap()) | ||
.read_line(&mut line) | ||
.unwrap(); | ||
let port = line.trim().parse().expect("the front prints its port"); | ||
TlsProxy { | ||
url: format!("https://{}:{}", tls.host, line.trim()), | ||
url: format!("https://{host}:{port}"), | ||
port, | ||
child, | ||
} | ||
} | ||
docs/reference/performance.md+64−12
| @@ -11,39 +11,91 @@ to the PDS, so every operation is planned around them. | ||
| `git fetch` | 1 when the new packs are among the newest 4 and under 192 KiB, as they arrive beside the refs (three reads in parallel); a client further behind adds one for the packs its refs name; larger packs add parallel blob reads | | ||
| `knotless repo commit`, no checkout | 2, plus 3 per lost race | | ||
| A ref's compare-and-swap | 0 extra: its claim record goes in the same `applyWrites` | | ||
| `git push` to a branch, with an appview | 2 more, to the appview, one after the other: who may push, and the pulls the push may have landed | | ||
| Connecting | 2 for each server a command reaches over https: TCP's handshake, then TLS's | | ||
| ||
"Warm" means after the first call: the identity, session and repository key | ||
are cached on disk, and every request in a process shares one connection. | ||
Each command is a process of its own, so each connects again. | ||
[Storage](../design/storage.md) explains each count. | ||
| ||
## Benchmarks | ||
| ||
Measured with `cargo run --release --example bench -- --rtt 50` (`just | ||
bench 50`): the npm reference PDS (`@atproto/pds` 0.5.43, SQLite) on the | ||
same machine, behind a proxy adding 50 ms of round-trip time, on an | ||
Apple-silicon laptop. | ||
same machine, an Apple-silicon laptop, reached as a PDS on the network is: | ||
over https, through a proxy adding 50 ms to every round trip, those a | ||
command spends connecting among them. Four rounds of 15 samples, each | ||
round on a repository of its own, with 5 clones a round. | ||
| ||
| | p50 | p95 | | ||
| --- | --- | --- | | ||
| `git push`, one small commit | 205 ms | 228 ms | | ||
| `git fetch`, one new commit | 170 ms | 272 ms | | ||
| `knotless repo commit`, no checkout | 423 ms | 610 ms | | ||
| `git clone`, 31 packs | 310 ms | 378 ms | | ||
| `git clone`, compacted | 191 ms | 344 ms | | ||
| `git clone --depth 1` via `knotless-serve` | 208 ms | 299 ms | | ||
| One file via the `knotless-serve` API, warm | 45 ms | 57 ms | | ||
| 16 agents pushing 16 branches at once | 591 ms total | | | ||
| 16 agents committing to one branch at once | 11.9 s total, 96 pushes | | | ||
| `git push`, one small commit | 296 ms | 320 ms | | ||
| `git fetch`, one new commit | 259 ms | 309 ms | | ||
| `git push` to a branch, with an appview | 507 ms | 516 ms | | ||
| `knotless repo commit`, no checkout | 435 ms | 469 ms | | ||
| `git clone`, 31 packs | 379 ms | 412 ms | | ||
| `git clone`, compacted | 263 ms | 299 ms | | ||
| `git clone --depth 1` via `knotless-serve` | 115 ms | 123 ms | | ||
| One file via the `knotless-serve` API, warm | 30 ms | 32 ms | | ||
| 16 agents pushing 16 branches at once | 561 ms total | | | ||
| 16 agents committing to one branch at once | 8.9 s total, 91 pushes | | | ||
| ||
Every row but the third runs with no appview. A client set up as the | ||
installer leaves it has one, and a push of a branch asks it two things | ||
before git hears the push landed: on a connection of its own, four round | ||
trips more. The appview here is as far away as the PDS. | ||
| ||
Three things are kinder than a PDS on the network. Compaction is off. The | ||
harness's PDS does not wait for its writes to reach the disk. And the two | ||
rows read through `knotless-serve` reach it with no latency added, since | ||
its mirror is beside the reader. | ||
| ||
## Many branches at once | ||
| ||
Agents each pushing a new branch of its own at the same moment, in the same | ||
bench at 50 ms: | ||
| ||
| Agents | All pushed in | Pushes a second | | ||
| --- | --- | --- | | ||
| 1 | 318 ms | 3.1 | | ||
| 4 | 354 ms | 11.3 | | ||
| 16 | 561 ms | 28.5 | | ||
| 64 | 1.31 s | 48.9 | | ||
| ||
No branch waits for another on the network: sixty-four pushes wait for the | ||
round trips of one. What grows is this machine's work, sixty-four `git`s | ||
and a PDS that writes one account's records in turn: with no latency added | ||
at all, the sixty-four take 1.5 s. | ||
| ||
## Many writers on one branch | ||
| ||
A branch is one compare-and-swap, so of the writers pushing straight to one | ||
branch, one lands per round. The reference PDS also serializes an account's | ||
writes. That is why the last row above is slow. Give each agent its own | ||
writes. That is why the sixteen committing to one branch take seconds, | ||
where sixteen pushing a branch each take half of one. Give each agent its own | ||
branch and merge, or send commits through a | ||
[gateway](../design/gateway.md#commit), which queues them per branch and | ||
lands each queue as one push. | ||
| ||
## Other round-trip times | ||
| ||
The same bench at 0, 100 and 200 ms separates what an operation costs on | ||
this machine from what it waits for on the network. Each time is close to | ||
the first column plus the round-trip time for each round trip, which is the | ||
slope measured from 0 to 200 ms. `--plain` reaches the PDS over plain http | ||
and charges nothing for connecting, which leaves the requests: the last | ||
column, measured the same way. | ||
| ||
| | 0 ms | 50 ms | 100 ms | 200 ms | Round trips | Of them requests | | ||
| --- | --- | --- | --- | --- | --- | --- | | ||
| `git push`, one small commit | 92 ms | 296 ms | 496 ms | 898 ms | 4.0 | 2.0 | | ||
| `git fetch`, one new commit | 105 ms | 259 ms | 406 ms | 710 ms | 3.0 | 1.0 | | ||
| `git push` to a branch, with an appview | 108 ms | 507 ms | 911 ms | 1.71 s | 8.0 | 4.0 | | ||
| `knotless repo commit`, no checkout | 242 ms | 435 ms | 640 ms | 1.05 s | 4.1 | 2.0 | | ||
| `git clone`, 31 packs | 237 ms | 379 ms | 533 ms | 842 ms | 3.0 | 1.0 | | ||
| `git clone`, compacted | 114 ms | 263 ms | 414 ms | 716 ms | 3.0 | 1.0 | | ||
| ||
## Large repositories | ||
| ||
Measured by pushing three real repositories into the npm reference PDS | ||