feat(deps): consume dunite PSA-36 (single-flight core + download Transport error class) (BUNYIP-44) #50

Merged
nrupard merged 2 commits from feat/bunyip-44-dunite-psa-36 into main 2026-06-03 15:44:12 +02:00
Owner

Closes BUNYIP-44. Consumes dunite PSA-36 (dunite PR #5), bringing bunyip from the PSA-35 pin (269ee30b) up to dunite main a35e84aa.

What the dunite update brings

  • dunite_core::services::SingleFlight<K, T, E>: a shared keyed single-flight executor. Both vertical caches (dunite-oci blob_cache, dunite-download download_cache) now run their fetches through it instead of each hand-rolling the mechanism. Failures are never cached; concurrent callers share the leader's result; removal is guarded by cell identity (Arc::ptr_eq) so a lagging waiter cannot evict a newer cohort's in-flight cell, and the guard runs on panic so a panicking fetch cannot leak a map entry.
  • DownloadCacheError::Transport: dunite-download's error type is now total and class-preserving. Transport failures (connection refused, DNS, TLS, timeout, mid-stream drop) previously collapsed into Io and round-tripped back misclassified, so a binary download against an unreachable Forgejo looked like a local filesystem error. This is the dunite-download twin of the dunite-oci bug PSA-35 fixed.
  • tokio feature narrowing: dunite-core advertises only tokio/sync; verticals enable what they need additively.

Consumer-side change

bunyip-api/src/handlers/download.rs previously mapped every DownloadCacheError to a blanket AppError::upstream (502), so a genuine local Io/Store fault was reported as an upstream outage. It now classifies:

Variant Result
Forgejo, Transport, ShaMismatch, SizeMismatch 502 upstream (integrity mismatches keep their dedicated warn)
Io, Store 500 internal, with a distinct "local failure, not an upstream outage" error log

This mirrors the three-way classification dunite-oci's From<&BlobCacheError> for OciError already does on the registry side. No API breaks: the workspace builds clean against the reworked BlobCache internals.

Verification (rust-builder-glibc 1.94.1 container + live dev stack)

  • cargo build --workspace --all-targets: clean (no API breaks from the BlobCache/SingleFlight rework)
  • cargo clippy --workspace --all-targets -- -D warnings: clean
  • cargo fmt --all --check: clean
  • cargo test --workspace --lib: 206 passed, 0 failed
  • just verify-oci against the live dev stack: full registry matrix PASS including the blob-cache second pull, which exercises the reworked dunite-oci BlobCache + the shared SingleFlight end to end.

E2E note

The dead-upstream binary-download path (which would directly exercise the new DownloadCacheError::Transport -> 502 classification) is not e2e-verified here for the same reason BUNYIP-35 flagged: there is no live release-backed binary product in the dev environment to pull a cache-miss asset from. The classification is covered by dunite's own regression test (upstream_unreachable_surfaces_as_transport_not_io), by the compile against the total error type, and by the handler matches! change. The OCI blob path, which does have a live product, is the proxy for the single-flight rework and passes.

  • dunite PSA-36 (PR #5): the changes consumed.
  • PSA-35 / bunyip PR #38 commit d8771c0: the previous consumer update this mirrors.
Closes BUNYIP-44. Consumes dunite PSA-36 (dunite PR #5), bringing bunyip from the PSA-35 pin (269ee30b) up to dunite main a35e84aa. ## What the dunite update brings - **`dunite_core::services::SingleFlight<K, T, E>`**: a shared keyed single-flight executor. Both vertical caches (dunite-oci `blob_cache`, dunite-download `download_cache`) now run their fetches through it instead of each hand-rolling the mechanism. Failures are never cached; concurrent callers share the leader's result; removal is guarded by cell identity (`Arc::ptr_eq`) so a lagging waiter cannot evict a newer cohort's in-flight cell, and the guard runs on panic so a panicking fetch cannot leak a map entry. - **`DownloadCacheError::Transport`**: dunite-download's error type is now total and class-preserving. Transport failures (connection refused, DNS, TLS, timeout, mid-stream drop) previously collapsed into `Io` and round-tripped back misclassified, so a binary download against an unreachable Forgejo looked like a local filesystem error. This is the dunite-download twin of the dunite-oci bug PSA-35 fixed. - **tokio feature narrowing**: dunite-core advertises only `tokio/sync`; verticals enable what they need additively. ## Consumer-side change `bunyip-api/src/handlers/download.rs` previously mapped every `DownloadCacheError` to a blanket `AppError::upstream` (502), so a genuine local Io/Store fault was reported as an upstream outage. It now classifies: | Variant | Result | |---------|--------| | `Forgejo`, `Transport`, `ShaMismatch`, `SizeMismatch` | 502 upstream (integrity mismatches keep their dedicated warn) | | `Io`, `Store` | 500 internal, with a distinct "local failure, not an upstream outage" error log | This mirrors the three-way classification dunite-oci's `From<&BlobCacheError> for OciError` already does on the registry side. No API breaks: the workspace builds clean against the reworked `BlobCache` internals. ## Verification (rust-builder-glibc 1.94.1 container + live dev stack) - `cargo build --workspace --all-targets`: clean (no API breaks from the BlobCache/SingleFlight rework) - `cargo clippy --workspace --all-targets -- -D warnings`: clean - `cargo fmt --all --check`: clean - `cargo test --workspace --lib`: 206 passed, 0 failed - `just verify-oci` against the live dev stack: full registry matrix PASS including the **blob-cache second pull**, which exercises the reworked dunite-oci `BlobCache` + the shared `SingleFlight` end to end. ## E2E note The dead-upstream binary-download path (which would directly exercise the new `DownloadCacheError::Transport` -> 502 classification) is not e2e-verified here for the same reason BUNYIP-35 flagged: there is no live release-backed binary product in the dev environment to pull a cache-miss asset from. The classification is covered by dunite's own regression test (`upstream_unreachable_surfaces_as_transport_not_io`), by the compile against the total error type, and by the handler `matches!` change. The OCI blob path, which does have a live product, is the proxy for the single-flight rework and passes. ## Related - dunite PSA-36 (PR #5): the changes consumed. - PSA-35 / bunyip PR #38 commit d8771c0: the previous consumer update this mirrors.
feat(deps): consume dunite PSA-36 (single-flight core + download Transport error class)
All checks were successful
Check / fmt / clippy / build / test (pull_request) Successful in 1m12s
8e4d08e326
Bump the four dunite git dependencies from 269ee30b (PSA-35) to dunite main a35e84aa (PSA-36, dunite PR #5).

What the update brings:
- dunite_core::services::SingleFlight: a shared keyed single-flight executor that both vertical caches (dunite-oci blob_cache, dunite-download download_cache) now run their fetches through, replacing the hand-duplicated per-vertical mechanism. Failures are never cached; removal is guarded by cell identity so a lagging waiter cannot evict a newer cohort's in-flight cell.
- dunite-download gained a total, class-preserving error type: the new DownloadCacheError::Transport variant carries upstream transport failures (connection refused, DNS, TLS, timeout, mid-stream drop) that previously round-tripped as Io and misclassified upstream outages as local filesystem errors.
- dunite-core narrows its tokio feature surface to ["sync"]; verticals enable what they need additively.

Consumer-side change: the member-download handler now classifies the cache error instead of returning a blanket 502. Forgejo/Transport outages and integrity mismatches stay upstream-class (502); Io/Store faults are local and return 500 with a distinct log line, so a genuine filesystem/store fault no longer falsely blames the upstream. No API breaks (workspace builds clean against the new BlobCache internals).

Verified in the rust-builder 1.94.1 container: clippy -D warnings clean, fmt clean, 206 workspace lib tests pass. Live dev stack: just verify-oci passes the full registry matrix including the blob-cache second pull, exercising the reworked dunite-oci BlobCache + shared SingleFlight end to end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: nrupard <natrsmith11@gmail.com>
fix(download): map upstream NotFound to 404 and classify with an exhaustive match (PR #50 review)
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
Check / fmt / clippy / build / test (pull_request) Successful in 4m20s
d407788718
- DownloadCacheError::Forgejo(ForgejoError::NotFound) now returns 404 (no upstream-failure audit), matching every other missing-asset path. dunite-download keeps NotFound as a typed variant precisely so consumers can do this; the previous classification lumped it into the upstream bucket and returned a retryable 502 for a permanent not-found (deleted/renamed release or asset), masking the real cause and mislabelling the audit log.
- Replace the non-exhaustive matches!-based is_upstream test with an exhaustive `match &e`, so a future DownloadCacheError variant forces a classification decision here at compile time instead of silently defaulting into the 500 bucket. Behaviour is otherwise unchanged: Forgejo/Transport and integrity mismatches stay 502, Io/Store stay 500 with the distinct local-failure log.
- Filed PSA-38 to move this classification into dunite-download itself (parity with dunite-oci's From<&BlobCacheError>); referenced in a code comment.

Verified: clippy -D warnings clean, fmt clean, 206 workspace lib tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: nrupard <natrsmith11@gmail.com>
nrupard deleted branch feat/bunyip-44-dunite-psa-36 2026-06-03 15:44:12 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
psa-systems/bunyip!50
No description provided.