Fix: surface swallowed hash-computation errors in calculate_pkg_hashes - #5249
Open
kevinemoore wants to merge 3 commits into
Open
Fix: surface swallowed hash-computation errors in calculate_pkg_hashes#5249kevinemoore wants to merge 3 commits into
kevinemoore wants to merge 3 commits into
Conversation
calculate_pkg_hashes submitted per-entry hash computations to a thread
pool and then called concurrent.futures.wait(comp_futures), which blocks
until every future is done but never reads their results. wait() retains
any raised exception on the future silently, so a failure inside a
computation (e.g. an AccessDenied on copy_object writing to a
cross-region scratch bucket) was discarded: entry.hash stayed unset and
package creation fell back to an expensive download-and-hash with no
signal that anything went wrong.
Replace wait() with an as_completed() loop that calls f.result() on each
future, re-raising the first exception so the failure is loud.
Regression test test_calculate_pkg_hashes_propagates_compute_error mocks
compute_checksum_via_copy to raise and asserts the exception propagates;
verified it fails ("DID NOT RAISE Exception") against the wait()-based
code before the fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two review findings on the regression test:
- It asserted against bare Exception, which a match string alone does not
make precise. Introduce a dedicated _ScratchAccessDenied subclass and
raise/expect that, so the test binds to the intended failure path rather
than any incidental exception whose message contains "AccessDenied".
- It only asserted that an exception propagates, not the core regression
symptom. Add an assertion that the failed (copy-path) entry's hash stays
None, guarding against a future change that surfaces the error but still
leaves the entry in a bad partial state.
Verified the tightened test still fails ("DID NOT RAISE _ScratchAccessDenied")
against the wait()-based code before the fix. Full pkgpush suite: 58 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests.
Additional details and impacted files@@ Coverage Diff @@
## master #5249 +/- ##
===========================================
- Coverage 54.70% 39.67% -15.03%
===========================================
Files 869 757 -112
Lines 36337 26283 -10054
Branches 6424 6424
===========================================
- Hits 19877 10428 -9449
+ Misses 14716 14111 -605
Partials 1744 1744
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
10 tasks
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.
Problem
calculate_pkg_hashessubmits per-entry hash computations to a thread pool, then callsconcurrent.futures.wait(comp_futures).wait()blocks until every future is done but never reads their results, so any exception raised inside a computation is silently retained on the future and discarded. The observable damage:AccessDeniedwriting to a cross-region scratch bucket (or any per-entry failure) is swallowed.entry.hashis left unset, and package creation falls back to an expensive download-and-hash — with no signal that anything went wrong.This is item 4.5 of the alias-roles 26.8.0 remediation (the swallowed-error half of the cross-region scratch finding). It is filed and reviewed as a standalone PR, separate from the cross-region scratch grant fix (item 4), which is an IAM/template change in the deployment repo.
Fix
Replace
wait()with anas_completed()+f.result()loop, re-raising the first exception so the failure is loud. This mirrors the pattern already used forprecomp_futuresa few lines above.Test
Added
test_calculate_pkg_hashes_propagates_compute_error: mockscompute_checksum_via_copyto raise a dedicated_ScratchAccessDenied, asserts the exception propagates, and asserts the failed entry'shashstaysNone(the actual regression symptom). Verified it fails (DID NOT RAISE) against the pre-fixwait()code before the fix was applied.A
/code-reviewpass ran on the branch's final head; the two test-quality findings it raised (bare-Exceptionassertion; missing symptom assertion) are folded into the test above.Test environment note
The pkgpush lambda suite runs standalone (no Postgres/Docker needed). Run locally with
uv run pytestunderlambdas/pkgpush/— 58 passed including the new regression test. This differs from the registry suite, which needs Postgres + Docker.Follow-up
#PRplaceholder link; it will be backfilled with this PR's number.Greptile Summary
This PR makes package hash computation fail loudly when a worker future raises, rather than silently proceeding with an unset entry hash.
as_completed()andresult()so worker exceptions propagate.Confidence Score: 5/5
The PR appears safe to merge because it surfaces worker failures as intended without introducing a concrete correctness, security, or lifecycle defect.
The executor context still waits for all submitted work during unwinding, while calling each completed future's result closes the existing exception-swallowing path and the regression test covers the intended failure behavior.
Important Files Changed
Sequence Diagram
sequenceDiagram participant Push as Package push participant Pool as Hash worker pool participant Worker as Entry hash computation Push->>Pool: Submit entry computations Pool->>Worker: Compute checksum alt computation succeeds Worker-->>Pool: Checksum Pool-->>Push: Future completes Push->>Push: result() returns else computation fails Worker-->>Pool: Exception Pool-->>Push: Future completes Push->>Push: result() re-raises exception endReviews (1): Last reviewed commit: "Tighten swallowed-hash-error regression ..." | Re-trigger Greptile
Context used: