Gatsby source plugin pg Pool max connections during develop
0 reputation · 28 Feb 2024, 17:03 UTC
0 reputation · 28 Feb 2024, 17:03 UTC
When using a Gatsby source plugin with node-postgres (pg) for database queries, intermittent connection pool exhaustion can occur during gatsby develop. Each plugin manages its own Pool instance, which emits errors like too many clients, already connected when the max limit is exceeded.
During development, concurrent page requests trigger parallel sourceNodes executions, potentially exceeding the pool's limit. Adjusting the pool size via PGMAXCONNECTIONS environment variables or plugin configuration can help, but the changes only apply to new pools. A running server must be restarted to apply new limits.
How can you diagnose and resolve intermittent connection pool exhaustion in a Gatsby source plugin using pg during development? Specifically:
gatsby develop?pg Pool error events to identify exhaustion issues?34775 reputation · 29 Feb 2024, 03:44 UTC
To resolve intermittent "too many clients" errors when a Gatsby source plugin uses node‑postgres (pg.Pool) during gatsby develop, monitor pool usage, log pool events, create a singleton pool, size it appropriately, and verify the change by restarting the dev server and checking active connections.
During gatsby develop the sourceNodes function runs repeatedly (on start, data refetches, and schema rebuilds). If the plugin creates a new pg.Pool on each run without calling pool.end(), each pool holds up to its max idle clients. Multiple pools multiply the total connections against PostgreSQL’s max_connections, eventually exhausting the limit and causing the "too many clients already" error.
pg.Pool options are max: 10, idleTimeoutMillis: 0, connectionTimeoutMillis: 0 (subject to the installed pg version).client.release() (or via pool.query callback) to return it to the idle pool.pool.on('acquire', () => console.log('pg acquire', pool.totalCount, pool.idleCount, pool.waitingCount));
pool.on('release', () => console.log('pg release', pool.totalCount, pool.idleCount, pool.waitingCount));
pool.on('remove', () => console.log('pg remove', pool.totalCount, pool.idleCount, pool.waitingCount));
gatsby develop, watch the console for the counts rising and falling. A steady increase in totalCount without matching release events indicates a leak.SELECT count(*) FROM pg_stat_activity WHERE application_name ILIKE '%gatsby%';
This shows the actual server‑side connections attributed to your Gatsby process.pool.on('error', (err) => {
console.error('pg pool error:', err);
// optionally send to a logging service
});
pool.connect calls are wrapped in a try/finally that releases the client:
pool.connect((err, client, done) => {
if (err) return console.error('connect err', err);
try {
// run query
} finally {
done(); // releases client back to pool
}
});
gatsby-node.js (or the plugin’s entry point) so it is created once per Node.js process:
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 4, // adjust based on observed usage
});
module.exports = { pool };
exports.onPostBootstrap = async () => {
await pool.end();
};
// also listen for process termination
process.on('SIGINT', () => pool.end());
process.on('SIGTERM', () => pool.end());
max value. For typical sourceNodes workloads (sequential per plugin) a range of 2‑5 is sufficient. If you run explicit parallel queries with Promise.all, increase proportionally but keep the total below PostgreSQL’s max_connections minus connections used by other services.
gatsby develop completely (stop the process, then start again) to ensure the new pool configuration takes effect.totalCount stabilizes around the chosen max and that no "error" events appear.SELECT count(*) FROM pg_stat_activity WHERE application_name ILIKE '%gatsby%';
The result should stay consistently below the pool’s max multiplied by the number of plugin instances (usually one).
If you observe that connection counts still exceed the expected max after these steps, please confirm the exact number of concurrent sourceNodes executions (e.g., how many plugins or parallel queries you are running) so we can adjust the pool size recommendation.
Use comments to ask for clarification. Post a solution as an answer.
5,960 reputation · 28 Feb 2024, 23:54 UTC
While singleton pools help within a single process, Gatsby's development cycle—especially with Hot Module Replacement (HMR)—can still leave "zombie" connections if the process is killed abruptly or restarts frequently. To prevent the development server from monopolizing all available slots in postgresql.conf, consider placing a connection pooler like PgBouncer between Gatsby and your database.
PgBouncer can manage a large number of incoming client connections while maintaining a small, fixed set of actual connections to the PostgreSQL server. This is particularly useful when multiple developers are hitting the same dev database. To verify if your current setup is leaking connections, you can run this query in your database while gatsby develop is active:
SELECT count(*) FROM pg_stat_activity;
If the count continues to climb despite using a singleton pool, it indicates that connections are not being closed properly during HMR triggers or process restarts.