Repository navigation
Add ability to use env variables in config files - #88
Merged
Merged
Conversation
This will make it much easier to use the same config with slightly different configs. Also adds a few useful debug tips on errors.
allada
commented
Jun 17, 2022
allada
left a comment
Contributor
Author
There was a problem hiding this comment.
Reviewed 16 of 16 files at r1, all commit messages.
Reviewable status:complete! all files reviewed, all discussions resolved (waiting on @allada)
rejuvenile
added a commit
to rejuvenile/nativelink
that referenced
this pull request
Aug 19, 2026
Server-side proxy-read opportunistic coalescer for small blobs. Mirrors the SmallBlobDispatcher in spirit (per-target queue, zero-window coalesce via tokio::task::yield_now, single-flight dedup), adapted for the read direction (oneshot reply per slot, per-digest result fanout). Wire protocol decision: REUSE REAPI BatchReadBlobs against the worker's CAS endpoint. The worker's cas_server::inner_batch_read_blobs already handles this RPC (no proto change). Justified over a custom proto in the module doc. Threshold: SMALL_BLOB_THRESHOLD = 16 KiB, shared with small_blob_dispatcher. Eligibility helper rejects partial reads + above-threshold blobs. Coalesce window: 0ms (one tokio::task::yield_now()), matching the existing grpc_store batch_flush_loop pattern. Tests + mutation results (all PASS, all mutations RED-FAILED): - is_eligible_rejects_above_threshold - is_eligible_rejects_nonzero_offset - is_eligible_accepts_full_read_with_permissive_length - five_concurrent_requests_become_one_batch (asserts 5 reqs -> 1 batch) - same_digest_concurrent_requests_dedup (asserts dedup works, mutated `is_first = true` -> RED-FAILED with specific assertion msg) - partial_failure_response_surfaces_per_digest_errors (mutated per-digest result distribution -> RED-FAILED with specific msg) - above_threshold_blob_is_not_eligible_for_batch Wire-up to WorkerProxyStore + production-composition test follow in Phase 2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rejuvenile
added a commit
to rejuvenile/nativelink
that referenced
this pull request
Aug 19, 2026
Adds an opt-in fast path that routes eligible small-blob reads through the BatchReadCoalescer instead of issuing per-blob ByteStream Read RPCs. WorkerProxyStore changes: - New `batch_small_blob_reads: AtomicBool` (default OFF) + `batch_read_coalescer: OnceLock<Arc<BatchReadCoalescer>>` field. - `enable_batch_small_blob_reads()` / `disable_batch_small_blob_reads()` / `batch_small_blob_reads_enabled()` setters/inspector mirroring the existing `enable_race_peers` / `enable_locality_in_has` pattern. - `init_batch_read_coalescer(self_arc)` bootstraps the coalescer with a Weak<Self> capture so its BatchFn can lazily look up per-endpoint GrpcStore connections. Idempotent. - `try_batched_read_from_endpoint(endpoint, digest, writer)`: helper that submits to the coalescer, then forwards bytes to the caller's writer (send + send_eof) and tees to the inner store via a fire-and-forget tokio::spawn. On Err, the writer is left untouched (per-peer-attempt convention preserved — caller falls through to the streaming get_part_and_cache path). - BatchFn (production): obtains GrpcStore via downcast_ref, builds a REAPI BatchReadBlobsRequest, parses per-digest responses (with the same length-mismatch corruption guard used by the worker-side validate_batch_read_responses). - BatchFn (test injection): when the connected store is NOT a GrpcStore (e.g. a MemoryStore in unit tests), falls back to per-blob `get_part_unchunked` so tests can exercise the coalescer end-to-end without a real gRPC server. - Inserted batched fast path inside `try_read_from_endpoints` AND `try_read_from_worker`. Eligibility checks: flag on, original caller offset/length unchanged (no resume-from-mid-stream interference), `BatchReadCoalescer::is_eligible(digest, offset, length)` true. On batched-Err, eviction policy mirrors the streaming path (NotFound / DataLoss only); fall through to get_part_and_cache. - New helper `code_from_grpc_status(i32) -> Code` mapping google.rpc.Status codes to nativelink_error::Code. src/bin/nativelink.rs: - Call `proxy_arc.init_batch_read_coalescer()` in the WorkerProxyStore wrapping loop so the coalescer is ready when the operator flips the kill-switch. Existing 42 worker_proxy_store_test cases continue to pass (default flag is OFF, so the new path is dormant in those tests). Production-composition test (per CLAUDE.md feedback_writer_termination_class) follows in Phase 3. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rejuvenile
added a commit
to rejuvenile/nativelink
that referenced
this pull request
Aug 19, 2026
…xy reads Adds nativelink-store/tests/worker_proxy_batch_read_test.rs: 6 tests wrapping VerifyStore -> WorkerProxyStore (the production cas_STORE chain) and asserting the writer-termination contract is preserved on the batched fast path. Per CLAUDE.md feedback_writer_termination_class_2026_04_25 + the asymmetric-coverage rule (under-action AND over-action both tested): UNDER-action (writer not terminated when it should be): - batched_path_terminates_writer_on_ok_under_verify_store Mutation: comment out `writer.send_eof()` in try_batched_read_from_endpoint -> RED-FAILED with the assertion msg "Sender dropped before sending EOF" (verified). OVER-action (writer terminated when wrapper still wants it): - batched_path_does_not_terminate_writer_on_err_under_verify_store Mutation: add `writer.send_error(e.clone())` to the Err path of try_batched_read_from_endpoint -> RED-FAILED with "Tried to send while stream is closed" surfacing through the streaming fallback to peer B (verified). Eligibility coverage: - above_threshold_blob_takes_streaming_path_under_verify_store (32 KiB blob, flag ON, must take streaming path) Concurrency coverage: - five_concurrent_small_reads_coalesce_into_one_batch_under_verify_store (5 same-endpoint distinct digests; correctness check, not a precise batch-count assertion — that's owned by the unit test in the coalescer module against a controlled BatchFn fake) - three_concurrent_same_digest_reads_succeed_under_verify_store (single-flight dedup smoke under VerifyStore) - delayed_peer_batched_read_holds_writer_open_under_verify_store (DelayedPeerStore fixture with 150ms delay; verifies the writer stays open across the async boundary) Every test wraps `outer.get_part_unchunked` in a 5s `tokio::time::timeout` per the deadlock-detector rule. All 6 tests PASS post-restore. All other nativelink-store tests continue to pass (failpoints test suite needs `--features failpoints`, unchanged from baseline; one filesystem_store_test is a pre-existing flake unrelated to this diff). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rejuvenile
added a commit
to rejuvenile/nativelink
that referenced
this pull request
Aug 19, 2026
Address 4 BLOCKERS + 3 MAJORS from the post-rebase reviewer pass on TraceMachina#88 (batch_read_small_blobs racing wastes bandwidth): B1 (code-reviewer + perf-optimizer): IS_WORKER_REQUEST not propagated across the spawned drainer's BatchFn. tokio::spawn does NOT inherit tokio::task_local! storage; the drainer's batch_read_blobs RPC was called WITHOUT IS_WORKER_REQUEST=true, so the receiving worker did not enter responder mode (loop-terminator invariant: "workers NEVER chain externally" enforced at depth 1). Fix: capture the flag at submit() time, ferry it via PendingRead, re-establish via IS_WORKER_REQUEST.scope(...) around the BatchFn invocation in the drainer. Regression test drainer_propagates_is_worker_request_across_spawn observes the flag inside the BatchFn fake — mutation-verified. B2 (perf-optimizer): DigestHasherFunc context lost across the spawn for the same reason as B1. Hard-coded SHA-256 broke non-SHA-256 deployments (every batch returned InvalidArgument from the worker side + silent fallback). Fix: capture the hasher from OTel Context at submit() time, pass it as a 3rd arg to BatchFn so the outgoing BatchReadBlobsRequest.digest_function field matches the caller's hasher. Regression test drainer_propagates_digest_hasher_across_spawn — mutation-verified. B3 (perf-optimizer): redirect-path try_read_from_endpoints was missing the bytes_before_proxy == writer.get_bytes_written() guard that the streaming-path try_read_from_worker callsite already had. Endpoint A streaming partial-write followed by endpoint B batched whole-blob send would silently corrupt the consumer-visible stream (partial_A_bytes ++ full_B_bytes). Fix: capture bytes_before_proxy once before the endpoints loop; gate the batched fast-path on writer.get_bytes_written() == bytes_before_proxy per the streaming- path pattern. Regression test redirect_path_skips_batched_after_partial_write_to_writer uses a PartialThenErrorPeer fixture for endpoint A + a healthy MemoryStore for endpoint B; asserts batches_dispatched <= 1 (at most A's failed attempt, NEVER B's). Mutation-verified. B4 (testing-czar): 7 unit tests in batch_read_coalescer.rs used #[tokio::test] instead of CLAUDE.md-mandated #[nativelink_test]. Converted all 7. M1 (code-reviewer): submit() took Arc<str>, allocating one fresh Arc per submission (defeats the perf benefit of coalescing). Now takes &str; allocates Arc<str> only on the insert-miss path inside submit when a new per-endpoint queue is created. Caller updated. M2 (rust-crate-reviewer): replace 17-line hand-rolled code_from_grpc_status with tonic::Code::from_i32 (re-exported as nativelink_error::Code). Old fn returned Internal for unknowns; from_i32 returns Unknown — both pass should_try_peers, semantically equivalent for the caller's fallback. NOTE comment added. M1 (rust-crate-reviewer): import order — moved opentelemetry::context out of the nativelink_util::* block; subsequently fully removed since the OTel Context is no longer read directly in worker_proxy_store (batch_digest_function is now passed as an argument to BatchFn). Mutation steps verified for B1, B2, B3 individually: - B1: comment out IS_WORKER_REQUEST.scope wrapper in drainer → drainer_propagates_is_worker_request_across_spawn FAILED - B2: replace batch_digest_function with default_digest_hasher_func() in BatchFn call → drainer_propagates_digest_hasher_across_spawn FAILED - B3: remove bytes_before_proxy guard at redirect path → redirect_path_skips_batched_after_partial_write_to_writer FAILED Deferred per task instructions (separate trackers): - testing-czar M1-M4 coverage gaps (init-not-called, kill-switch flip mid-flight, 16 KiB boundary, partial-read offset/length) - rust-crate L1-L8 polish (MetricsComponent, new_cyclic, comment cleanup) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rejuvenile
added a commit
to rejuvenile/nativelink
that referenced
this pull request
Aug 19, 2026
…_blob_mirror (TraceMachina#168) Per operator authorization 2026-05-05, promote two production gates from default-OFF to ON at server startup: - TraceMachina#88 (BatchReadBlobs coalescing): call `WorkerProxyStore::enable_batch_small_blob_reads()` immediately after `init_batch_read_coalescer()` on every CAS proxy wrap. Concurrent same-target small-blob server→worker proxy reads now collapse into one BatchReadBlobs RPC per coalesce window per endpoint instead of N individual ByteStream Read RPCs. - TraceMachina#168 (small-CAS peer mirror): construct `SmallBlobDispatcherConfig` with `small_blob_mirror_enabled: true` instead of `::default()` (which had it false). Promotes the dispatcher's `enqueue` from inert no-op to live mirror-push. The dispatcher remains gated on per-store pin-set registration below, so stores without a FastSlowStore backing are still skipped. Pre-existing flake `test_execute_dependent_actions_with_peer_sharing` also fails on origin/main HEAD (verified) — unrelated to this change.
rejuvenile
added a commit
to rejuvenile/nativelink
that referenced
this pull request
Aug 19, 2026
…rt race Adds `race_peer_win_total` / `race_server_win_total` counters to WorkerProxyStore so the worker-side server-vs-peer `get_part` race's effectiveness is readable in production. The win/loss outcome was previously ONLY at `debug!`, which `release_max_level_info` compiles out of the prod binary — the race ran dark. SCOPE NOTE / design-drift (reported to dispatcher): the brainstorm's TraceMachina#3 premise — "the LARGE branch ... does NOT race fastest-of {server, peer-A, peer-B}" — is largely FALSE against current code. The large input path (download_to_directory large branch -> populate_fast_store_unchecked -> copy_slow_to_fast -> run_producer -> slow_store.get -> WorkerProxyStore::get_part) ALREADY races the inner CAS against peers[0] with PROMPT loser cancellation (`cancel_loser_racer`: cooperative rx-drop + 50ms LOSER_GRACE + abort fallback) — landed in `7457436d`, enabled in prod at `local_worker.rs:4733` (`enable_race_peers`). The `TraceMachina#88` bandwidth-doubling/loser-abort concern the dispatch flagged is therefore ALREADY solved. The only genuine residual gaps are (a) it races a SINGLE peer (peers[0]), not multiple peers, and (b) no load-spreading across peers for the large class (unlike the small path's round-robin). Both are an ARCHITECTURAL extension to an already-tuned race (the RST-budget note at `cancel_loser_racer` documents why parallel_chunk_count was cut 64->16 to stay under hyper's reset budget) — re-implementing/multiplying the race needs sign-off, so this commit ships ONLY the missing observability the dispatch explicitly asked for and flags the rest. Invariant being violated (pre-fix): the win-rate of the production server-vs-peer race is not observable in the release binary (debug!-only), so an operator cannot tell whether peer-fetch is offloading the central CAS or whether the race is pure overhead (near-zero peer wins) — and cannot size the bandwidth-doubling cost. Mechanism that violated it: the 4 winner-naming branches in `WorkerProxyStore::get_part`'s `tokio::select!` and the two fallback helpers only emitted `debug!`, compiled out under release_max_level_info. Mechanism that re-establishes it: two `CounterWithTime` fields incremented at EVERY serving point — the 2 primary data branches + 2 zero-length branches in the select (`get_part`), and the success points of `await_peer_after_empty_server` / `await_server_after_empty_peer` (now `&self` methods). "Who ultimately served the bytes" => complete, path-independent win-rate. Published via the existing `publish!` block in `MetricsComponent::publish` (so these ARE on /metrics, unlike the scheduler-side prefetch counters). Composite invariants this fix interacts with: none structurally — counting is side-effect-only on existing branches. The two helpers changed from associated fns to `&self` methods (4 call sites in `get_part`, all updated); no signature or behavior change beyond the `inc()`. No change to racing, loser-cancellation, stale-positive eviction, writer-termination, ack semantics, or store-chain shape. Test that proves the fix: test_race_peer_win_increments_peer_counter (inner empty -> peer serves -> peer counter +1, server counter unchanged) and test_race_server_win_increments_server_counter (peer stale-positive empty -> server serves via fallback -> server counter +1, peer counter unchanged). Both in production composition (race_peers=ON, INITIATOR mode). TDD fail->pass->mutate verified: commenting all peer inc sites red-fails the peer test with "peer win must increment race_peer_win_total (was 0, now 0)"; same for server. Behavior changes: - WorkerProxyStore (worker-side, race_peers=ON; prod composition wrapping the central-CAS GrpcStore as FastSlowStore.slow): on every raced get_part, ALSO increments race_peer_win_total or race_server_win_total. Prior: debug!-only. New: two registered counters. No runtime path/timing/ack change. Server-side WorkerProxyStore (race_peers=OFF) never enters the race branches, so the counters stay zero there — unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This will make it much easier to use the same config with slightly
different configs.
Also adds a few useful debug tips on errors.
This change is