Log the start-up checkpoint's figures under their own names (closes #117)
check / check (push) Canceled after 0s

Initialize scanned the row of PRAGMA wal_checkpoint(TRUNCATE), which is
the busy flag, the frames in the WAL and the frames copied, into
wal_pages, checkpointed and moved; and a completed TRUNCATE reads 0 for
both counts. The line now logs busy, wal_frames and checkpointed_frames,
plus wal_bytes, the WAL file's size read before the checkpoint, and is a
warning when busy is 1. Tests open a database a second time while the
first stays open, with and without a read open on the first, and check
the line's figures.

Model: opus-5-5
This commit is contained in:
2026-10-20 02:08:51 +00:00
parent 651a9e22f6
commit cdc0d33da7
2 changed files with 210 additions and 10 deletions
+41 -10
View File
@@ -245,6 +245,12 @@ func New(cfg *config.Config, logger *logger.Logger) (*Database, error) {
return database, nil
}
// The messages Initialize logs the start-up WAL checkpoint with.
const (
startupCheckpointMessage = "WAL checkpoint on startup"
startupCheckpointIncompleteMessage = "WAL checkpoint on startup incomplete"
)
// Initialize creates the database schema if it doesn't exist.
func (d *Database) Initialize() error {
// Set SQLite pragmas for performance. Per-connection settings (cache_size,
@@ -266,19 +272,44 @@ func (d *Database) Initialize() error {
}
}
// Run WAL checkpoint on startup to consolidate any existing WAL
var walPages, checkpointedPages, movedPages int
// The start-up checkpoint copies the WAL a previous run left into the
// database file and empties the WAL file; its frame counts then read 0,
// so the WAL file's size is read first. There is no WAL file when the
// database is new or the last run closed the database, which deletes it.
var walBytes int64
err := d.writer.QueryRowContext(context.Background(),
wal, err := os.Stat(d.path + walFileSuffix)
if err == nil {
walBytes = wal.Size()
} else if !errors.Is(err, os.ErrNotExist) {
d.logger.Warn("Failed to read the WAL file's size on startup",
"error", err)
}
var busy, walFrames, checkpointedFrames int
err = d.writer.QueryRowContext(context.Background(),
"PRAGMA wal_checkpoint(TRUNCATE)",
).Scan(&walPages, &checkpointedPages, &movedPages)
if err != nil {
).Scan(&busy, &walFrames, &checkpointedFrames)
switch {
case err != nil:
d.logger.Warn("Failed to checkpoint WAL on startup", "error", err)
} else {
d.logger.Info("WAL checkpoint on startup",
"wal_pages", walPages,
"checkpointed", checkpointedPages,
"moved", movedPages,
case busy != 0:
// Another process using the database kept the checkpoint from
// copying or emptying all of the WAL.
d.logger.Warn(startupCheckpointIncompleteMessage,
"wal_bytes", walBytes,
"busy", busy,
"wal_frames", walFrames,
"checkpointed_frames", checkpointedFrames,
)
default:
d.logger.Info(startupCheckpointMessage,
"wal_bytes", walBytes,
"busy", busy,
"wal_frames", walFrames,
"checkpointed_frames", checkpointedFrames,
)
}