From 42a23dff485a0ca80be89ddfa06022998e80eebc Mon Sep 17 00:00:00 2001 From: Karl Prieb Date: Tue, 24 Sep 2024 11:23:12 -0300 Subject: [PATCH 1/3] metrics: add WAL checkpoint metrics Add metrics for the WAL checkpoint pages for each database. Log output: ``` info: WAL checkpoint {"class":"StandaloneSqliteDatabase","dbName":"data","timestamp":"2024-09-24T14:22:13.957Z","walCheckpoint":{"busy":0,"checkpointed":0,"log":0}} ``` Metric output: ``` sqlite_wal_checkpoint_pages{db="data",type="busy"} 0 sqlite_wal_checkpoint_pages{db="data",type="checkpointed"} 0 sqlite_wal_checkpoint_pages{db="data",type="log"} 0 ``` --- src/database/standalone-sqlite.ts | 28 ++++++++++++++++++++++++---- src/metrics.ts | 6 ++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/database/standalone-sqlite.ts b/src/database/standalone-sqlite.ts index 9aa48b54..9ad58123 100644 --- a/src/database/standalone-sqlite.ts +++ b/src/database/standalone-sqlite.ts @@ -2348,7 +2348,9 @@ export class StandaloneSqliteDatabaseWorker { } cleanupWal(dbName: 'core' | 'bundles' | 'data' | 'moderation'): void { - this.dbs[dbName].pragma('wal_checkpoint(TRUNCATE)'); + const walCheckpoint = this.dbs[dbName].pragma('wal_checkpoint(TRUNCATE)'); + + return walCheckpoint[0]; } } @@ -2971,7 +2973,25 @@ export class StandaloneSqliteDatabase } async cleanupWal(dbName: WorkerPoolName): Promise { - return this.queueWrite(dbName, 'cleanupWal', [dbName]); + this.queueWrite(dbName, 'cleanupWal', [dbName]).then((walCheckpoint) => { + this.log.info('WAL checkpoint', { + dbName, + walCheckpoint, + }); + + metrics.sqliteWalCheckpointPages.set( + { db: dbName, type: 'busy' }, + walCheckpoint.busy, + ); + metrics.sqliteWalCheckpointPages.set( + { db: dbName, type: 'log' }, + walCheckpoint.log, + ); + metrics.sqliteWalCheckpointPages.set( + { db: dbName, type: 'checkpointed' }, + walCheckpoint.checkpointed, + ); + }); } } @@ -3114,8 +3134,8 @@ if (!isMainThread) { parentPort?.postMessage(null); break; case 'cleanupWal': - worker.cleanupWal(args[0]); - parentPort?.postMessage(undefined); + const walCheckpoint = worker.cleanupWal(args[0]); + parentPort?.postMessage(walCheckpoint); break; case 'terminate': parentPort?.postMessage(null); diff --git a/src/metrics.ts b/src/metrics.ts index f4ab855c..99fd5463 100644 --- a/src/metrics.ts +++ b/src/metrics.ts @@ -130,6 +130,12 @@ export const saveMethodsDuplicateCounter = new promClient.Counter({ labelNames: ['method'], }); +export const sqliteWalCheckpointPages = new promClient.Gauge({ + name: 'sqlite_wal_checkpoint_pages', + help: 'Number of pages in the WAL', + labelNames: ['db', 'type'], +}); + // // Block importer metrics // From c8885841037924bce706c14b2f47d31d1f92a44d Mon Sep 17 00:00:00 2001 From: David Whittington Date: Tue, 24 Sep 2024 13:20:03 -0500 Subject: [PATCH 2/3] chore: correct cleanupWal return type The function on the worker now returns a value so we can record metrics in the parent. --- src/database/standalone-sqlite.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/standalone-sqlite.ts b/src/database/standalone-sqlite.ts index 9ad58123..054fd432 100644 --- a/src/database/standalone-sqlite.ts +++ b/src/database/standalone-sqlite.ts @@ -2347,7 +2347,7 @@ export class StandaloneSqliteDatabaseWorker { }); } - cleanupWal(dbName: 'core' | 'bundles' | 'data' | 'moderation'): void { + cleanupWal(dbName: 'core' | 'bundles' | 'data' | 'moderation') { const walCheckpoint = this.dbs[dbName].pragma('wal_checkpoint(TRUNCATE)'); return walCheckpoint[0]; From 6f7db64dc7f9f5e0728dedf5da9f11c67e0c165e Mon Sep 17 00:00:00 2001 From: David Whittington Date: Tue, 24 Sep 2024 13:36:52 -0500 Subject: [PATCH 3/3] chore: return promise from cleanupWal We want to return a promise so callers can await on it if desired. --- src/database/standalone-sqlite.ts | 38 ++++++++++++++++--------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/database/standalone-sqlite.ts b/src/database/standalone-sqlite.ts index 054fd432..91df613e 100644 --- a/src/database/standalone-sqlite.ts +++ b/src/database/standalone-sqlite.ts @@ -2973,25 +2973,27 @@ export class StandaloneSqliteDatabase } async cleanupWal(dbName: WorkerPoolName): Promise { - this.queueWrite(dbName, 'cleanupWal', [dbName]).then((walCheckpoint) => { - this.log.info('WAL checkpoint', { - dbName, - walCheckpoint, - }); + return this.queueWrite(dbName, 'cleanupWal', [dbName]).then( + (walCheckpoint) => { + this.log.info('WAL checkpoint', { + dbName, + walCheckpoint, + }); - metrics.sqliteWalCheckpointPages.set( - { db: dbName, type: 'busy' }, - walCheckpoint.busy, - ); - metrics.sqliteWalCheckpointPages.set( - { db: dbName, type: 'log' }, - walCheckpoint.log, - ); - metrics.sqliteWalCheckpointPages.set( - { db: dbName, type: 'checkpointed' }, - walCheckpoint.checkpointed, - ); - }); + metrics.sqliteWalCheckpointPages.set( + { db: dbName, type: 'busy' }, + walCheckpoint.busy, + ); + metrics.sqliteWalCheckpointPages.set( + { db: dbName, type: 'log' }, + walCheckpoint.log, + ); + metrics.sqliteWalCheckpointPages.set( + { db: dbName, type: 'checkpointed' }, + walCheckpoint.checkpointed, + ); + }, + ); } }