Checkpoint without waiting on readers and log a busy checkpoint (closes #102)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
The 5-second checkpoint ran in TRUNCATE mode, which waits up to the 5-second busy timeout for every open read while holding the database lock every batch write needs. It now runs in PASSIVE mode, which never waits. A WAL file over 64 MiB instead gets one TRUNCATE checkpoint whose wait for reads is capped at 500 ms and logged; it empties the file once no read is open, and while reads follow one another with no pause it can give up every time. A checkpoint that leaves part of the WAL uncopied returns ErrCheckpointIncomplete, which the maintainer logs as incomplete instead of completed. The writing connection's journal_size_limit is 64 MiB. Model: opus-5-5
This commit was merged in pull request #126.
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
package routewatch
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"testing"
|
||||
|
||||
"git.eeqj.de/sneak/routewatch/internal/database"
|
||||
)
|
||||
|
||||
// incompleteCheckpointStore is a store whose every checkpoint leaves part of
|
||||
// the WAL uncopied.
|
||||
type incompleteCheckpointStore struct{ *mockStore }
|
||||
|
||||
// Checkpoint reports the checkpoint incomplete.
|
||||
func (incompleteCheckpointStore) Checkpoint(context.Context) error {
|
||||
return database.ErrCheckpointIncomplete
|
||||
}
|
||||
|
||||
// TestIncompleteCheckpointNotLoggedAsCompleted runs a checkpoint that leaves
|
||||
// part of the WAL uncopied and checks that the maintainer logs it as
|
||||
// incomplete, not as completed, and does not count it as a checkpoint that
|
||||
// completed.
|
||||
func TestIncompleteCheckpointNotLoggedAsCompleted(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var logs bytes.Buffer
|
||||
|
||||
logger := slog.New(slog.NewJSONHandler(&logs,
|
||||
&slog.HandlerOptions{Level: slog.LevelDebug}))
|
||||
m := NewDBMaintainer(incompleteCheckpointStore{newMockStore()}, logger)
|
||||
|
||||
m.runCheckpoint(t.Context())
|
||||
|
||||
var logged struct {
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
err := json.Unmarshal(logs.Bytes(), &logged)
|
||||
if err != nil {
|
||||
t.Fatalf("want one log message, got %q: %v", logs.String(), err)
|
||||
}
|
||||
|
||||
if logged.Msg != checkpointIncompleteMessage {
|
||||
t.Errorf("logged %q, want %q", logged.Msg, checkpointIncompleteMessage)
|
||||
}
|
||||
|
||||
if m.checkpointCount != 0 {
|
||||
t.Errorf("checkpointCount = %d after an incomplete checkpoint, want 0",
|
||||
m.checkpointCount)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user