package lease import ( "context" "errors" "io" "os" "path/filepath" "testing" "time" "github.com/benbjohnson/litestream" lsfile "github.com/benbjohnson/litestream/file" ) func TestGenerationRefusedSnapshotReleasesWAL(t *testing.T) { path := filepath.Join(t.TempDir(), "source.db") seedDB(t, path) db := litestream.NewDB(path) db.MonitorInterval = 0 db.Replica = litestream.NewReplicaWithClient(db, lsfile.NewReplicaClient(t.TempDir())) db.Replica.MonitorEnabled = false if err := db.Open(); err != nil { t.Fatal(err) } t.Cleanup(func() { db.Close(context.Background()) }) if err := db.SyncAndWait(t.Context()); err != nil { t.Fatal(err) } countWAL := func() int { t.Helper() fds, err := os.ReadDir("/proc/self/fd") if err != nil { t.Fatal(err) } count := 0 for _, fd := range fds { name, err := os.Readlink(filepath.Join("/proc/self/fd", fd.Name())) if err == nil && name == path+"-wal" { count++ } } return count } baseline := countWAL() pos, reader, err := db.SnapshotReader(t.Context()) if err != nil { t.Fatal(err) } pipe := reader.(*io.PipeReader) t.Cleanup(func() { pipe.Close() }) deadline := time.Now().Add(time.Second) for countWAL() == baseline && time.Now().Before(deadline) { time.Sleep(time.Millisecond) } if countWAL() <= baseline { t.Fatal("snapshot did not hold its WAL descriptor") } client := &generationClient{client: db.Replica.Client, check: func(context.Context) error { return ErrLost }} if _, err := client.WriteLTXFile(t.Context(), litestream.SnapshotLevel, 1, pos.TXID, reader); !errors.Is(err, ErrLost) { t.Fatal(err) } deadline = time.Now().Add(time.Second) for countWAL() > baseline && time.Now().Before(deadline) { time.Sleep(time.Millisecond) } if got := countWAL(); got != baseline { t.Fatalf("refused snapshot retained WAL descriptors: got %d, want %d", got, baseline) } }