Production MongoDB: Transactions, Replication, and SecurityLesson 6.4
MongoDB connection pooling and performance tuning in Node.js
connection pool, maxPoolSize, minPoolSize, MongoClient singleton pattern, serverSelectionTimeoutMS, socketTimeoutMS, connection string options, pool monitoring
Why connection pooling matters
Establishing a new MongoDB TCP connection takes 20–100 ms and allocates resources on both the client and server. Under load, creating a connection per request would saturate MongoDB's connection limit within seconds. The MongoClient maintains a pool of pre-established, reusable connections — each request borrows one for its operation and immediately returns it, with minimal overhead after pool warm-up.
// WRONG — new connection per request, pools bypassed entirely
app.get('/products', async (req, res) => {
const client = new MongoClient(uri) // new TCP connection every time
await client.connect()
const data = await client.db('shop').collection('products').find().toArray()
await client.close()
res.json(data)
})
// CORRECT — singleton client created at startup, shared across all requests
let _db = null
export async function getDB() {
if (_db) return _db
const client = await MongoClient.connect(process.env.MONGO_URI, {
maxPoolSize: 10, // max concurrent connections
minPoolSize: 2, // keep 2 alive to reduce cold-start latency
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000
})
_db = client.db('shopDB')
return _db
}
app.get('/products', async (req, res) => {
const db = await getDB() // instant — returns cached db reference
res.json(await db.collection('products').find().toArray())
})Start with maxPoolSize: 10. Increase it only after profiling shows requests are waiting in the pool queue under real production load.
