Repository navigation
Rewrite the build infrastructure - #394
Conversation
e88add7 to
017c872
Compare
allada
left a comment
There was a problem hiding this comment.
Could you update this PR and lets get it landed?
Reviewable status: 0 of 202 files reviewed, all discussions resolved (waiting on @blakehatch and @MarcusSorealheis)
017c872 to
1548ce6
Compare
f389570 to
33b1d29
Compare
allada
left a comment
There was a problem hiding this comment.
Reviewed 98 of 202 files at r1, 13 of 108 files at r2, all commit messages.
Reviewable status: 111 of 213 files reviewed, 9 unresolved discussions (waiting on @aaronmondal, @blakehatch, and @MarcusSorealheis)
BUILD.bazel line 11 at r2 (raw file):
exports_files( glob(["src/**", "tests/**"]) + [
nit: Lets list every file.
BUILD.bazel line 22 at r2 (raw file):
name = "native_link", srcs = glob( include = ["src/**/*.rs"],
nit: Lets list every file.
BUILD.bazel line 100 at r2 (raw file):
rust_test_suite( name = "native_link_tests", srcs = glob(["tests/*.rs"]),
nit: Lets list every file.
Cargo.toml line 7 at r2 (raw file):
[workspace] resolver = "2"
Do we need this now?
src/config.rs line 1 at r2 (raw file):
pub mod cas_server;
nit: Needs copyright.
src/grpc_service.rs line 1 at r2 (raw file):
pub mod ac_server;
nit: Needs copyright.
src/lib.rs line 1 at r2 (raw file):
pub mod config;
nit: Needs copyright.
src/scheduler.rs line 1 at r2 (raw file):
pub mod action_messages;
nit: Needs copyright.
src/store.rs line 15 at r2 (raw file):
// limitations under the License. pub mod ac_utils;
yuck, can we somehow use crate:: instead?
allada
left a comment
There was a problem hiding this comment.
Reviewable status: 111 of 213 files reviewed, 10 unresolved discussions (waiting on @aaronmondal, @blakehatch, and @MarcusSorealheis)
BUILD.bazel line 74 at r2 (raw file):
rust_binary( name = "nlink",
Lets not rename this yet, we will do it in a follow up PR. Because there's things like terraform that will likely break.
33b1d29 to
af8b0f2
Compare
aaronmondal
left a comment
There was a problem hiding this comment.
Reviewable status: 104 of 214 files reviewed, 3 unresolved discussions (waiting on @allada, @blakehatch, and @MarcusSorealheis)
BUILD.bazel line 74 at r2 (raw file):
Previously, allada (Nathan (Blaise) Bruer) wrote…
Lets not rename this yet, we will do it in a follow up PR. Because there's things like terraform that will likely break.
Done.
Cargo.toml line 7 at r2 (raw file):
Previously, allada (Nathan (Blaise) Bruer) wrote…
Do we need this now?
Unfortunately yes. Both the Bazel resolver and nix cargo wrappers need this workspace for some reason. The resolver = "2" line is technically redundant and we could remove that. I think there was some issue I can't remember where it was recommended to make the resover version explicit.
src/store.rs line 15 at r2 (raw file):
Previously, allada (Nathan (Blaise) Bruer) wrote…
yuck, can we somehow use
crate::instead?
I removed the rest of the actual code from here and put it in a store_manager subcrate. The amount of files here is a bit much, but I'd say this is more of a "not hiding anything" type of thing. Arguably quite ugly, but this gives us very explicit import paths that (in conjunction with the now ordered imports in every file) make it much easier to see where we could reduce interdependencies and split this into sensible smaller crates.
There are a few alternatives to the thing.rs/thing directory approach, but this seems to be the current "recommended" way of doing things and it's at least better than the mod.rs approach where literally every file in the repo would just be called mod.rs 🤣
As a side note, a benefit of this explicit pub mod approach is that it makes it quite easy to turn features on and off by using markers like #[cfg(feature = "s3")].
aaronmondal
left a comment
There was a problem hiding this comment.
Reviewable status: 104 of 214 files reviewed, 3 unresolved discussions (waiting on @allada, @blakehatch, and @MarcusSorealheis)
src/store.rs line 15 at r2 (raw file):
Previously, aaronmondal (Aaron Siddhartha Mondal) wrote…
I removed the rest of the actual code from here and put it in a
store_managersubcrate. The amount of files here is a bit much, but I'd say this is more of a "not hiding anything" type of thing. Arguably quite ugly, but this gives us very explicit import paths that (in conjunction with the now ordered imports in every file) make it much easier to see where we could reduce interdependencies and split this into sensible smaller crates.There are a few alternatives to the
thing.rs/thingdirectory approach, but this seems to be the current "recommended" way of doing things and it's at least better than themod.rsapproach where literally every file in the repo would just be calledmod.rs🤣As a side note, a benefit of this explicit
pub modapproach is that it makes it quite easy to turn features on and off by using markers like#[cfg(feature = "s3")].
Actually, when I think about it, this pattern might be better than I initially thought. With the right code layout,thing.rs can act as the public API interface (think exposed header in C++ land) while all the files in the thing directory act as implementation details. In that case we'd not have the pub mod lines anymore and instead have a seemingly very clean interface where thing.rs contains the entire public API for a crate and everything else can be ignored by users. This would also make it easy to spot API changes since one only needs to look at history of the thing.rs file.
02f1a57 to
54bd14a
Compare
|
FYI @allada In case you're wondering why the new targets are Also note that all the test targets are just called |
54bd14a to
1a9bfec
Compare
allada
left a comment
There was a problem hiding this comment.
All the details we can fight another day.
Reviewed 4 of 202 files at r1, 2 of 108 files at r2, 1 of 1 files at r4, 119 of 119 files at r5, 12 of 12 files at r6, all commit messages.
Reviewable status: all files reviewed, 22 unresolved discussions (waiting on @aaronmondal, @blakehatch, and @MarcusSorealheis)
native-link-service/tests/ac_server_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-service/tests/bytestream_server_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-service/tests/cas_server_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-store/tests/ac_utils_test.rs line 1 at r6 (raw file):
// Copyright 2023 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-store/tests/compression_store_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-store/tests/dedup_store_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-store/tests/fast_slow_store_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-store/tests/filesystem_store_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-store/tests/memory_store_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-store/tests/ref_store_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-store/tests/shard_store_test.rs line 1 at r6 (raw file):
// Copyright 2023 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-store/tests/size_partitioning_store_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-store/tests/verify_store_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-util/tests/async_fixed_buffer_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-util/tests/buf_channel_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-util/tests/evicting_map_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-util/tests/fastcdc_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-util/tests/fs_test.rs line 1 at r6 (raw file):
// Copyright 2023 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-util/tests/resource_info_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-util/tests/retry_test.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-worker/tests/utils/local_worker_test_utils.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
native-link-worker/tests/utils/mock_running_actions_manager.rs line 1 at r6 (raw file):
// Copyright 2022 The Turbo Cache Authors. All rights reserved.
nit: Wrong name.
1a9bfec to
a176c12
Compare
aaronmondal
left a comment
There was a problem hiding this comment.
🥳
Reviewable status: 161 of 239 files reviewed, all discussions resolved (waiting on @allada, @blakehatch, and @MarcusSorealheis)
Our previous build layout was fairly custom and hard to maintain. Migrate to a more standards-conform layout that is compatible with good Cargo and Bazel practices and feels natural to develop in. This commit swaps the previous "one-crate-per-file" layout to a layout where we have a few medium sized crates that are just fine-grained enough to allow almost the same amount of parallelization as the previous fine-grained layout. The new layout enables dependency auto-updaters, standard cargo tooling and doc autogeneration. This should improve DX quite a bit and gives us some much needed compatibility with nix/cargo interop and should make most standard cargo extensions compatible with the repository. Some notable changes to the file layout: - The previous `error` crate is no longer part of `utils`. It is now a standalone module. This was necessary to keep macro exports compatible with the Cargo build. - The old `scheduler.rs` file is now split into `action_scheduler.rs` and `worker_scheduler.rs` since we don't want imports like `native_link_scheduler::scheduler`. - Some tests now use a slightly different import mechanism for test-only dependencies. - The store traits are now in `native_link_utils`, as it was used virtually everywhere. Moving this up in the dependency graph allows for higher parallelization of the other crates. Added documentation targets for every crate (except the `cas` binary itself, as this will require a dedicated commit). These doc targets can be viewed by pointing a browser to `bazel-bin/thetarget.rustdoc/thetarget/index.html`. Added rustfmt configuration to standardize imports. We now use `crate::` syntax which plays well with rusfmts's import formatting. The new layout makes it easier to spot crate/directory interdependencies so the we can remove those in future commits.
a176c12 to
48f8190
Compare
Our previous build layout was fairly custom and hard to maintain. Migrate to a more standards-conform layout that is compatible with good Cargo and Bazel practices and feels natural to develop in. This commit swaps the previous "one-crate-per-file" layout to a layout where we have a few medium sized crates that are just fine-grained enough to allow almost the same amount of parallelization as the previous layout. The new layout enables dependency auto-updaters, standard cargo tooling and doc autogeneration. This should improve DX quite a bit and gives us some much needed compatibility with nix/cargo interop and should make most standard cargo extensions compatible with the repository. Some notable changes to the file layout: - The previous `error` crate is no longer part of `utils`. It is now a standalone module. This was necessary to keep macro exports compatible with the Cargo build. - The old `scheduler.rs` file is now split into `action_scheduler.rs` and `worker_scheduler.rs` since we don't want imports like `native_link_scheduler::scheduler`. - Some tests now use a slightly different import mechanism for test-only dependencies. - The store traits are now in `native_link_utils`, as it was used virtually everywhere. Moving this up in the dependency graph allows for higher parallelization of the other crates. Added documentation targets for every crate (except the `cas` binary itself, as this will require a dedicated commit). These doc targets can be viewed by pointing a browser to `bazel-bin/thetarget.rustdoc/thetarget/index.html`. Added rustfmt configuration to standardize imports. We now use `crate::` syntax which plays well with rusfmts's import formatting. The new layout makes it easier to spot crate/directory interdependencies so that we can remove them in future commits.
Our previous build layout was fairly custom and hard to maintain. Migrate to a more standards-conform layout that is compatible with good Cargo and Bazel practices and feels natural to develop in. This commit swaps the previous "one-crate-per-file" layout to a layout where we have a few medium sized crates that are just fine-grained enough to allow almost the same amount of parallelization as the previous layout. The new layout enables dependency auto-updaters, standard cargo tooling and doc autogeneration. This should improve DX quite a bit and gives us some much needed compatibility with nix/cargo interop and should make most standard cargo extensions compatible with the repository. Some notable changes to the file layout: - The previous `error` crate is no longer part of `utils`. It is now a standalone module. This was necessary to keep macro exports compatible with the Cargo build. - The old `scheduler.rs` file is now split into `action_scheduler.rs` and `worker_scheduler.rs` since we don't want imports like `native_link_scheduler::scheduler`. - Some tests now use a slightly different import mechanism for test-only dependencies. - The store traits are now in `native_link_utils`, as it was used virtually everywhere. Moving this up in the dependency graph allows for higher parallelization of the other crates. Added documentation targets for every crate (except the `cas` binary itself, as this will require a dedicated commit). These doc targets can be viewed by pointing a browser to `bazel-bin/thetarget.rustdoc/thetarget/index.html`. Added rustfmt configuration to standardize imports. We now use `crate::` syntax which plays well with rusfmts's import formatting. The new layout makes it easier to spot crate/directory interdependencies so that we can remove them in future commits.
Our previous build layout was fairly custom and hard to maintain. Migrate to a more standards-conform layout that is compatible with good Cargo and Bazel practices and feels natural to develop in. This commit swaps the previous "one-crate-per-file" layout to a layout where we have a few medium sized crates that are just fine-grained enough to allow almost the same amount of parallelization as the previous layout. The new layout enables dependency auto-updaters, standard cargo tooling and doc autogeneration. This should improve DX quite a bit and gives us some much needed compatibility with nix/cargo interop and should make most standard cargo extensions compatible with the repository. Some notable changes to the file layout: - The previous `error` crate is no longer part of `utils`. It is now a standalone module. This was necessary to keep macro exports compatible with the Cargo build. - The old `scheduler.rs` file is now split into `action_scheduler.rs` and `worker_scheduler.rs` since we don't want imports like `native_link_scheduler::scheduler`. - Some tests now use a slightly different import mechanism for test-only dependencies. - The store traits are now in `native_link_utils`, as it was used virtually everywhere. Moving this up in the dependency graph allows for higher parallelization of the other crates. Added documentation targets for every crate (except the `cas` binary itself, as this will require a dedicated commit). These doc targets can be viewed by pointing a browser to `bazel-bin/thetarget.rustdoc/thetarget/index.html`. Added rustfmt configuration to standardize imports. We now use `crate::` syntax which plays well with rusfmts's import formatting. The new layout makes it easier to spot crate/directory interdependencies so that we can remove them in future commits.
…tlier probe (TraceMachina#413 Option A) Per audit `.claude/audits/413-large-blob-cascade-design-20260511.md`: 17-min post-deploy window 21:55:38–22:13 PDT 2026-05-11 captured 11 cascade events / 9 distinct digests (1.2% of 900 chunked commits) at the prior cap=64. Size distribution: - 64-128 MiB: 2 events - 128-256 MiB: 7 events - 256-540 MiB: 2 events Cap=256 (× 1 MiB CHUNK_SIZE = 256 MiB per-blob, exactly equal to `MAX_CHUNKED_BLOB_SIZE`) structurally eliminates 9 of 11 events (everything ≤256 MiB). The 2 residual 256-540 MB events (RustcLink output blobs / macOS bundles) are tracked under TraceMachina#413 Option D (server-internal driver fan-out, design doc `.claude/audits/303-driver-fanout-design-20260512.md`) and will be revisited once the new `back_edge_ms` probe data validates the inferred ~25-50 MB/s per-blob drain ceiling. **Bottleneck localized via TraceMachina#413 audit:** `chunk_send_ms = 0` for ALL 14 logged steps in cascade trace — the producer never blocks. The cascade is **chunker → driver back-edge**, where per-blob driver pwrite (serialized by per-entry async mutex at `chunked_filesystem.rs:467`) drains slower than the chunker fills the per-blob mpsc. cap=256 widens the buffer; the underlying drain rate (TraceMachina#394) is unchanged. **Aggregate RSS bound unchanged.** Per-blob × concurrent-blobs is NOT the regulator. The §13.2 12-16 GiB worst-case server RSS bound is derived from the GLOBAL `ChunkBudget` (4 GiB / 4096 permits at `chunk_budget.rs:52`) and per-blob `PinBudget`. Bumping cap=256 shifts fan-out shape (max 16 saturated blobs at this cap; was 64 at cap=64; was 256 at cap=16) but not the aggregate ceiling. **New `back_edge_ms` outlier probe** at `chunked_driver.rs:987-999`: emits `warn!` when per-chunk back-edge drain exceeds 50 ms (40 ms = 1 MiB / 25 MB/s healthy ceiling + 25 % cushion). Survives in release builds (`Cargo.toml:101 release_max_level_info` retains `warn!`/`error!`; only `debug!`/`trace!` are compiled out — a `debug!`-level probe would have been dead code in production). Fires only on the success path; the timeout arm (`record_pwrite_timeout_and_maybe_warn`) and write-error arm emit their own `warn!`s without `back_edge_ms`. Acceptable for cap- tuning: failures already log loudly; outlier-on-success was the gap. Field name `back_edge_ms` (NOT `pwrite_ms`) reflects the actual scope: the wall-clock includes per-blob async-mutex acquire BEFORE spawn_blocking, so a same-digest mutex contention spike shows up as "async-mutex hold-time" in the >50 ms arm — operator-actionable, correctly attributed. **Updated:** - `PER_BLOB_MPSC_CAP` constant 64 → 256 + rewritten doc-comment (audit cite, back-edge bound rationale, MAX_CHUNKED_BLOB_SIZE natural ceiling, observability gap acknowledgment) - ChunkPin doc-comment math (64 MiB → 256 MiB) - Unit test renamed `per_blob_mpsc_cap_is_sixtyfour` → `per_blob_mpsc_cap_is_two_fifty_six`, updated body + pin doc - NEW `global_chunk_budget_remains_4_gib_bound_at_cap_256` composite- invariant test (CLAUDE.md "Admission/Eviction/Pin Composability" mandatory): asserts `PER_BLOB_MPSC_CAP × 16 == TOTAL_CHUNK_PERMITS` + saturates 16 × cap = 4096 permits + 17th `try_acquire_chunk()` returns `None`. Mutation-verified RED-fail with bespoke "composite invariant violated" message. - `chunked_client.rs` module-doc reference (256; bumped from 64 on 2026-05-12; was 16 before 2026-05-11; see TraceMachina#413 audit) - `chunked_write_handler.rs:591` stale `(64)` literal dropped from comment (pattern has gone stale 3 cap-bumps in a row; symbolic `PER_BLOB_MPSC_CAP` only) - Integration test `handler_per_blob_mpsc_full_returns_resource_exhausted_with_backpressure_signal` — `const N: usize = 32` → `const N: usize = PER_BLOB_MPSC_CAP + 1` (symbolic, with `wrapping_add` for byte-fill overflow safety) + imports + history-of-regression doc (16 → 64 → 256). Was already toothless at cap=64; cap=256 doubly so. Restored to actually exercise the rejection branch. Composite invariant (per CLAUDE.md): gate-active (per-blob mpsc admission cap) ⇒ (eviction-recovers-fast via chunk-completion permit-drop OR pin-prevents-double-admission via PinBudget). Cap bump shifts which corner fires first; the global ChunkBudget remains the dominant gate at 4 GiB. Reviewer cadre (4 + 2 follow-up): - perf-optimizer: APPROVE (after fix-up: 2 BLOCKERs closed cleanly, mutation-verified — debug→warn release-survival + integration test symbolic N + composite-invariant test with bespoke red-fail) - distributed-systems-reviewer: APPROVE-WITH-FINDINGS (no composite invariant violated; cap edge with MAX_CHUNKED_BLOB_SIZE validated; bitmap completeness exact-equal at 256 chunks; TraceMachina#345 mirror cap interaction confirmed unrelated for chunked path) - code-reviewer: APPROVE-WITH-FINDINGS (file-local `?digest` convention preserved over CLAUDE.md style guide; sibling-pwrite- probe audit clean — no other per-chunk pwrite sites) - assumption-auditor: SHIP (12/12 claims PASS post-fix-up; mutation re-verified the composite test red-fails with the bespoke message; 9-of-11 cascade-coverage doc citation corrected from "8 of 11") Build: `cargo check -p nativelink-store -p nativelink-service` clean. Test: 59 chunked + 20 chunked_write_handler tests pass; new `global_chunk_budget_remains_4_gib_bound_at_cap_256` passes; renamed constant-pin test passes; integration test now actually exercises the rejection branch at N=257. Follow-ups filed: - TraceMachina#413 Option D (driver fan-out, design ready, awaiting pwrite_ms data — see `.claude/audits/303-driver-fanout-design-20260512.md`) - TraceMachina#420 (chunked test /tmp tempdir leak — separate from this diff) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…na#394/TraceMachina#413 Phase 1 measurement gate Per audit `.claude/audits/394-413-saturation-class-plan-20260512.md` §8. Phase 1 of TraceMachina#394 (chunked admission p99) / TraceMachina#413 (large-blob cascade) saturation-class plan: ADD INSTRUMENTATION ONLY — no architectural change. Phase 2 (Option C+A — unbounded mpsc + send().await) lands AFTER this probe confirms the pulse-burst hypothesis in production. **Falsifiable prediction (audit §8):** for blobs that go on to cascade, at least one 100 ms window shows `chunks_in_window > BURST_THRESHOLD_CHUNKS_PER_WINDOW (= 50)` — i.e., producer arrived ≥5x faster than the inferred ~25-50 MB/s per-blob drain ceiling cited in `chunked_driver.rs`'s `PER_BLOB_MPSC_CAP` doc. Clean blobs are predicted to stay <20. **Composite invariant unchanged.** No gate / pin / TTL corner is modified. The probe observes the producer-side admit attempts; sub-gates (1) global ChunkBudget, (2) PinBudget, and (3) per-blob mpsc still gate identically. **Red-team 20260512 RECONSIDER-PREMISE amendments** (this revision): Finding 1 (BLOCK): the original probe could not falsify `H_drain_stall`. Under a wedged drain, the producer hammers a saturated mpsc, `record_attempt` fires BEFORE `try_send`, and rejected attempts still count toward `attempts_in_window`. A drain-stall therefore looks IDENTICAL to a pulse-burst at the warn site — instrument could not discriminate the two hypotheses it was deployed to discriminate. Fix: add a per-window admitted snapshot (`chunks_admitted_at_window_start: u32`) captured when the window opens, and emit `admitted_in_window = chunks_admitted_total - chunks_admitted_at_window_start` in BOTH warn sites. No cross-task coordination because `chunks_admitted_total` is incremented by `record_success` on the same single-task admit loop immediately after a successful `try_send`. Operator discriminator: - `admitted_in_window ≈ chunks_in_window` → pulse-burst (queue accepted the burst; producer outran a healthy drain) - `admitted_in_window << chunks_in_window` → drain-stall (queue stayed full; producer hammered rejections on a wedged drain) Finding 3 (silent-probe vs broken-probe): add an `info!` heartbeat at admit-loop entry in `dispatch_chunks_to_driver`. Fires exactly once per Bazel-facing chunked stream. Tells the operator "the probe was reached for this digest" even if no warn ever fires for the stream, eliminating the prior log-quiet → "is the probe disarmed, threshold never crossed, or this code path not reached?" ambiguity. Format: `info!(target: "nativelink_service::chunked_write_handler", ?digest, "producer-arrival probe armed (TraceMachina#394/TraceMachina#413 Phase 1)")`. Assumption-auditor CLAIM 3 PARTIAL: doc-comment now explicitly relates the probe to the existing `back_edge_ms` probe — "shares the `warn!` / `?digest` / `offset` shape" but latches one-per-window to avoid log-flood on sustained bursts (the `back_edge_ms` probe fires per-chunk on the drain side; this one fires once-per-100ms-window on the admit side). Assumption-auditor CLAIM 4 PARTIAL: under-action test now drives N=200 admissions (up from 60). Even with one mid-loop window rollover on loaded CI, the second window crosses the 50-attempt threshold with plenty of margin, eliminating a flake hazard. Red-team Finding 2 (threshold-calibration unfalsifiability): deferred to follow-up tracker — run probe with `BURST_THRESHOLD_CHUNKS_PER_WINDOW=0` (always-fire) for 5 min in baseline mode to measure healthy distribution before locking the threshold. Current 50/100ms is inferred from the same 25-50 MB/s drain rate the experiment is meant to measure; that circular justification is documented in the const doc. **New per-stream probe state** (`ProducerArrivalProbe`, owned by `dispatch_chunks_to_driver`'s admit loop — single-task per blob, no atomicity needed): - `first_attempt_at: Option<Instant>` — for `producer_arrival_us_since_admission` on the rejection log - `window_start_at: Option<Instant>` + `attempts_in_window: u32` — rolling 100 ms count of admission ATTEMPTS (counts producer rate regardless of which sub-gate accepts/rejects) - `warned_this_window: bool` — latches one warn per pulse, avoids log-flood on sustained burst within a single window - `chunks_admitted_total: u32` — cumulative successful try_send; cross-references rejection log (chunk 20/200 = pulse-burst; chunk 200/210 = drain-stall) - `chunks_admitted_at_window_start: u32` (NEW) — snapshot of `chunks_admitted_total` at window open; powers the `admitted_in_window` discriminator (red-team Finding 1 fix) **Three log sites** in the Bazel-facing path: 1. Probe-armed `info!` at admit-loop entry in `dispatch_chunks_to_driver` — fires once per stream (Finding 3). 2. Burst-detect `warn!` inside `record_attempt` when window count exceeds threshold — fires at most once per 100 ms window per blob. Message: "producer-arrival burst exceeded threshold — pulse-burst signature (TraceMachina#394/TraceMachina#413 Phase 1 probe)" with fields `chunks_in_window`, `admitted_in_window` (NEW), `elapsed_window_ms`, `mpsc_capacity_remaining`, `chunks_admitted_total`, `offset`, `?digest`. 3. Rejection enrichment `warn!` inside the `Err(Full)` arm augments the existing per-blob-mpsc-full event with probe state. Message: "per-blob mpsc full at producer-arrival probe — pulse-burst vs drain-stall correlation (TraceMachina#394/TraceMachina#413 Phase 1 probe)" with fields `chunks_in_window`, `admitted_in_window` (NEW), `chunks_admitted_total`, `producer_arrival_us_since_admission`, `mpsc_capacity_remaining`, `offset`, `?stream_digest`. **Why `warn!` (not `debug!`):** workspace pins `tracing = features = ["release_max_level_info"]` (`Cargo.toml:101`) which compiles out `debug!`/`trace!` in release builds — a `debug!` probe would be dead code in production. Same rationale as the existing `back_edge_ms` probe (commit 3d43759). **Threshold = 50 attempts / 100 ms:** at the inferred 25-50 MB/s drain ceiling and 1 MiB chunks, steady-state delivers ~3-5 chunks per 100 ms window; a 50-attempt window means producer is running ≥10x faster than steady-state drain. Audit §8 predicts cascade-bound blobs cross this threshold; clean blobs stay <20. Tunable as `BURST_THRESHOLD_CHUNKS_PER_WINDOW`. NOTE: threshold-calibration follow-up (red-team Finding 2) will run a baseline-mode measurement before locking this constant. **Probe scope (Bazel-facing path only):** `dispatch_chunks_to_driver` passes `Some(&mut probe)` to `admit_prepared_chunk`; the worker-side `ChunkedWriteHandler::admit_chunk` passes `None`. The cascade hypothesis targets the chunker → driver back-edge on the Bazel-facing internal-chunking path; the worker-driven WriteChunked RPC path has a different producer (worker reads its own slow tier) and is not the cascade-prone path. **Tests (asymmetric-contract coverage per CLAUDE.md):** 1. `producer_arrival_probe_warns_on_burst_above_threshold` (UNDER-action): - Production composition: calls `admit_prepared_chunk` with `Some(&mut probe)` — the same shape `dispatch_chunks_to_driver` uses on the cascade-prone Bazel-facing path. - Drives 200 admission attempts (was 60; bump per auditor CLAIM 4 PARTIAL fix — robust against mid-loop window rollover on loaded CI). - Asserts via `logs_contain` that the bespoke marker "pulse-burst signature (TraceMachina#394/TraceMachina#413 Phase 1 probe)" appears. - Wrapped in `tokio::time::timeout(5s)` deadlock detector. - Mutation verified: setting `if false &&` on the burst-detect gate red-fails with bespoke message "Phase 1 probe never warned — burst-detect gate stripped or threshold raised". 2. `producer_arrival_probe_quiet_below_threshold` (OVER-action, NEW): - Same production composition (`admit_prepared_chunk` with `Some(&mut probe)`). - Drives 40 admissions — below the 50-attempt threshold. - Asserts via `!logs_contain(...)` that the pulse-burst marker does NOT appear (healthy streams must not produce spurious false-alarm warns). - Mutation verified: changing `> BURST_THRESHOLD_CHUNKS_PER_WINDOW` to `> 0` (always-fire) red-fails with bespoke message "Phase 1 probe fired below threshold — the burst-detect arm emitted a `warn!` after only 40 admissions". **No changes to:** `admit_prepared_chunk` body order (validation → ChunkBudget → PinBudget → try_send), error codes, retry hints, backpressure signals, gate composition. All 22 tests in `chunked_write_handler_test.rs` pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mechanical sweep demoting audited high-volume log sites. No behavior changes
to data flow, error handling, or metrics.
Behavior changes (mandatory per .claude/rules/architectural-changes.md):
WARN → debug:
buf_channel.rs:228: warn→debug, "channel backpressure (>1s wait)", 1,373×/12h
buf_channel.rs:694: warn→debug/warn (debug for <60s gap/wait; warn kept for
≥60s gap or ≥60s recv wait — previously fired at >5s), 39,424×/12h
buf_channel.rs:963: warn→debug, "Receiver dropped mid-stream", 463×/12h
stall_detector.rs:302: warn→debug (benign SuppressDumpProducerDriven branch
only; stack-dump branch unchanged), 3,049×/12h
proto_stream_utils.rs:87: warn→debug, "GrpcStore::write made no progress", 477×/12h
proto_stream_utils.rs:182: warn→debug, "read path made no progress", 122×/12h
bytestream_server.rs:616: warn→debug, "slow read completion (>=5s, <1MB/s)", 153×/12h
bytestream_server.rs:2148: warn→debug, "mirror channel closed, dropping mirror", 433×/12h
grpc_store.rs:1788: warn→debug, "GrpcStore::write succeeded but took > 60s", 410×/12h
fast_slow_store.rs:5525: warn→debug, "background slow write task was delayed
before starting" (streaming update path), 337×/12h (both paths combined)
fast_slow_store.rs:5874: warn→debug, "update_oneshot: background slow write
task was delayed before starting"
fast_slow_store.rs:4913,4939,4963: warn→debug, "slow lock acquire" (3 sites,
has_with_results local_only_reads: in_flight/chunked/mirror), 65×/12h
fast_slow_store.rs:4998,5035,5065: warn→debug, "slow lock acquire" (3 sites,
has_with_results server: in_flight/chunked/mirror)
filesystem_store.rs:1229: warn→debug, "update_file slow phases (>100ms)", 67×/12h
filesystem_store.rs:2162: warn→debug, "update slow write (>100ms)", 115×/12h
cas_server.rs:1011: warn→debug, "GetTree: slow BFS level (>100ms)", 85×/12h
fast_slow_store.rs:4918-ish: see slow lock acquire above
streaming_blob.rs:1323: warn→debug, "streaming blob reader slow notify wakeup", 39×/12h
api_worker_scheduler.rs:3756: warn→debug, "tree resolution slow directory fetch", 69×/12h
WARN → trace:
fs.rs:571: warn→trace, "read_file_to_channel: slow read syscall (>100ms)", 311×/12h
INFO → debug:
grpc_store.rs:1445: info→debug, "TraceMachina#59 GrpcStore::write: starting ByteStream write"
grpc_store.rs:1673: info→debug, "TraceMachina#59 GrpcStore::write: RPC returned Ok (normal success)"
grpc_store.rs:1777: info→debug, "TraceMachina#59 GrpcStore::write: completed successfully"; combined ~464K/12h
bytestream_server.rs:660: info→debug, "ByteStream::read: CAS read completed", ~243K/12h
api_worker_scheduler.rs:1931: info→debug, "scoring: pre-match state", ~207K/12h
bytestream_server.rs:3092: info→debug, "TraceMachina#62 ByteStream::Write: already-exists short-circuit", ~155K/12h
chunked_write_handler.rs:1013: info→debug, "WriteChunked RPC entry"
chunked_write_handler.rs:1317: info→debug, "in_flight handler-local entry inserted (write_chunked_inner)"
chunked_write_handler.rs:2901: info→debug, "chunked dispatch: blob committed (reaper)"
chunked_write_handler.rs:3072: info→debug, "in_flight handler-local entry inserted (dispatch_chunks_to_driver)"
chunked_write_handler.rs:3119: info→debug, "producer-arrival probe armed (TraceMachina#394/TraceMachina#413 Phase 1)"
chunked_write_handler.rs:3827: info→debug, "chunked_in_flight registered"
chunked_write_handler.rs:3877: info→debug, "chunked_in_flight guard disarmed"
chunked_write_handler.rs:3934: info→debug, "chunked_in_flight removed"; combined ~120K/12h
chunked_write_handler.rs:1517: "WriteChunked: blob committed" KEPT at info (terminal state transition)
fast_slow_store.rs:1854: info→debug, "FastSlowStore::update (chunked): admission accepted", ~54K/12h
fast_slow_store.rs:2093: info→debug, "FastSlowStore::update (chunked): commit complete"
fast_slow_store.rs:1830: info→debug, "data_stream_fut step", ~16K/12h
existence_cache_store.rs:848: info→debug, "returning NotFound to caller", ~17K/12h
cas_server.rs:1067: info→debug, "GetTree: resolved directory tree", ~16K/12h
api_worker_scheduler.rs:2384: info→debug, "inline tree resolution complete, caching"
api_worker_scheduler.rs:2396: info→debug, "tree cache byte-bounded eviction"
api_worker_scheduler.rs:2643: info→debug, "prefetch: spawning batched push"
api_worker_scheduler.rs:2827: info→debug, "prefetch: completed batched push"; combined ~51K/12h
api_worker_scheduler.rs:1263: info→debug, "phase6 scheduler dispatching StartAction"
worker_api_server.rs:1493: info→debug, "phase6 scheduler received action-N completion"
NOT touched (kept at warn per spec):
bytestream_server.rs:841 "TraceMachina#418: discarding corrupt StreamState" — KEEP warn
proto_stream_utils.rs aggregate warn (rate-limited burst warn) — KEEP warn
chunked per-chunk back-edge lines (TraceMachina#47 active) — KEEP warn
redis_store slow-pipeline/slow-command warns — KEEP warn
No tests required for pure level changes.
cargo check -p nativelink-util -p nativelink-store -p nativelink-service -p nativelink-scheduler — clean (exit 0). Log: /tmp/logsweep-check1.log.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… (observability-only) The ~9 stuck large CAS blobs (ead6c5ce-62303904, 647ad175-44673680, a79ccb25-59958352, …) loop forever on the Bazel-facing internal-chunking backfill, failing in ~100–660 ms with `"buf_channel: writer dropped without commit : bazel-facing internal-chunking: reader.recv()"`. The server logged NOTHING per-digest for them — the reject returns the Status before the per-digest log path runs — so the root branch was DARK while 38,544 other blobs committed. Per CLAUDE.md "instrument first, theorize second" (three prior root-cause hypotheses already FALSIFIED this session), this ships the probe instead of a 4th guess. WHAT (three probe sites, all observability-only): - (a) `dispatch_bazel_facing_internal_chunking` early-dedup bounded-drain failure (`chunked_write_handler.rs`): a reject that fires BEFORE `dispatch_chunks_to_driver` arms the TraceMachina#394/TraceMachina#413 producer-arrival probe. Bumps `chunked_reject_pre_probe` + rate-limited `warn!` tagging digest + branch. - (b) `build_bazel_chunk_stream` FIRST `reader.recv()` Err (`chunked_write_handler.rs`) — the EXACT stuck-loop signature. Bumps `chunked_reject_at_offset0` (producer sent zero chunks; `bytes_received == 0`) or `chunked_reject_midstream` (dropped after ≥1 chunk) + rate-limited `warn!` tagging digest + `bytes_received`/`chunks_received` + branch. The offset0:midstream ratio discriminates "worker sends nothing" from "worker drops partway". - (c) worker `WorkerApiWriteChunkedDispatcher::dispatch` (`chunked_client.rs:201`): tags the failing attempt `reject_origin = channel_acquire` (worker-side; RPC never reached the server / stuck client state) vs `rpc_returned_error` (RPC reached the transport, error came back). This is the worker-side half of the open fork sites (a)/(b) answer. RATE LIMIT: every `warn!` is gated by the EXACT idiom shipped for the QUIC read-failure diagnostic in `ccab2243` (`quic_read_fail_should_emit`): a pure monotonic-nanos predicate over an `AtomicU64` last-emit, single-flighted by a CAS, carrying `suppressed_since_last`. ≲1/s per site, so the ~30 s loop cannot reproduce the 46k-line spam class the `dafcf8b6`/`cbc2e595` demotions retired. `warn!` (not `debug!`/`trace!`) survives `release_max_level_info` in the prod binary. Each site owns its own gate so a burst at one does not silence another. COUNTERS: `chunked_reject_pre_probe`, `chunked_reject_at_offset0`, `chunked_reject_midstream` — additive `#[metric] AtomicU64` on the existing `ChunkedWriteHandlerMetrics` derive (`// CAPPED AT 1` each). Their ratio IS the discriminator. Invariant being violated: every reproducible CAS-write failure must be attributable to a specific code branch from logs alone. Mechanism that violates it: the reject path (`build_bazel_chunk_stream` first-recv Err; early-dedup bounded-drain Err) returns/yields the Error without a digest-tagged log, so the stuck loop is un-diagnosable per-digest. Mechanism that re-establishes it after the fix: the three probes tag digest + branch at warn! level (release-surviving), rate-limited so they cannot themselves become a log-volume incident. Composite invariants this fix interacts with: NONE behaviorally — no pin/eviction/ack/admission/retry/routing touch; the diagnostic is purely additive observation called on the existing Err paths. The rate-limit gate prevents a log-volume regression (the only way an observability change can harm prod). Test that proves the fix re-establishes the invariant: `chunked_reject_diagnostic_counts_offset0_drop_and_logs_digest` — drives `dispatch_bazel_facing_internal_chunking` (production composition) with the producer dropped at offset 0 (tx dropped without bytes/EOF → the exact `writer dropped without commit : reader.recv()` signature), asserts `chunked_reject_at_offset0 == 1`, `chunked_reject_midstream == 0`, and that the captured `warn!` carries the digest. Mutation: commenting the `chunked_reject_at_offset0.fetch_add` red-fails with "offset-0 reject counter MUST increment …" (left 0, right 1); stripping `%digest` from the `warn!` red-fails the warn-carries-digest assertion. Behavior changes: none. Every runtime path returns/yields the SAME Result it did before; the only added runtime effects are (1) three monotonically- increasing counters and (2) the rate-limited warn!s, both inert until a chunked write actually rejects. Verified against production composition: - Bazel-facing internal chunking (`FastSlowStore::update` → BazelChunked- DispatcherImpl → dispatch_bazel_facing_internal_chunking): the failing path. site (a) tags early-dedup drain rejects; site (b) tags first-recv rejects. `build_bazel_chunk_stream` now threads the `metrics` Arc through its `unfold` `State` (no per-chunk Arc clone on the hot path) — the chunk stream it yields is byte-identical to before. - worker v1 WriteChunked dispatch (GrpcStore → WorkerApiWriteChunkedDispatcher ::dispatch): the `factory().await?` and `map_err` legs propagate the SAME errors; site (c) only tags + logs before propagation. The v2 dispatcher (`WorkerApiWriteChunkedV2Dispatcher`) is untouched. - early-dedup hit, stalled-producer, oversized-producer, digest-mismatch, and all 16 pre-existing bazel_facing tests pass unchanged. Internal-RPC policy: no per-RPC timeout/deadline added; no buffering field added beyond single `AtomicU64`s (each `// CAPPED AT 1`). No fsync/sync-write primitive. Bench: exempt — observability-only on a data-plane file; per .claude/rules/data-plane-bench.md the bench is exempt for trace-only changes with no production-code behavior delta. Tests (timeout-wrapped, teed to /tmp/chunkdiag-*.log): - bazel_facing_internal_chunking_test: 17 passed (16 prior + 1 new). - chunked::chunked_client unit tests: 14 passed (13 prior + 1 new gate test). - writer_site_logs_test: 5 passed (handler-touch regression). - mutation-verified: offset-0 counter bump (bespoke red); warn! digest strip (bespoke red); chunked_client gate `last_emit == 0` guard flip (bespoke red). DESIGN-DRIFT FLAGGED (does not block the diagnostic): the spec premise "the server logs nothing per-digest, not even `producer-arrival probe armed`" is inconsistent with the code path. The failing error string originates ONLY at `build_bazel_chunk_stream` first-recv, which is reached via `dispatch_chunks_to_driver`'s admit loop — AFTER that fn arms the producer-arrival `info!` (`chunked_write_handler.rs:~3119`). So a digest that reaches the `reader.recv()` reject MUST already have emitted the armed-probe info!. Either that info! is being lost for another reason (target filtering, or the reject races the emit) or the operator scan missed it. The diagnostic is placed correctly regardless; site (b) is the authoritative per-digest tag for this failure. Surfaced for the parent to reconcile against the live logs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Our previous build layout was fairly custom and hard to maintain.
Migrate to a more standards-conform layout that is compatible with good
Cargo and Bazel practices and feels natural to develop in.
This commit swaps the previous "one-crate-per-file" layout to a layout
where we have a few medium sized crates that are just fine-grained
enough to allow almost the same amount of parallelization as the
previous fine-grained layout.
The new layout enables dependency auto-updaters, standard cargo tooling
and doc autogeneration. This should improve DX quite a bit and gives us
some much needed compatibility with nix/cargo interop and should make
most standard cargo extensions compatible with the repository.
Some notable changes to the file layout:
errorcrate is no longer part ofutils. It is now astandalone module. This was necessary to keep macro exports compatible
with the Cargo build.
scheduler.rsfile is now split intoaction_scheduler.rsand
worker_scheduler.rssince we don't want imports likenative_link_scheduler::scheduler.dependencies.
native_link_utils, as it was usedvirtually everywhere. Moving this up in the dependency graph allows
for higher parallelization of the other crates.
Added documentation targets for every crate (except the
casbinaryitself, as this will require a dedicated commit). These doc targets can
be viewed by pointing a browser to
bazel-bin/thetarget.rustdoc/thetarget/index.html.Added rustfmt configuration to standardize imports. We now use
crate::syntax which plays well with rusfmts's import formatting. The new layout
makes it easier to spot crate/directory interdependencies so the we can
remove those in future commits.
This change is