Skip to content

Commit 388805b

Browse files
TeoSlayerteovl
andauthored
test(registry): reconcile stale WAL torn-tail test with current contract
tests/pkg/registry/server held a stale mirror of the rendezvous WAL test suite: TestReplayErrorsOnTruncatedData expected Replay to hard-error on a truncated record. The rendezvous WAL deliberately tolerates a torn tail (a crash between the length-prefix and payload writes) — documented in wal.Replay and covered by rendezvous wal.TestWALReplayTornTail — replaying complete entries and dropping the torn tail. Replace it with TestReplayToleratesTornTail asserting the real contract: a lone torn record replays nothing; a complete entry survives a trailing torn tail. No error in either case. Co-authored-by: Teodor Calin <teodor@vulturelabs.io>
1 parent 53358fe commit 388805b

1 file changed

Lines changed: 71 additions & 16 deletions

File tree

tests/pkg/registry/server/zz_wal_test.go

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -308,26 +308,81 @@ func TestReplayRejectsOversizedEntry(t *testing.T) {
308308
}
309309
}
310310

311-
func TestReplayErrorsOnTruncatedData(t *testing.T) {
311+
// TestReplayToleratesTornTail asserts the WAL's torn-tail recovery
312+
// contract: a length prefix written without (all of) its payload — e.g. a
313+
// crash between the length write and the data write — is tolerated.
314+
// Replay applies every complete entry and drops the torn tail, returning
315+
// no error. (Mirrors rendezvous wal.TestWALReplayTornTail; this package's
316+
// older expectation of a hard error predated that deliberate relaxation.)
317+
func TestReplayToleratesTornTail(t *testing.T) {
312318
t.Parallel()
313-
dir := t.TempDir()
314-
p := filepath.Join(dir, "x.wal")
315319

316-
// Claim length 100 but only write 3 bytes of data.
317-
var lb [4]byte
318-
binary.LittleEndian.PutUint32(lb[:], 100)
319-
raw := append(lb[:], []byte("abc")...)
320-
if err := os.WriteFile(p, raw, 0600); err != nil {
321-
t.Fatal(err)
322-
}
320+
t.Run("lone torn record replays nothing", func(t *testing.T) {
321+
t.Parallel()
322+
dir := t.TempDir()
323+
p := filepath.Join(dir, "x.wal")
323324

324-
w, _ := server.NewWAL(p)
325-
defer w.Close()
325+
// Claim length 100 but only write 3 bytes of data.
326+
var lb [4]byte
327+
binary.LittleEndian.PutUint32(lb[:], 100)
328+
if err := os.WriteFile(p, append(lb[:], []byte("abc")...), 0600); err != nil {
329+
t.Fatal(err)
330+
}
326331

327-
_, err := w.Replay(func(server.DeltaEntry) error { return nil })
328-
if err == nil {
329-
t.Fatalf("expected error on truncated data")
330-
}
332+
w, _ := server.NewWAL(p)
333+
defer w.Close()
334+
335+
count, err := w.Replay(func(server.DeltaEntry) error { return nil })
336+
if err != nil {
337+
t.Fatalf("Replay should tolerate a torn tail, got error: %v", err)
338+
}
339+
if count != 0 {
340+
t.Fatalf("Replay count = %d, want 0 (torn tail dropped)", count)
341+
}
342+
})
343+
344+
t.Run("complete entry survives a trailing torn tail", func(t *testing.T) {
345+
t.Parallel()
346+
dir := t.TempDir()
347+
p := filepath.Join(dir, "x.wal")
348+
349+
w, _ := server.NewWAL(p)
350+
if err := w.Append(server.DeltaEntry{SeqNo: 1, Type: server.DeltaRegister, NodeID: 42}); err != nil {
351+
t.Fatal(err)
352+
}
353+
_ = w.Close()
354+
355+
// Append a torn record (length prefix, no payload) after the
356+
// complete entry, simulating a crash mid-append.
357+
f, err := os.OpenFile(p, os.O_APPEND|os.O_WRONLY, 0600)
358+
if err != nil {
359+
t.Fatal(err)
360+
}
361+
var lb [4]byte
362+
binary.LittleEndian.PutUint32(lb[:], 512)
363+
if _, err := f.Write(lb[:]); err != nil {
364+
t.Fatal(err)
365+
}
366+
_ = f.Close()
367+
368+
w2, _ := server.NewWAL(p)
369+
defer w2.Close()
370+
371+
var replayed []server.DeltaEntry
372+
count, err := w2.Replay(func(e server.DeltaEntry) error {
373+
replayed = append(replayed, e)
374+
return nil
375+
})
376+
if err != nil {
377+
t.Fatalf("Replay should tolerate a torn tail, got error: %v", err)
378+
}
379+
if count != 1 || len(replayed) != 1 {
380+
t.Fatalf("Replay count = %d (replayed %d), want 1 complete entry", count, len(replayed))
381+
}
382+
if replayed[0].NodeID != 42 {
383+
t.Errorf("replayed entry NodeID = %d, want 42", replayed[0].NodeID)
384+
}
385+
})
331386
}
332387

333388
func TestReplayLeavesFileAtEndForAppend(t *testing.T) {

0 commit comments

Comments
 (0)