bastion 0.53.2
ci / personal-data (push) Successful in 5s
ci / test (push) Successful in 3m4s
ci / fuzz (push) Successful in 3m45s

This commit is contained in:
Privatae
2026-10-20 04:42:14 +00:00
commit e4c9b69b46
471 changed files with 159794 additions and 0 deletions
+96
View File
@@ -0,0 +1,96 @@
package main
// Gate: the one background hashing job survives a file whose read never
// returns (a network mount that stopped answering). Each file gets a bound;
// past it the job moves on to the other folders, reads nothing more from that
// folder until the stuck read returns, and keeps that read's digest when it
// does.
import (
"os"
"path/filepath"
"sync"
"testing"
"time"
)
func awaitHash(t *testing.T, p, want, why string) {
t.Helper()
deadline := time.Now().Add(5 * time.Second)
for time.Now().Before(deadline) {
if fi, err := os.Lstat(p); err == nil && cachedHash(p, fi) == want {
return
}
time.Sleep(10 * time.Millisecond)
}
t.Fatal(why)
}
func TestAHashReadThatNeverReturnsHoldsUpNoOtherFolder(t *testing.T) {
const bound = 200 * time.Millisecond
hashMu.Lock()
prevBound := hashJobTimeout
hashJobTimeout = bound
hashMu.Unlock()
t.Cleanup(func() { hashMu.Lock(); hashJobTimeout = prevBound; hashMu.Unlock() })
quiet := opMaskLog
opMaskLog = func(string, ...any) {}
t.Cleanup(func() { opMaskLog = quiet })
dead, live := t.TempDir(), t.TempDir()
deadFile := filepath.Join(dead, fixtureData)
deadOther := filepath.Join(dead, "20260101T000000Z.tar.zst.age")
liveFile := filepath.Join(live, fixtureData)
for p, b := range map[string]string{deadFile: "stuck", deadOther: "stuck too", liveFile: "answers"} {
if err := os.WriteFile(p, []byte(b), 0o600); err != nil {
t.Fatal(err)
}
}
var mu sync.Mutex
deadReads := 0
gate := make(chan struct{})
var once sync.Once
release := func() { once.Do(func() { close(gate) }) }
prev := hashFileFn
hashFileFn = func(p string) (string, int64, error) {
if filepath.Dir(p) == dead {
mu.Lock()
deadReads++
mu.Unlock()
<-gate
}
return hashFile(p)
}
t.Cleanup(func() { release(); hashFileFn = prev })
reads := func() int {
mu.Lock()
defer mu.Unlock()
return deadReads
}
requestHash(deadFile)
deadline := time.Now().Add(5 * time.Second)
for reads() == 0 {
if time.Now().After(deadline) {
t.Fatal("the hashing job never started")
}
time.Sleep(5 * time.Millisecond)
}
requestHash(liveFile)
awaitHash(t, liveFile, sha([]byte("answers")),
"a read that never returned held up the hashing of every other folder")
// While the read is stuck, nothing more is read from that folder.
requestHash(deadOther)
time.Sleep(3 * bound)
if n := reads(); n != 1 {
t.Fatalf("the folder whose read is stuck was read %d times", n)
}
// When it returns, its digest is kept, and the folder is read again.
release()
awaitHash(t, deadFile, sha([]byte("stuck")), "the stuck read's digest was not kept when it returned")
requestHash(deadOther)
awaitHash(t, deadOther, sha([]byte("stuck too")), "the folder was not read again once it answered")
}