Files
seaweedfs/weed/util/log_buffer/shutdown_test.go
T
David Christopher a859f0a019 filer: preserve accepted metadata log records on shutdown (#11359)
fix: flush metadata log before closing filer store

Serialize sealed-batch handoffs with shutdown, reject late appends, and wait for log-buffer workers before closing the filer metadata store.

Cover queued writes, interval and explicit flushes, late-write rejection, and pending persistence with shutdown tests.
2026-09-16 12:23:59 -07:00

113 lines
2.9 KiB
Go

package log_buffer
import (
"bytes"
"errors"
"fmt"
"reflect"
"testing"
"testing/synctest"
"time"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/util"
)
func TestShutdownDrainsAcceptedRecords(t *testing.T) {
for _, seal := range []string{"append", "interval", "force"} {
t.Run(seal, func(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
releaseFlush := make(chan struct{})
var persisted bytes.Buffer
lb := NewLogBuffer("shutdown", time.Hour, func(_ *LogBuffer, _, _ time.Time, data []byte, _, _ int64) {
<-releaseFlush
persisted.Write(data)
}, nil, nil)
// Fill the queue behind a blocked persistence call, leaving one
// record in the current window. Each append seals its predecessor.
var want []string
appendRecord := func() {
value := fmt.Sprint(len(want))
ts := time.Now().Add(time.Duration(len(want)) * 2 * time.Hour).UnixNano()
if err := lb.AddDataToBuffer(nil, []byte(value), ts); err != nil {
t.Error(err)
}
want = append(want, value)
}
for range flushQueueDepth + 2 {
appendRecord()
synctest.Wait()
}
switch seal {
case "append":
go appendRecord()
case "interval":
time.Sleep(time.Hour)
case "force":
go lb.ForceFlush()
}
synctest.Wait()
done := make(chan struct{})
go func() {
lb.ShutdownLogBuffer()
lb.WaitForShutdown()
close(done)
}()
<-lb.shutdownCh
select {
case <-done:
t.Error("shutdown completed while persistence was blocked")
default:
}
close(releaseFlush)
<-done
synctest.Wait()
var got []string
for persisted.Len() > 0 {
size := util.BytesToUint32(persisted.Next(4))
var entry filer_pb.LogEntry
if err := entry.UnmarshalVT(persisted.Next(int(size))); err != nil {
t.Fatal(err)
}
got = append(got, string(entry.Data))
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("persisted records = %v, want %v", got, want)
}
})
})
}
}
func TestShutdownRejectsNewRecords(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
lb := NewLogBuffer("stopped", time.Hour, nil, nil, nil)
lb.ShutdownLogBuffer()
for _, appendRecord := range []func() error{
func() error { return lb.AddDataToBuffer(nil, []byte("late data"), 0) },
func() error { return lb.AddLogEntryToBuffer(&filer_pb.LogEntry{Data: []byte("late entry")}) },
} {
if err := appendRecord(); !errors.Is(err, ErrBufferStopped) {
t.Errorf("append after shutdown = %v, want ErrBufferStopped", err)
}
}
lb.ForceFlush()
lb.WaitForShutdown()
if lb.pos != 0 {
t.Errorf("stopped buffer retained %d bytes", lb.pos)
}
})
}
func TestWaitForShutdownWithEmptyBuffer(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
lb := NewLogBuffer("empty", time.Hour, nil, nil, nil)
lb.ShutdownLogBuffer()
lb.WaitForShutdown()
lb.WaitForShutdown()
})
}