mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-13 13:53:12 -06:00
The checkpointer startup line no longer describes a policy it stopped having
It still read "escalate >16MB or 3 growing runs" after #240 added a size floor and a cooldown to that second rule. Growth-across-three-runs on its own is exactly the half that no longer holds, so the line described a checkpointer that does not exist — and it is the line an operator reads to learn the policy. During an incident it would send you hunting for a blocking checkpoint that the new gates had in fact suppressed. [wal-checkpoint] off-thread checkpointer started (PASSIVE every 15000ms; blocking TRUNCATE when the WAL exceeds 16MB, or after 3 growing runs but only at >=8MB and at most once per 300s; respawn max 5/60000ms) A test now asserts the line reports every knob that governs the decision, since nothing else keeps a log string and the rule it describes in step. Writing it caught its own bug first: anchoring the slice back to `return worker;` matched the idempotence guard at the top of startWalCheckpointer(), not the log below it, so the window was empty and every assertion passed vacuously. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014skWYXJUWhF73EvNPgB2AS
This commit is contained in:
parent
70b2227fa8
commit
bf9ad16486
|
|
@ -121,7 +121,17 @@ function startWalCheckpointer(db, dbPath) {
|
|||
try { db.pragma('wal_checkpoint(TRUNCATE)'); } catch (_) { /* best-effort */ }
|
||||
|
||||
worker = spawnWorker();
|
||||
console.log(`[wal-checkpoint] off-thread checkpointer started (every ${config.walCheckpointIntervalMs}ms; escalate >${config.walCheckpointHighWaterMB}MB or ${config.walCheckpointStarvationRuns} growing runs; respawn max ${config.walCheckpointRespawnMax}/${config.walCheckpointRespawnWindowMs}ms)`);
|
||||
// #240: this line is where an operator learns the escalation policy, so it must state ALL of
|
||||
// it. It advertised only "3 growing runs" after the size floor and the cooldown were added,
|
||||
// which is the half that no longer holds on its own — and reading it during an incident would
|
||||
// send you looking for a checkpoint that the gates had in fact suppressed.
|
||||
console.log(
|
||||
`[wal-checkpoint] off-thread checkpointer started (PASSIVE every ${config.walCheckpointIntervalMs}ms; ` +
|
||||
`blocking TRUNCATE when the WAL exceeds ${config.walCheckpointHighWaterMB}MB, ` +
|
||||
`or after ${config.walCheckpointStarvationRuns} growing runs but only at >=${config.walCheckpointStarvationFloorMB}MB ` +
|
||||
`and at most once per ${Math.round(config.walCheckpointEscalateCooldownMs / 1000)}s; ` +
|
||||
`respawn max ${config.walCheckpointRespawnMax}/${config.walCheckpointRespawnWindowMs}ms)`
|
||||
);
|
||||
return worker;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,6 +108,28 @@ test('#240: the worker actually applies the floor it is handed', () => {
|
|||
// Measured, not assumed: with a single reader mid-transaction, TRUNCATE returns busy=1
|
||||
// after sitting on its 5s busy timeout and reclaims nothing (probe: WAL 8.8MB -> 8.8MB,
|
||||
// worst main-thread write 4,936ms). That outcome must not be logged as a success.
|
||||
// The startup line is where an operator learns the policy. It went stale the moment the gates were
|
||||
// added — it still promised "3 growing runs" with no mention of the floor or the cooldown, so it
|
||||
// described a checkpointer that no longer existed. A log line that states a rule has to state the
|
||||
// whole rule, and nothing but a test keeps the two in step.
|
||||
test('#240: the startup line states the WHOLE escalation policy', () => {
|
||||
const ctl = fs.readFileSync(path.join(__dirname, '..', 'db', 'wal-checkpointer.js'), 'utf8');
|
||||
// Anchor forward from the message text, not back to `return worker;` — the idempotence guard at
|
||||
// the top of startWalCheckpointer() returns first, so slicing to it yields nothing at all.
|
||||
const start = ctl.indexOf('off-thread checkpointer started');
|
||||
assert.ok(start > 0, 'the startup line is gone entirely');
|
||||
const line = ctl.slice(start, start + 800);
|
||||
for (const knob of [
|
||||
'walCheckpointIntervalMs',
|
||||
'walCheckpointHighWaterMB',
|
||||
'walCheckpointStarvationRuns',
|
||||
'walCheckpointStarvationFloorMB',
|
||||
'walCheckpointEscalateCooldownMs',
|
||||
]) {
|
||||
assert.ok(line.includes(knob), `the startup line must report ${knob} — an operator reads it as the policy`);
|
||||
}
|
||||
});
|
||||
|
||||
test('#240: a TRUNCATE that reclaimed nothing says so', () => {
|
||||
const src = fs.readFileSync(path.join(__dirname, '..', 'db', 'wal-checkpointer-worker.js'), 'utf8');
|
||||
assert.match(src, /busy === 1/, 'worker must inspect the checkpoint result');
|
||||
|
|
|
|||
Loading…
Reference in a new issue