@stackmemoryai/stackmemory
Version:
Lossless, project-scoped memory for AI coding tools. Durable context across sessions with 56 MCP tools, FTS5 search, conductor orchestrator, loop/watch monitoring, snapshot capture, pre-flight overlap checks, Claude/Codex/OpenCode wrappers, Linear sync, a
247 lines (246 loc) • 7.49 kB
JavaScript
import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import Database from "better-sqlite3";
import { trace } from "./debug-trace.js";
import { logger } from "../monitoring/logger.js";
function createTracedDatabase(filename, options) {
const db = new Database(filename, options);
if (options?.traceEnabled !== false) {
return wrapDatabase(db, options?.slowQueryThreshold);
}
return db;
}
function wrapDatabase(db, slowQueryThreshold = 100) {
const originalPrepare = db.prepare.bind(db);
db.prepare = function(source) {
const statement = originalPrepare(source);
return wrapStatement(statement, source, slowQueryThreshold);
};
const originalExec = db.exec.bind(db);
db.exec = function(source) {
return trace.traceSync(
"query",
`EXEC: ${source.substring(0, 50)}...`,
{},
() => {
const startTime = performance.now();
const result = originalExec(source);
const duration = performance.now() - startTime;
if (duration > slowQueryThreshold) {
logger.warn(`Slow query detected: ${duration.toFixed(0)}ms`, {
query: source.substring(0, 200),
duration
});
}
return result;
}
);
};
const originalTransaction = db.transaction.bind(db);
db.transaction = function(fn) {
return originalTransaction(function(...args) {
return trace.traceSync(
"query",
"TRANSACTION",
{ args: args.length },
() => {
return fn.apply(this, args);
}
);
});
};
db.__queryStats = {
totalQueries: 0,
slowQueries: 0,
totalDuration: 0,
queryTypes: {}
};
return db;
}
function wrapStatement(statement, source, slowQueryThreshold) {
const queryType = source.trim().split(/\s+/)[0].toUpperCase();
const shortQuery = source.substring(0, 100).replace(/\s+/g, " ");
const originalRun = statement.run.bind(statement);
statement.run = function(...params) {
return trace.traceSync(
"query",
`${queryType}: ${shortQuery}`,
params,
() => {
const startTime = performance.now();
const result = originalRun(...params);
const duration = performance.now() - startTime;
updateQueryStats(statement, queryType, duration, slowQueryThreshold);
if (duration > slowQueryThreshold) {
logger.warn(`Slow ${queryType} query: ${duration.toFixed(0)}ms`, {
query: shortQuery,
params,
duration,
changes: result.changes
});
}
return result;
}
);
};
const originalGet = statement.get.bind(statement);
statement.get = function(...params) {
return trace.traceSync(
"query",
`${queryType} (get): ${shortQuery}`,
params,
() => {
const startTime = performance.now();
const result = originalGet(...params);
const duration = performance.now() - startTime;
updateQueryStats(statement, queryType, duration, slowQueryThreshold);
if (duration > slowQueryThreshold) {
logger.warn(`Slow ${queryType} query: ${duration.toFixed(0)}ms`, {
query: shortQuery,
params,
duration,
found: result != null
});
}
return result;
}
);
};
const originalAll = statement.all.bind(statement);
statement.all = function(...params) {
return trace.traceSync(
"query",
`${queryType} (all): ${shortQuery}`,
params,
() => {
const startTime = performance.now();
const result = originalAll(...params);
const duration = performance.now() - startTime;
updateQueryStats(statement, queryType, duration, slowQueryThreshold);
if (duration > slowQueryThreshold) {
logger.warn(`Slow ${queryType} query: ${duration.toFixed(0)}ms`, {
query: shortQuery,
params,
duration,
rows: result.length
});
}
if (result.length > 100 && queryType === "SELECT") {
logger.warn(`Large result set: ${result.length} rows`, {
query: shortQuery,
suggestion: "Consider pagination or more specific queries"
});
}
return result;
}
);
};
const originalIterate = statement.iterate.bind(statement);
statement.iterate = function(...params) {
const startTime = performance.now();
let rowCount = 0;
const iterator = originalIterate(...params);
const wrappedIterator = {
[Symbol.iterator]() {
return this;
},
next() {
const result = iterator.next();
if (!result.done) {
rowCount++;
} else {
const duration = performance.now() - startTime;
updateQueryStats(statement, queryType, duration, slowQueryThreshold);
if (duration > slowQueryThreshold) {
logger.warn(
`Slow ${queryType} iteration: ${duration.toFixed(0)}ms`,
{
query: shortQuery,
params,
duration,
rows: rowCount
}
);
}
}
return result;
}
};
return wrappedIterator;
};
return statement;
}
function updateQueryStats(statement, queryType, duration, slowQueryThreshold) {
const db = statement.database;
if (db.__queryStats) {
db.__queryStats.totalQueries++;
db.__queryStats.totalDuration += duration;
if (duration > slowQueryThreshold) {
db.__queryStats.slowQueries++;
}
if (!db.__queryStats.queryTypes[queryType]) {
db.__queryStats.queryTypes[queryType] = 0;
}
db.__queryStats.queryTypes[queryType]++;
}
}
function getQueryStatistics(db) {
const stats = db.__queryStats;
if (!stats) return null;
return {
...stats,
averageDuration: stats.totalQueries > 0 ? stats.totalDuration / stats.totalQueries : 0
};
}
async function traceQuery(db, queryName, query, params, fn) {
return trace.traceAsync("query", queryName, { query, params }, async () => {
try {
const result = fn();
if (query.includes("JOIN") || query.includes("GROUP BY")) {
logger.debug(`Complex query executed: ${queryName}`, {
query: query.substring(0, 200),
params
});
}
return result;
} catch (error) {
logger.error(`Database query failed: ${queryName}`, error, {
query,
params,
errorCode: error.code
});
throw error;
}
});
}
function createTracedTransaction(db, name, fn) {
return trace.traceSync("query", `TRANSACTION: ${name}`, {}, () => {
const startTime = performance.now();
try {
const tx = db.transaction(fn);
const result = tx.deferred();
const duration = performance.now() - startTime;
logger.info(`Transaction completed: ${name}`, {
duration,
success: true
});
return result;
} catch (error) {
const duration = performance.now() - startTime;
logger.error(`Transaction failed: ${name}`, error, {
duration,
success: false
});
throw error;
}
});
}
export {
createTracedDatabase,
createTracedTransaction,
getQueryStatistics,
traceQuery,
wrapDatabase
};