Refactor database connection management to auto-close based on usage
Resolved 💬 2 comments Opened Jan 18, 2026 by jodysalt Closed Jan 18, 2026
Current Problem
The database client in @pinstripe/database uses a module-level connectionCounter that requires manual destroy() calls. If any context forgets to destroy, or accesses the database outside a workspace, connections stay open indefinitely.
This caused an issue where reset_database_from_sql called destroy() but the connection stayed open because another context (the start_server command) had also accessed the database.
Proposed Solution
Replace reference counting with usage-based auto-close:
- Track active locks, transactions, and in-flight queries
- When all are 0, automatically close connection (with short debounce)
- Reconnect lazily on next query
Implementation Sketch
// packages/@pinstripe/database/lib/client.js
let connection;
let activeOperations = 0; // locks + transactions + queries
let closeTimeout;
async run(query, options = {}){
clearTimeout(closeTimeout);
activeOperations++;
try {
if(!connection) await this.connect();
// ... execute query
} finally {
activeOperations--;
this.scheduleClose();
}
}
scheduleClose(){
if(activeOperations > 0) return;
clearTimeout(closeTimeout);
closeTimeout = setTimeout(() => {
if(activeOperations === 0 && connection) {
this.closeConnection();
}
}, 100); // debounce delay TBD
}
Changes Needed
run()- increment before, decrement after (in finally block)lock()- increment/decrement around lock operationstransaction()- increment/decrement around transaction- Remove or deprecate
destroy()method - Handle reconnection in
run()if connection was auto-closed
Open Questions
- What debounce delay? (0ms, 100ms, 1000ms?)
- Should
destroy()be kept for explicit cleanup, or removed entirely? - Any concern about connection churn in high-throughput scenarios?
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗