Conversation
|
Smithers evidence run for TC-410 Benchmark verdict: regressed The TC-410 commit (7b7585e, only auth_guards.rs +74 and routes/mod.rs 151 lines) strictly removes per-request work: the old ObjectHeaders guard cloned every header (including Authorization) into an owned BTreeMap; the new InvokeHeaders<'r> guard borrows control headers and materializes only the storable allowlist. Nothing in this diff plausibly adds 11ms to kv.get tails — the guard runs identically for put/list/sql scenarios, whose p99s improved. Three pieces of evidence point to measurement artifact: (1) the baseline run itself carries wandering ~20-26ms p99 outliers in kv.put and sql.query (5x their own p95s) that vanish in the candidate run while an equivalent ~17ms outlier appears in kv.get — one slow window per run landing in different scenarios; (2) the profile uses rounds=5/samples=50, so p99 is effectively the single slowest request of 50 — a one-request GC pause, SQLite checkpoint, or dropped keep-alive per round flips the gate; (3) the non-target crypto.decrypt p99 (+227%) cannot be affected by server header handling, confirming run-level tail instability. Additionally, the branch is stacked: main..HEAD contains b0bf4b1 (TC-409, #200) and a62a92f (TC-313, #201), and local main (593c4d8) is 2 commits behind origin/main. If the benchmark baseline was built from stale local main, the measured delta conflates TC-409's admission/replay-persistence changes (admission.rs, db.rs, invocation_replay.rs — plausible sources of read-path tail stalls via DB write contention) with TC-410's header work. Next actions:
|
|
Benchmark provenance correction: the baseline was not stale. All five baseline summaries identify server revision Therefore Fable's stale-baseline concern is ruled out, but the regressed gate still stands: KV-get p99, header p99, and total p99 each increased by ~11 ms and crossed the 4/5 consistency threshold. The PR remains open and unmerged. The useful Fable follow-ups are to raise samples to >=200, interleave A/B rounds on a quiesced machine, instrument server-vs-network outliers if reproduced, hoist Connection-token parsing if warranted, and attach the issue's requested small/depth-4 allocation evidence. Artifacts are local under |
TC-410: https://linear.app/tinycloud-labs/issue/TC-410/perfnode-stop-cloning-authorization-into-object-metadata
Summary
In tinycloud-node-server/src/auth_guards.rs: added a new
InvokeHeaders<'r>FromRequest guard replacing ObjectHeaders' FromRequest impl (ObjectHeaders itself is retained solely as the stored-metadata Responder). InvokeHeaders borrows the last Rocket-parsed value (viaheaders.get(name).last(), zero-alloc) for accept, content-type, if-match, if-none-match, x-tinycloud-expected-version, x-tinycloud-max-response-bytes, x-tinycloud-limit, and x-tinycloud-cursor, and owns only a Metadata map built from the existing storable allowlist (content-type/encoding/language/disposition + x-tinycloud-meta-*). Fixedis_storable_object_headerto be allocation-free and ASCII-case-insensitive (previously calledto_ascii_lowercase(), allocating a String on every check) usingeq_ignore_ascii_caseand a byte-slice prefix compare. Addedconnection_nominates, which scansConnectionheader values viasplit(',')(no token-set allocation) so any storable header nominated by a comma-separated Connection token is excluded from persisted metadata — Authorization, Cookie, and all hop-by-hop headers were already excluded by the allowlist regardless. In tinycloud-node-server/src/routes/mod.rs: switched both#[cfg(feature="duckdb")]/#[cfg(not(...))]invoke()variants andinvoke_implto takeInvokeHeaders<'_>instead ofObjectHeaders; updatedkv_invoke_options,kv_invoke_options_for_capabilities[_with_cursor],is_multipart, andbuild_batch_kv_inputsto read the borrowed fields directly instead of mutating/taking from an owned Metadata map; removed the now-unusedmetadata_header/take_metadata_headerhelpers; changedparse_positive_u64_headerto acceptOption<&str>directly; replaced the DuckDB Arrow-Accept check to readheaders.acceptdirectly; the non-multipart KV put path now movesheaders.metadataonce intofilter_stored_object_metadata(defense-in-depth filter retained unchanged). Updated three unit tests (bounded_kv_headers_are_positive,kv_create_and_replace_headers_build_exact_key_preconditions,kv_condition_headers_reject_ambiguous_or_batch_mutations) to constructInvokeHeadersfixtures instead ofObjectHeaders. No changes to authorization, revocation, replay-cache, timestamp validation, database schema, CORS, signed-URL, SDK, or deployment/infrastructure code.Acceptance Criteria
Test Plan
Add Rocket-backed characterization tests before refactoring that establish logical case folding, first-name casing, FIFO duplicates, last-value selection, and multiple Connection-line behavior. Add InvokeHeaders unit tests for mixed case and repeated values; approved metadata and every borrowed control must preserve current selection, while hostile headers are absent.
Add deterministic test-only accounting at the selection seam: selected-entry count and owned name/value bytes. Compare otherwise identical small-Authorization and representative 8–16 KiB/depth-4 requests. Copied Authorization bytes must be exactly zero, and both counts must be identical; adding approved metadata must change counts only by its attributable bytes.
Add an end-to-end /invoke KV put followed by metadata/read coverage. Approved metadata must persist and replay; Authorization, Cookie, fixed hop-by-hop fields, Proxy-Connection, and a Connection-nominated x-tinycloud-meta-* field must not persist or replay. Retain defense-in-depth tests for legacy metadata and multipart part filtering.
Run focused precondition, limit, cursor, multipart, Arrow-Accept, response-streaming, durable-replay, revocation, and w1_native_contract tests. Final gates: cargo fmt --all -- --check; cargo clippy -p tinycloud-node --all-targets -- -D warnings; cargo test -p tinycloud-node; and DuckDB-feature coverage sufficient to compile and exercise borrowed Accept behavior.
Benchmark Plan
Use .context/benchmarks/profiles/TC-410.json unchanged with pinned driver revision 9d4866f, its serverBuildCommand and CARGO_TARGET_DIR, 5 warmups, 50 samples, and 5 paired rounds. Both origin/main baseline and candidate must be release builds. Record exact SHAs and profile hash, use the same quiet host/configuration, and interleave baseline/candidate order across rounds; debug/release mixing or unpaired runs invalidates the result.
Targets are sdk.kv.get, sdk.kv.list, sdk.kv.put, sdk.sql.execute, and sdk.sql.query, especially their .http.headers.post.invoke phases; corresponding .http.total.post.invoke and operation totals are secondary target evidence. Non-targets are every other scenario or phase emitted by the profile and remain regression sentinels.
For every pair, report raw samples plus mean, p95, and p99 deltas. A mean regression counts only when it exceeds both 5% and 0.15 ms for either target or non-target metrics; p95 requires both 7.5% and 0.25 ms; p99 requires both 15% and 0.5 ms. A merge-blocking regression requires the same adverse threshold breach in at least 4 rounds.
Before merge, attach release-build provenance, raw outputs, paired-round comparison, and deterministic copy-accounting results. If latency is below timer noise, merge is acceptable only with zero redundant Authorization copying and no consistent regression; do not claim a speedup. If the profile lacks separate small/depth-4 latency cases, document that without altering it and use deterministic accounting as the size-scaling proof.