refactor(core): shared single-flight primitive; fix dunite-download lossy error mirror (PSA-36) #5
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "refactor/psa-36-single-flight-core"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Implements PSA-36: the follow-up from PSA-35's code review.
1. Shared single-flight primitive in dunite-core
New
dunite_core::services::single_flight::SingleFlight<K, T, E>: a keyed single-flight executor (map +OnceCell+ insert/remove locking). The first caller for a key runs the operation; concurrent callers await the same cell and receive a clone of the result; the key is removed on completion so failures are never cached. Both vertical caches (dunite-ociblob_cache, dunite-downloaddownload_cache) now use it instead of each hand-rolling the same map/cell/locking pattern (architecture rule: cross-vertical mechanism goes DOWN into core).2. dunite-download lossy-mirror bug fix (same bug PSA-35 fixed in dunite-oci)
ForgejoError::Http(Forgejo unreachable: connection refused, DNS, TLS, timeout) collapsed into the mirror'sOther(String)catch-all and round-tripped back asDownloadCacheError::Io, so a binary download against a down Forgejo was misclassified as a local filesystem error (HTTP 500) instead of an upstream failure (502). The mirror is now total in both directions: new publicDownloadCacheError::Transportvariant, dedicatedIo/Storearms, no catch-all. Mid-stream body errors are alsoTransportnow. The compiler now enforces totality: a future variant added to either error enum is a compile error until its classification is chosen.3. Cleanups from the PSA-35 review
dunite-oci/tests/limiter.rsdeduped: engine behavior is covered by dunite-core's own tests; only the slot-leak regression test (consumer-facing API surface) remains.lib.rs: core names (LimitDenial) are canonical; dunite-oci's full aliasing is backward compatibility for consumers written against its pre-PSA-35 API. Renaming was rejected because bunyip matchesLimitDenial::Concurrency/DailyCapdirectly and a rename breaks consumers for cosmetics.single_flightand the total-mirror rule.Tests
122 tests green in the rust-builder container (fmt + clippy -D warnings clean):
upstream_unreachable_surfaces_as_transport_not_ioregression test)Consumer impact
None breaking. bunyip's download handler uses
matches!()on specific variants (not exhaustive match), so the newTransportvariant flows into its existing generic upstream path (HTTP 502) - the correct classification. bunyip picks up the improvements on its nextcargo update.