Error Medic

Troubleshooting MongoDB Connection Pool Exhaustion and Timeout Errors

Fix MongoDB connection timeouts, "server selection timed out", and pool exhaustion errors. Learn how to configure maxPoolSize, socket timeouts, and debug limits

Last updated:
Last verified:
1,641 words
Key Takeaways
  • Connection pool exhaustion often occurs due to insufficient maxPoolSize for high-concurrency applications or connection leaks in your code.
  • Network latency, firewalls, or Atlas IP whitelisting issues typically cause "server selection timed out" or "MongoSocketReadTimeoutException".
  • Quick Fix: Increase maxPoolSize for monolithic apps, reduce it for serverless (AWS Lambda), and tune socketTimeoutMS and serverSelectionTimeoutMS.
Fix Approaches Compared
MethodWhen to UseTimeRisk
Increase maxPoolSizeHigh concurrency traffic overwhelming default pool limits5 minsLow
Configure AWS Lambda CachingServerless environments opening too many connections30 minsMedium
Tune socketTimeoutMSLong-running queries or aggregate timeouts15 minsLow
Fix Connection LeaksPool closed or exhausted errors over time1-2 hoursMedium

Understanding the Error: Connection Pools and Timeouts

When building applications that interact with MongoDB, managing connections efficiently is critical for performance and reliability. Poor connection management often leads to a cascade of errors, ranging from MongoSocketReadTimeoutException to server selection timed out. This guide dives deep into MongoDB connection pooling, common timeout errors, and actionable steps to diagnose and fix them across various environments, including Node.js, Java, Python, Golang, and serverless architectures like AWS Lambda.

A connection pool is a cache of database connections maintained by the MongoDB driver so that connections can be reused when future requests to the database are required. Opening a new TCP connection and completing the MongoDB TLS/SSL handshake is computationally expensive and slow. When the pool is misconfigured, exhausted, or when network instability occurs, you will encounter specific timeout exceptions.

Exact Error Messages You Might See

  1. Server Selection Timeout: MongoTimeoutException: Timed out after 30000 ms while waiting for a server that matches com.mongodb.client.internal.MongoClientDelegate Or in Node.js: MongoServerSelectionError: Server selection timed out after 30000 ms Meaning: The driver cannot find an eligible MongoDB server. This is often due to network issues, IP whitelisting in MongoDB Atlas, or DNS resolution failures.

  2. Socket Read Timeout: com.mongodb.MongoSocketReadTimeoutException: Timeout while receiving message Meaning: The connection was established, but the database took too long to respond to a query (exceeding socketTimeoutMS). This is common with unoptimized aggregate queries.

  3. Buffering Timed Out: MongooseError: Operation users.findOne() buffering timed out after 10000ms Meaning: Mongoose buffers commands when the connection is not yet ready. If the connection fails to establish within 10 seconds, this error is thrown.

  4. Pool Exhaustion/Wait Queue Timeout: MongoWaitQueueFullException: Too many operations are already waiting for a connection. Meaning: All connections in the pool are currently in use, and the queue of operations waiting for a free connection has reached its limit.

  5. Connection Pool Closed: MongoNotPrimaryException: The connection pool has been closed or mongodb connection pool closed Meaning: The application attempted to use a MongoClient instance that has already been explicitly closed via a .close() or .disconnect() call.

Step 1: Diagnose the Root Cause

Before blindly increasing timeout settings or pool sizes, diagnose what is consuming your connections.

1. Check MongoDB Atlas Metrics (or mongostat) If you are using MongoDB Atlas, check the "Connections" metric in your cluster dashboard. If connections are steadily climbing without dropping, you have a connection leak. If they hit a hard ceiling matching your pool limits, you are hitting your maxPoolSize limits across your application instances.

2. Identify Long-Running Queries Sometimes, a pool is exhausted because connections are tied up waiting for slow queries to complete. Use db.currentOp() to find queries taking too long:

db.currentOp({
  "active": true,
  "secs_running": { "$gt": 3 }
})

3. Review Network Configurations If you are getting server selection timeout mongodb, verify your MongoDB Atlas Network Access tab. Ensure the IP address of your application server (or your VPC NAT Gateway) is whitelisted.

Step 2: Fix Connection Pool Exhaustion (maxPoolSize)

By default, most MongoDB drivers (like Node.js, Java, Python) have a default maxPoolSize of 100. If your application handles high concurrency, this might not be enough.

Configuring in Node.js (Mongoose / Native Driver)

You can configure the pool size in the connection string or the connection options.

Connection String: mongodb+srv://user:pass@cluster.mongodb.net/myDb?maxPoolSize=200

Options Object:

const mongoose = require('mongoose');

mongoose.connect(process.env.MONGO_URI, {
  maxPoolSize: 200, // Maintain up to 200 socket connections
  minPoolSize: 10,  // Keep at least 10 connections open
});
Configuring in Java (Spring Data MongoDB)

In a Spring Boot application, you can configure the connection pool in your application.properties or application.yml:

spring.data.mongodb.uri=mongodb+srv://user:pass@cluster.mongodb.net/myDb?maxPoolSize=150&maxIdleTimeMS=120000

Or programmatically via MongoClientSettings:

MongoClientSettings settings = MongoClientSettings.builder()
    .applyConnectionString(new ConnectionString("mongodb+srv://..."))
    .applyToConnectionPoolSettings(builder -> 
        builder.maxSize(150).minSize(10).maxConnectionIdleTime(120, TimeUnit.SECONDS))
    .build();
Configuring in Python (PyMongo)
from pymongo import MongoClient

client = MongoClient(
    "mongodb+srv://user:pass@cluster.mongodb.net/myDb",
    maxPoolSize=150,
    minPoolSize=10
)
Configuring in Golang
clientOptions := options.Client().
    ApplyURI("mongodb+srv://...").
    SetMaxPoolSize(150).
    SetMinPoolSize(10)

Step 3: Resolve Timeouts (Socket, Server Selection, Connect)

MongoDB has several different timeout configurations. Understanding which one to tune is critical.

  • connectTimeoutMS: (Default 10000ms). The maximum time the driver waits to establish a TCP connection to the server. Increase this only if you have high network latency during initial connection.
  • serverSelectionTimeoutMS: (Default 30000ms). The time the driver waits to find an available, appropriate server for an operation. If this fails, it's usually an IP whitelisting issue (Atlas), DNS issue, or the cluster is down.
  • socketTimeoutMS: (Default 0 - infinite, or sometimes set by ORMs). The time the driver waits for a response from the database after sending a query. If you get com.mongodb.mongosocketreadtimeoutexception, increase this, or better, optimize the slow query (add indexes).
  • maxIdleTimeMS: (Default 0 - infinite). The maximum time a connection can remain idle in the pool before being closed and replaced. Setting this (e.g., 60000ms) can help prune dead connections dropped by firewalls (like Azure or AWS NAT Gateways).

Step 4: AWS Lambda and Serverless Environments

A major cause of mongodb server selection timed out and exhausted connection pools is serverless functions (like AWS Lambda, Vercel, Netlify).

Because Lambdas scale out horizontally, each new container execution environment creates a new connection pool. If 1,000 Lambdas spin up, you get 1,000 connection pools. If maxPoolSize is 100, you are attempting to open 100,000 connections to your MongoDB cluster, which will instantly exceed the limits of most Atlas tiers and cause timeouts.

The Fix for AWS Lambda:
  1. Cache the Database Connection: Define the MongoClient outside the Lambda handler function. This allows subsequent invocations in the same container to reuse the existing connection pool.
  2. Reduce maxPoolSize: For serverless, set maxPoolSize very low (e.g., 1 or 2). A single Lambda container handles one request at a time, so it doesn't need a pool of 100 connections.
  3. Set serverSelectionTimeoutMS low: Fail fast so the Lambda doesn't hang and bill you for 30 seconds if the database is unreachable.

Step 5: Dealing with "Connection Pool Closed"

If you encounter a mongodb connection pool closed error, it means the application attempted to execute a query using a MongoClient instance that has already had .close() called on it. Ensure you are not calling mongoose.disconnect() or client.close() at the end of every HTTP request or Lambda invocation. Connections should be long-lived and managed by the pool, remaining open for the lifetime of the application process.

Conclusion

Resolving MongoDB connection pool and timeout errors requires a combination of proper driver configuration, query optimization, and architectural adjustments (especially in serverless environments). By monitoring your active connections and tuning maxPoolSize, socketTimeoutMS, and serverSelectionTimeoutMS, you can build resilient applications that handle high traffic without dropping database requests.

Frequently Asked Questions

javascript
const { MongoClient } = require('mongodb');

// Define client outside the handler to cache it across warm invocations
let cachedClient = null;

async function connectToDatabase() {
  if (cachedClient) {
    return cachedClient;
  }
  
  // Optimized connection options for Serverless / High Concurrency
  const client = new MongoClient(process.env.MONGODB_URI, {
    maxPoolSize: 2, // Low pool size for Lambda (increase for monolithic servers)
    serverSelectionTimeoutMS: 5000, // Fail fast if network is down
    maxIdleTimeMS: 60000 // Close idle connections after 1 min to prevent firewall drops
  });
  
  await client.connect();
  cachedClient = client;
  return client;
}

exports.handler = async (event, context) => {
  // Prevent Lambda from waiting for the event loop to empty
  context.callbackWaitsForEmptyEventLoop = false;
  
  try {
    const client = await connectToDatabase();
    const db = client.db('myDb');
    const result = await db.collection('users').findOne({});
    return { statusCode: 200, body: JSON.stringify(result) };
  } catch (error) {
    console.error("MongoDB Connection Error:", error);
    return { statusCode: 500, body: "Database Error" };
  }
};
E

Error Medic Editorial

Error Medic Editorial comprises senior DevOps engineers and SREs dedicated to solving complex database scaling and infrastructure challenges.

Sources

Related Guides