import * as mssqlQueries from '../../providers/mssql/queries/healthcheck.js'; import * as sqliteQueries from '../../providers/sqlite3/queries/healthcheck.js'; import makeRunner from '../runner.js'; export default function check(provider) { const isSqlite = provider?.dialect === 'sqlite3'; const queries = isSqlite ? sqliteQueries : mssqlQueries; /** * Executes a database health check: * 1. Ensures the healthcheck table has one row (creates with sequence 0 if not). * 2. Selects that row for update with minimal, reasonable locking within a transaction. * 3. Updates the sequence by 1 in a separate statement within the transaction. * * @param {object} [tx] - Optional transaction to run within. * @returns {Promise<{ok: boolean, sequence: number}>} The health check result. * * @example * const result = await store.healthcheck.check(); * // => { ok: true, sequence: 1 } */ return async (tx) => { const runTx = async (runner) => { // 1. Ensure table has one row, if not create that row with number 0 await runner.query(queries.ENSURE_ROW); // 2. Select for update on that row with minimal, reasonable locking const selected = await runner.query(queries.SELECT_FOR_UPDATE); const row = selected.rows[0]; if (!row) { throw new Error('Healthcheck row not found'); } // 3. Updating the number by 1 and do an update (not in one statement, but in a tx) const currentSeq = Number(row.sequence ?? 0); const nextSeq = currentSeq + 1; await runner.query(queries.UPDATE_SEQUENCE, { id: row.id, sequence: nextSeq, }); return { ok: true, sequence: nextSeq, }; }; if (tx) { return runTx(makeRunner(provider, tx)); } if (typeof provider?.transaction === 'function') { return provider.transaction((runner) => runTx(makeRunner(provider, runner))); } return runTx(makeRunner(provider)); }; }