docs: v0.152.0 REPORT/CONTEXT + fix an async race in TestFabUpload_GCAndIdleTimeout

The fab-upload GC test stat-ed the .part immediately after observing the slot
free, but expireIdleUpload unlinks AFTER releasing the mutex. Passed alone,
failed in the full package once this release's render tests made web heavier.
Not a production defect - a new upload mints a fresh random .part. The test now
waits for the outcome it asserts on the same deadline; red-proofed by removing
the unlink from production.
This commit is contained in:
2026-07-20 13:55:03 +02:00
parent 37e12c82a7
commit fd40b29119
4 changed files with 163 additions and 114 deletions
@@ -340,7 +340,21 @@ func TestFabUpload_GCAndIdleTimeout(t *testing.T) {
}
time.Sleep(10 * time.Millisecond)
}
if _, err := os.Stat(part); !os.IsNotExist(err) {
// The unlink is ASYNCHRONOUS with respect to the slot: expireIdleUpload nils `cur`, releases
// the mutex, and only then closes + removes the file. So "the slot is free" does not yet mean
// "the .part is gone", and stat-ing immediately is a race the test loses under package load
// (observed 2026-07-20 — passes alone, fails in the full package). Wait for the outcome being
// asserted; the assertion itself is unchanged, and the deadline still fails a part that is
// never deleted.
partGone := false
for deadline := time.Now().Add(3 * time.Second); time.Now().Before(deadline); {
if _, err := os.Stat(part); os.IsNotExist(err) {
partGone = true
break
}
time.Sleep(10 * time.Millisecond)
}
if !partGone {
t.Fatal("idle-expired .part must be deleted")
}
if rr := uploadChunk(s, id, 0, []byte("late")); rr.Code != http.StatusNotFound {