<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Mongodb on Krishnatejaswi S</title>
    <link>https://KTS-o7.github.io/bear/tags/mongodb/</link>
    <description>Recent content in Mongodb on Krishnatejaswi S</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <managingEditor>shentharkrishnatejaswi@gmail.com (Krishnatejaswi S)</managingEditor>
    <webMaster>shentharkrishnatejaswi@gmail.com (Krishnatejaswi S)</webMaster>
    <copyright>Krishnatejaswi S</copyright>
    <lastBuildDate>Sat, 06 Dec 2025 12:00:00 +0000</lastBuildDate>
    <atom:link href="https://KTS-o7.github.io/bear/tags/mongodb/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>MongoDB Connection Pool Exhaustion: Diagnosis, Fixes, and SDAM</title>
      <link>https://KTS-o7.github.io/bear/systems/connection_pool_exhaustion/</link>
      <pubDate>Sat, 06 Dec 2025 12:00:00 +0000</pubDate><author>shentharkrishnatejaswi@gmail.com (Krishnatejaswi S)</author>
      <guid>https://KTS-o7.github.io/bear/systems/connection_pool_exhaustion/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Why increasing maxPoolSize breaks your database.&lt;/strong&gt; A deep dive into MongoDB connection pooling, SDAM, WiredTiger tickets, and preventing transaction pinning.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;&#xA;&lt;p&gt;In the MongoDB ecosystem, the &amp;ldquo;connection pool&amp;rdquo; is often treated as a set-and-forget configuration within &lt;code&gt;MongoClient&lt;/code&gt;. However, when latency spikes or the database becomes unresponsive during high traffic, the pool is usually the first mechanism to break.&lt;/p&gt;&#xA;&lt;p&gt;Unlike relational databases that often sit behind proxies like PgBouncer, MongoDB drivers are &amp;ldquo;smart&amp;rdquo;—they maintain direct connections to multiple nodes in a Replica Set or Sharded Cluster. This complexity means exhaustion isn&amp;rsquo;t just about running out of sockets; it&amp;rsquo;s about topology monitoring, transaction pinning, and the brutal reality of the thread-per-connection model on the server side.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>Why increasing maxPoolSize breaks your database.</strong> A deep dive into MongoDB connection pooling, SDAM, WiredTiger tickets, and preventing transaction pinning.</p>
</blockquote>
<h2 id="introduction">Introduction</h2>
<p>In the MongoDB ecosystem, the &ldquo;connection pool&rdquo; is often treated as a set-and-forget configuration within <code>MongoClient</code>. However, when latency spikes or the database becomes unresponsive during high traffic, the pool is usually the first mechanism to break.</p>
<p>Unlike relational databases that often sit behind proxies like PgBouncer, MongoDB drivers are &ldquo;smart&rdquo;—they maintain direct connections to multiple nodes in a Replica Set or Sharded Cluster. This complexity means exhaustion isn&rsquo;t just about running out of sockets; it&rsquo;s about topology monitoring, transaction pinning, and the brutal reality of the thread-per-connection model on the server side.</p>
<h2 id="how-mongodb-connects-and-executes-queries">How MongoDB Connects and Executes Queries</h2>
<p>Before diving into pool exhaustion, it&rsquo;s worth understanding the lifecycle of a MongoDB connection and query. The process is more involved than a simple socket open/close.</p>
<h3 id="connection-establishment">Connection Establishment</h3>
<p>When your application calls <code>MongoClient.connect()</code>, the driver doesn&rsquo;t immediately open a socket. Instead, it:</p>
<ol>
<li>
<p><strong>TCP Socket Creation:</strong> Establishes a TCP/IP connection to the MongoDB server (default port 27017). This is a standard three-way handshake at the network layer.</p>
</li>
<li>
<p><strong>Wire Protocol Handshake:</strong> The client sends a <code>hello</code> command (formerly <code>isMaster</code> in older versions) to the server. This serves multiple purposes:</p>
<ul>
<li>Verifies the server is a MongoDB instance</li>
<li>Retrieves server capabilities (wire protocol version, supported authentication mechanisms)</li>
<li>In MongoDB 4.4+, can include <code>speculativeAuthenticate</code> to start authentication concurrently, reducing latency</li>
</ul>
</li>
<li>
<p><strong>Authentication:</strong> If credentials are provided, the client authenticates using mechanisms like SCRAM (Salted Challenge Response Authentication Mechanism):</p>
<ul>
<li>Client sends <code>saslStart</code> with chosen mechanism and initial payload</li>
<li>Server responds with a challenge</li>
<li>Client computes and sends proof derived from the challenge</li>
<li>Server verifies and grants access</li>
</ul>
</li>
</ol>
<p>This entire process—TCP handshake, TLS negotiation (if enabled), handshake, and authentication—typically takes 10-50ms depending on network latency and encryption overhead.</p>
<h3 id="query-execution-flow">Query Execution Flow</h3>
<p>Once authenticated, the connection is ready for operations. When you execute a query:</p>
<ol>
<li>
<p><strong>Connection Checkout:</strong> The driver checks out a connection from the pool (or waits if none are available—this is where exhaustion manifests).</p>
</li>
<li>
<p><strong>Message Construction:</strong> The query is serialized into MongoDB&rsquo;s wire protocol format. Modern MongoDB uses the <code>OP_MSG</code> opcode (introduced in 3.6), which includes:</p>
<ul>
<li>Standard message header (request ID, response-to ID)</li>
<li>Flags indicating message properties</li>
<li>Sections containing the actual command/query data</li>
<li>Optional checksum for integrity</li>
</ul>
</li>
<li>
<p><strong>Network Transmission:</strong> The message is sent over the TCP socket to the server.</p>
</li>
<li>
<p><strong>Server Processing:</strong> The <code>mongod</code> process:</p>
<ul>
<li>Receives the message on a dedicated thread (in the thread-per-connection model)</li>
<li>Parses the query</li>
<li>Plans execution strategy</li>
<li>Accesses data through WiredTiger (subject to ticket limits)</li>
<li>Creates a cursor for read operations</li>
</ul>
</li>
<li>
<p><strong>Response:</strong> The server sends results back over the same connection. For large result sets, the driver uses cursors to fetch documents in batches (default batch size is 101 documents).</p>
</li>
<li>
<p><strong>Connection Return:</strong> After the operation completes, the connection is returned to the pool for reuse—unless it&rsquo;s pinned to a transaction (see &ldquo;The Specific Pathology: Pinning&rdquo; below).</p>
</li>
</ol>
<h3 id="why-this-matters-for-pooling">Why This Matters for Pooling</h3>
<p>Each of these steps has latency and resource costs. The connection establishment overhead (10-50ms) is why pooling exists: reusing connections avoids paying this cost for every operation. However, the server-side thread allocation means each connection has a real memory and scheduling cost on <code>mongod</code>, which is why blindly increasing pool size backfires.</p>
<h2 id="connection-pool-internals">Connection Pool Internals</h2>
<p>Understanding how the pool manages connections internally is crucial for diagnosing exhaustion. The pool maintains connections in different states:</p>
<h3 id="connection-states">Connection States</h3>
<ol>
<li>
<p><strong>Idle:</strong> Connections that are established and authenticated but not currently in use. These sit in the pool, ready for immediate checkout. They consume server-side resources (thread + memory) but are available for operations.</p>
</li>
<li>
<p><strong>Checked Out:</strong> Connections actively executing an operation. When you call <code>collection.find()</code>, the driver checks out a connection, performs the operation, then returns it to idle—unless it&rsquo;s pinned (see &ldquo;The Specific Pathology: Pinning&rdquo;).</p>
</li>
<li>
<p><strong>Pending:</strong> Connections in the process of being established (TCP handshake, authentication). These don&rsquo;t count toward <code>maxPoolSize</code> until fully established.</p>
</li>
<li>
<p><strong>Closed:</strong> Connections that have been terminated, either due to errors, idle timeout, or explicit closure.</p>
</li>
</ol>
<h3 id="the-wait-queue">The Wait Queue</h3>
<p>When all connections are checked out and the pool has reached <code>maxPoolSize</code>, new requests enter a <strong>wait queue</strong>. This queue has its own limits:</p>
<ul>
<li><strong>waitQueueTimeoutMS:</strong> How long a request waits for a connection before throwing <code>ConnectionPoolCheckoutFailed</code>. Default is 0 (infinite wait—dangerous in production). <strong>Always set this</strong> to fail fast rather than hang indefinitely.</li>
</ul>
<p>If the wait queue fills up, your application starts failing requests. This is often the first symptom of pool exhaustion, not the root cause.</p>
<h3 id="connection-lifecycle">Connection Lifecycle</h3>
<p>The connection lifecycle follows these states and transitions:</p>





<pre tabindex="0"><code>[Not Created]
    │
    │ connect()
    ▼
[Pending] ── TCP handshake, authentication ──► [Idle]
    │                                              │
    │                                              │ Driver request
    │                                              ▼
    │                                        [Checked Out]
    │                                              │
    │                                              │ Operation complete
    │                                              │
    │                                              ▼
    │                                          [Idle] ◄──┐
    │                                              │     │
    │                                              │     │ Return to pool
    │                                              │     │
    │                                              │     │
    │                                              │ startTransaction()
    │                                              ▼     │
    │                                          [Pinned] ──┘
    │                                              │
    │                                              │ ʕ•ᴥ•ʔ DANGEROUS:
    │                                              │ Exclusive to session
    │                                              │ until commit/abort
    │                                              │
    │                                              │ commit/abortTransaction()
    │                                              ▼
    │                                          [Idle]
    │                                              │
    │                                              │ maxIdleTimeMS reached
    │                                              ▼
    │                                          [Closed]
    │                                              │
    └──────────────────────────────────────────────┘</code></pre><p><strong>State Descriptions:</strong></p>
<ul>
<li><strong>Pending:</strong> Connection is being established (TCP handshake, authentication)</li>
<li><strong>Idle:</strong> Connection is established and available in the pool for immediate use</li>
<li><strong>Checked Out:</strong> Connection is actively executing an operation</li>
<li><strong>Pinned:</strong> Connection is reserved for a transaction (removed from pool until transaction completes)</li>
<li><strong>Closed:</strong> Connection has been terminated</li>
</ul>
<p>The pool lazily creates connections up to <code>maxPoolSize</code>. If <code>minPoolSize &gt; 0</code>, it pre-warms that many connections at startup. Connections idle longer than <code>maxIdleTimeMS</code> are closed to free server resources.</p>
<h2 id="sdam-the-hidden-connection-overhead">SDAM: The Hidden Connection Overhead</h2>
<p>MongoDB drivers implement <strong>SDAM</strong> (Server Discovery and Monitoring), which maintains connections to <em>all</em> nodes in a Replica Set or Sharded Cluster, not just the primary.</p>
<h3 id="how-sdam-works">How SDAM Works</h3>
<ol>
<li>
<p><strong>Initial Discovery:</strong> On <code>MongoClient.connect()</code>, the driver connects to seed addresses and sends <code>hello</code> commands to discover all replica set members.</p>
</li>
<li>
<p><strong>Continuous Monitoring:</strong> Every <code>heartbeatFrequencyMS</code> (default 10 seconds), the driver sends <code>hello</code> to each known node to:</p>
<ul>
<li>Detect role changes (primary elections, step-downs)</li>
<li>Check server capabilities</li>
<li>Update topology description</li>
</ul>
</li>
<li>
<p><strong>Topology Events:</strong> When a primary election occurs, the driver:</p>
<ul>
<li>Closes connections to the old primary</li>
<li>Opens connections to the new primary</li>
<li>Potentially drains and recreates pools</li>
</ul>
</li>
</ol>
<h3 id="the-connection-multiplier">The Connection Multiplier</h3>
<p>If you have a 3-node replica set and set <code>maxPoolSize=50</code>, you&rsquo;re not creating 50 connections—you&rsquo;re creating up to <strong>150 connections</strong> (50 per node). SDAM maintains separate pools for each server in the topology.</p>
<p>During a primary election, the driver may temporarily exceed this as it transitions between nodes. If you&rsquo;re running 20 app instances, that&rsquo;s 3,000 potential connections during failover, even if only one node is serving traffic.</p>
<p><strong>Takeaway:</strong> Your <code>maxPoolSize</code> is per-server, not per-client. In replica sets, multiply by the number of nodes you&rsquo;re monitoring.</p>
<h2 id="connection-pool-configuration-options">Connection Pool Configuration Options</h2>
<p>Here are the key options that affect pool behavior:</p>
<ul>
<li>
<p><strong>maxPoolSize:</strong> Maximum connections per server. Default 100. This is the most commonly misconfigured setting.</p>
</li>
<li>
<p><strong>minPoolSize:</strong> Minimum idle connections to maintain. Default 0. Setting this &gt; 0 pre-warms the pool but consumes server resources even when idle.</p>
</li>
<li>
<p><strong>maxIdleTimeMS:</strong> Close idle connections after this duration. Default 0 (never close). Useful in serverless where you want aggressive cleanup.</p>
</li>
<li>
<p><strong>waitQueueTimeoutMS:</strong> How long to wait for a connection before failing. Default 0 (wait forever—dangerous in production). <strong>Always set this</strong> to fail fast rather than hang indefinitely.</p>
</li>
<li>
<p><strong>connectTimeoutMS:</strong> Timeout for initial connection establishment. Default 30 seconds.</p>
</li>
<li>
<p><strong>socketTimeoutMS:</strong> Timeout for individual operations. Default 0 (no timeout). Different from connection checkout timeout.</p>
</li>
<li>
<p><strong>heartbeatFrequencyMS:</strong> How often SDAM checks server status. Default 10 seconds. Lower values detect failovers faster but increase network overhead.</p>
</li>
<li>
<p><strong>TCP Keepalive:</strong> Often overlooked. If your app sits behind a firewall or load balancer (e.g., AWS Network Load Balancer) with a 350s idle timeout, and your pool has <code>maxIdleTimeMS</code> &gt; 350s, the LB will silently drop the connection. The driver won&rsquo;t know until it tries to write to the socket and fails. <strong>Important:</strong> <code>socketTimeoutMS</code> does not send keepalive probes. <strong>Best Practice:</strong> Set <code>maxIdleTimeMS</code> to be slightly lower than your infrastructure&rsquo;s timeout (e.g., 120000ms). On Linux, also tune <code>net.ipv4.tcp_keepalive_time</code> to be less than the cloud provider&rsquo;s timeout (usually &lt; 120 seconds).</p>
</li>
</ul>
<p><strong>Example Configuration (Node.js):</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">const</span> <span class="nx">client</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">MongoClient</span><span class="p">(</span><span class="nx">uri</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="nx">maxPoolSize</span><span class="o">:</span> <span class="mi">10</span><span class="p">,</span>              <span class="c1">// Small pool for 16-core DB
</span></span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="nx">minPoolSize</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span>               <span class="c1">// Pre-warm 2 connections
</span></span></span><span class="line"><span class="ln">4</span><span class="cl">  <span class="nx">maxIdleTimeMS</span><span class="o">:</span> <span class="mi">30000</span><span class="p">,</span>         <span class="c1">// Close idle after 30s
</span></span></span><span class="line"><span class="ln">5</span><span class="cl">  <span class="nx">waitQueueTimeoutMS</span><span class="o">:</span> <span class="mi">5000</span><span class="p">,</span>     <span class="c1">// Fail after 5s wait
</span></span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="nx">connectTimeoutMS</span><span class="o">:</span> <span class="mi">10000</span><span class="p">,</span>      <span class="c1">// 10s connection timeout
</span></span></span><span class="line"><span class="ln">7</span><span class="cl">  <span class="nx">socketTimeoutMS</span><span class="o">:</span> <span class="mi">45000</span><span class="p">,</span>       <span class="c1">// 45s operation timeout
</span></span></span><span class="line"><span class="ln">8</span><span class="cl">  <span class="nx">heartbeatFrequencyMS</span><span class="o">:</span> <span class="mi">10000</span>   <span class="c1">// Check every 10s
</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="p">});</span></span></span></code></pre></div><h2 id="the-cost-of-a-connection-in-mongod">The Cost of a Connection in mongod</h2>
<p>While MongoDB has moved toward a more asynchronous networking model in recent versions, historically (and effectively, for resource planning), it relies on a synchronous thread-per-connection model.</p>
<p>When your application opens a new connection:</p>
<ol>
<li><strong>Network:</strong> TCP handshake + TLS negotiation (CPU intensive).</li>
<li><strong>Authentication:</strong> SCRAM or X.509 handshakes occur.</li>
<li><strong>Memory:</strong> The <code>mongod</code> process allocates stack memory (typically ~1MB) for the thread handling that connection.</li>
<li><strong>Context:</strong> The internal <code>serviceExecutor</code> must schedule this thread.</li>
</ol>
<p>If you set <code>maxPoolSize=100</code> on 50 Kubernetes pods, you are allowing 5,000 potential concurrent threads on the Primary. Before you hit hardware limits, you will likely hit <strong>WiredTiger ticket exhaustion</strong>.</p>
<h2 id="the-math-sizing-for-wiredtiger">The Math: Sizing for WiredTiger</h2>
<p>The naive approach is to increase <code>maxPoolSize</code> when you see <code>ConnectionPoolCheckoutFailed</code> errors. This is usually wrong.</p>
<p>WiredTiger (the storage engine) uses &ldquo;tickets&rdquo; to control concurrency. In MongoDB versions prior to 7.0, there are 128 read tickets and 128 write tickets by default. MongoDB 7.0+ uses dynamic ticketing that adjusts based on workload, but still caps at 128 per type. <strong>Note on MongoDB 7.0+:</strong> While the hard cap remains 128 tickets, the dynamic algorithm often lowers this limit (e.g., to 60 or 80) in real-time to preserve latency. This means you might see queuing before you hit 128 concurrent active queries. If 1,000 connections try to read simultaneously, 872 of them are just queuing inside the kernel, consuming RAM but doing zero work.</p>
<p>The optimal pool size formula remains tied to hardware capability, not traffic volume:</p>
$$
PoolSize \approx \frac{\text{CoreCount} \times 2}{\text{AppInstances}}
$$<p>If your database has 16 cores and you have 20 application instances, a <code>maxPoolSize</code> of 100 is overkill. You might actually achieve <em>higher</em> throughput with a pool size of 2 or 3 per instance, preventing the database from spending all its cycles on context switching.</p>
<p><strong>Note:</strong> The <code>(Cores * 2)</code> rule applies strictly to the <em>active</em> executing threads. You can have a larger pool to handle network jitter, but <code>waitQueueTimeoutMS</code> is your safety valve. If you need 100 connections to saturate the CPU, your queries are likely too slow (unindexed). Fix the queries, don&rsquo;t inflate the pool.</p>
<p><strong>Workload Considerations:</strong></p>
<ul>
<li><strong>CPU-Bound Workloads:</strong> The formula above applies directly. Each active connection should map to an executing thread.</li>
<li><strong>IO-Bound Workloads:</strong> If the working set doesn&rsquo;t fit in RAM and disk I/O is the bottleneck, a slightly larger pool <em>might</em> help hide I/O latency, though typically <code>minPoolSize</code> is more relevant here to avoid establishing connections during I/O spikes.</li>
<li><strong>Atlas Proxy / Serverless:</strong> If using <strong>MongoDB Atlas Serverless</strong> or the <strong>Atlas Proxy</strong>, the connection limits are handled differently (via a proxy layer), and the &ldquo;100 limit&rdquo; on the client side is less about server stress and more about client-side throttling.</li>
</ul>
<h2 id="the-specific-pathology-pinning">The Specific Pathology: &ldquo;Pinning&rdquo;</h2>
<p>Since you are versed in Mongo, you know the drivers implement the <strong>SDAM</strong> (Server Discovery and Monitoring) specification. But the most dangerous feature regarding pooling is <strong>Client-Side Operation Pinning</strong>, specifically with Transactions.</p>
<p>In a standard read/write, the driver grabs a connection, executes, and returns it.
In a Transaction (Multi-Document ACID):</p>
<ol>
<li><code>session.startTransaction()</code> is called.</li>
<li>The driver <strong>pins</strong> a connection to that session.</li>
<li><strong>That connection is removed from the pool</strong> and reserved exclusively for this session until <code>commitTransaction</code> or <code>abortTransaction</code> is called.</li>
</ol>
<h3 id="the-exhaustion-scenario">The Exhaustion Scenario</h3>
<p>If you have <code>maxPoolSize=50</code> and an API endpoint that takes 2 seconds to execute some logic <em>inside</em> a transaction, you can only serve 25 requests per second. The 26th request will wait. If the logic hangs (e.g., waiting for a third-party HTTP call), you will drain the pool instantly, causing a cascading failure across the app.</p>
<blockquote>
<p><strong>ʕ•ᴥ•ʔ Critical Rule:</strong> Never perform network I/O or long computations inside a MongoDB transaction. Transactions should be atomic, fast, and only contain database operations.</p>
</blockquote>
<h2 id="practical-examples-the-right-way-vs-the-wrong-way">Practical Examples: The Right Way vs. The Wrong Way</h2>
<h3 id="proper-client-initialization">Proper Client Initialization</h3>
<p><strong>Node.js - Express (Correct):</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// app.js - Initialize once at startup
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">MongoClient</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;mongodb&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="kd">let</span> <span class="nx">client</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="kd">let</span> <span class="nx">db</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">initDatabase</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="nx">client</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">MongoClient</span><span class="p">(</span><span class="nx">uri</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="nx">maxPoolSize</span><span class="o">:</span> <span class="mi">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="nx">waitQueueTimeoutMS</span><span class="o">:</span> <span class="mi">5000</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="kr">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">connect</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="nx">db</span> <span class="o">=</span> <span class="nx">client</span><span class="p">.</span><span class="nx">db</span><span class="p">(</span><span class="s1">&#39;myapp&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Database connected&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1">// Initialize before starting server
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="nx">initDatabase</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="nx">app</span><span class="p">.</span><span class="nx">listen</span><span class="p">(</span><span class="mi">3000</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1">// Use the same client instance everywhere
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">&#39;/users&#39;</span><span class="p">,</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="kr">const</span> <span class="nx">users</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">db</span><span class="p">.</span><span class="nx">collection</span><span class="p">(</span><span class="s1">&#39;users&#39;</span><span class="p">).</span><span class="nx">find</span><span class="p">({}).</span><span class="nx">toArray</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">(</span><span class="nx">users</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="p">});</span></span></span></code></pre></div><p><strong>Node.js - Express (Wrong - Creates New Pool Per Request):</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// ʕ•̀ω•́ʔ DON&#39;T DO THIS
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">&#39;/users&#39;</span><span class="p">,</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="kr">const</span> <span class="nx">client</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">MongoClient</span><span class="p">(</span><span class="nx">uri</span><span class="p">);</span>  <span class="c1">// New pool every request!
</span></span></span><span class="line"><span class="ln">4</span><span class="cl">  <span class="kr">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">connect</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">  <span class="kr">const</span> <span class="nx">db</span> <span class="o">=</span> <span class="nx">client</span><span class="p">.</span><span class="nx">db</span><span class="p">(</span><span class="s1">&#39;myapp&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="kr">const</span> <span class="nx">users</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">db</span><span class="p">.</span><span class="nx">collection</span><span class="p">(</span><span class="s1">&#39;users&#39;</span><span class="p">).</span><span class="nx">find</span><span class="p">({}).</span><span class="nx">toArray</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">  <span class="kr">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>  <span class="c1">// Closes pool, but too late
</span></span></span><span class="line"><span class="ln">8</span><span class="cl">  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">(</span><span class="nx">users</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="p">});</span></span></span></code></pre></div><p><strong>Python - Gunicorn (Correct):</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># app.py - Create client after fork</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">from</span> <span class="nn">pymongo</span> <span class="kn">import</span> <span class="n">MongoClient</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">client</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">db</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">def</span> <span class="nf">on_starting</span><span class="p">(</span><span class="n">server</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="c1"># This runs in the master process</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">def</span> <span class="nf">when_ready</span><span class="p">(</span><span class="n">server</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="c1"># This runs in each worker after fork</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="k">global</span> <span class="n">client</span><span class="p">,</span> <span class="n">db</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">client</span> <span class="o">=</span> <span class="n">MongoClient</span><span class="p">(</span><span class="n">uri</span><span class="p">,</span> <span class="n">maxPoolSize</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="n">db</span> <span class="o">=</span> <span class="n">client</span><span class="p">[</span><span class="s1">&#39;myapp&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="nd">@app.route</span><span class="p">(</span><span class="s1">&#39;/users&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">def</span> <span class="nf">get_users</span><span class="p">():</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="n">users</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">users</span><span class="o">.</span><span class="n">find</span><span class="p">({}))</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="k">return</span> <span class="n">jsonify</span><span class="p">(</span><span class="n">users</span><span class="p">)</span></span></span></code></pre></div><p><strong>Python - Gunicorn (Wrong - Fork-Unsafe):</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># ʕ•̀ω•́ʔ DON&#39;T DO THIS</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">from</span> <span class="nn">pymongo</span> <span class="kn">import</span> <span class="n">MongoClient</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># Created in master process - NOT fork-safe!</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">client</span> <span class="o">=</span> <span class="n">MongoClient</span><span class="p">(</span><span class="n">uri</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">db</span> <span class="o">=</span> <span class="n">client</span><span class="p">[</span><span class="s1">&#39;myapp&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nd">@app.route</span><span class="p">(</span><span class="s1">&#39;/users&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">def</span> <span class="nf">get_users</span><span class="p">():</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="c1"># Child processes share parent&#39;s file descriptors = deadlocks</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="c1"># This often results in AutoReconnect errors or </span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="c1"># &#39;file descriptor bad&#39; errors because the socket </span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="c1"># is shared across memory boundaries</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">users</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">users</span><span class="o">.</span><span class="n">find</span><span class="p">({}))</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="k">return</span> <span class="n">jsonify</span><span class="p">(</span><span class="n">users</span><span class="p">)</span></span></span></code></pre></div><p><strong>Java - Spring Boot:</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nl">spring</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">  </span><span class="n">data</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="n">mongodb</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">      </span><span class="n">uri</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;mongodb://user:pass@host1:27017,host2:27017/?replicaSet=myRS&amp;maxPoolSize=10&amp;minPoolSize=2&amp;maxIdleTimeMS=30000&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">// Or if using a Configurer Bean (safe way to customize without breaking auto-config)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nd">@Configuration</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">MongoConfig</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="nd">@Bean</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">MongoClientSettingsBuilderCustomizer</span><span class="w"> </span><span class="nf">customizer</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">builder</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">builder</span><span class="p">.</span><span class="na">applyToConnectionPoolSettings</span><span class="p">(</span><span class="n">pool</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">            </span><span class="n">pool</span><span class="p">.</span><span class="na">maxSize</span><span class="p">(</span><span class="n">10</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">            </span><span class="n">pool</span><span class="p">.</span><span class="na">maxWaitTime</span><span class="p">(</span><span class="n">5</span><span class="p">,</span><span class="w"> </span><span class="n">TimeUnit</span><span class="p">.</span><span class="na">SECONDS</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="p">});</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><strong>The Health Check Trap:</strong></p>
<blockquote>
<p><strong>ʕ•ᴥ•ʔ Warning:</strong> Do not create a new <code>MongoClient</code> inside your Kubernetes readiness/liveness probe. This is a classic DOS attack on your own database. The probe runs every 10s; if it creates a new connection each time without proper closure, you will exhaust the server limits in minutes. Use the singleton client to run a lightweight command like <code>db.command({ ping: 1 })</code>.</p>
</blockquote>
<h3 id="monitoring-pool-metrics">Monitoring Pool Metrics</h3>
<p><strong>Node.js - Command Monitoring:</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">MongoClient</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;mongodb&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kr">const</span> <span class="nx">client</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">MongoClient</span><span class="p">(</span><span class="nx">uri</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="nx">monitorCommands</span><span class="o">:</span> <span class="kc">true</span>  <span class="c1">// Enable command monitoring
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">client</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;commandStarted&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Command:&#39;</span><span class="p">,</span> <span class="nx">event</span><span class="p">.</span><span class="nx">commandName</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">client</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;commandSucceeded&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Duration:&#39;</span><span class="p">,</span> <span class="nx">event</span><span class="p">.</span><span class="nx">duration</span><span class="p">,</span> <span class="s1">&#39;ms&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1">// For connection pool metrics, use pool monitoring events
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nx">client</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;connectionCheckedOut&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">duration</span> <span class="o">&gt;</span> <span class="mi">100</span><span class="p">)</span> <span class="p">{</span>  <span class="c1">// Checkout took &gt; 100ms
</span></span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">warn</span><span class="p">(</span><span class="s1">&#39;Slow connection checkout:&#39;</span><span class="p">,</span> <span class="nx">event</span><span class="p">.</span><span class="nx">duration</span><span class="p">,</span> <span class="s1">&#39;ms&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="nx">client</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;commandFailed&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">error</span><span class="p">.</span><span class="nx">code</span> <span class="o">===</span> <span class="s1">&#39;ConnectionPoolCheckoutFailed&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;Pool exhausted!&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="p">});</span></span></span></code></pre></div><p><strong>Go - Pool Monitoring:</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="s">&#34;go.mongodb.org/mongo-driver/event&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="s">&#34;go.mongodb.org/mongo-driver/mongo&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">poolMonitor</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">event</span><span class="p">.</span><span class="nx">PoolMonitor</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="nx">Event</span><span class="p">:</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">evt</span><span class="w"> </span><span class="o">*</span><span class="nx">event</span><span class="p">.</span><span class="nx">PoolEvent</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">        </span><span class="k">switch</span><span class="w"> </span><span class="nx">evt</span><span class="p">.</span><span class="nx">Type</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="nx">event</span><span class="p">.</span><span class="nx">ConnectionCreated</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Connection created&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="nx">event</span><span class="p">.</span><span class="nx">ConnectionCheckedOut</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Connection checked out&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="nx">event</span><span class="p">.</span><span class="nx">ConnectionCheckoutFailed</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Checkout failed:&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">evt</span><span class="p">.</span><span class="nx">Reason</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="nx">event</span><span class="p">.</span><span class="nx">ConnectionReturned</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Connection returned&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="nx">clientOptions</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">options</span><span class="p">.</span><span class="nf">Client</span><span class="p">().</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">    </span><span class="nf">ApplyURI</span><span class="p">(</span><span class="nx">uri</span><span class="p">).</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">    </span><span class="nf">SetPoolMonitor</span><span class="p">(</span><span class="nx">poolMonitor</span><span class="p">).</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">    </span><span class="nf">SetMaxPoolSize</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="nx">client</span><span class="p">,</span><span class="w"> </span><span class="nx">_</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">mongo</span><span class="p">.</span><span class="nf">Connect</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">TODO</span><span class="p">(),</span><span class="w"> </span><span class="nx">clientOptions</span><span class="p">)</span></span></span></code></pre></div><h2 id="driver-nuances--pitfalls">Driver Nuances &amp; Pitfalls</h2>
<h3 id="nodejs-mongoose--native-driver">Node.js (Mongoose / Native Driver)</h3>
<p>The Node driver is asynchronous, but the pool logic is strict.</p>
<ul>
<li><strong>The Singleton Mistake:</strong> A common pattern in serverless or improper Express setup is calling <code>MongoClient.connect()</code> inside the route handler. This creates a new pool for every request, rapidly hitting the Atlas connection limit (e.g., 1,500 connections on M10). Always initialize the client <em>once</em> at startup.</li>
<li><strong>Unified Topology:</strong> The Node.js driver (v4.0+) uses Unified Topology by default (it&rsquo;s the only option). This keeps a background monitoring thread that can detect step-downs. If your pool churns during a primary election, check if your <code>heartbeatFrequencyMS</code> is tuned correctly.</li>
</ul>
<h3 id="python-pymongo">Python (PyMongo)</h3>
<ul>
<li><strong>Fork Safety:</strong> <code>MongoClient</code> is not fork-safe. If you use <code>gunicorn</code> or <code>uwsgi</code> with forking, you <strong>must</strong> create the client <em>after</em> the fork (e.g., inside the worker process initialization), or you will end up with child processes trying to use the parent&rsquo;s file descriptors, leading to deadlocks and obscure socket errors. This often manifests as <code>AutoReconnect</code> errors or &ldquo;file descriptor bad&rdquo; errors.</li>
<li><strong>Wait Queue:</strong> In PyMongo 4.0+, <code>waitQueueMultiple</code> was removed. Use <code>waitQueueTimeoutMS</code> to control how long threads wait for connections. Limit your application&rsquo;s thread pool size to control concurrency.</li>
</ul>
<h3 id="java-spring-boot--mongodb-java-driver">Java (Spring Boot / MongoDB Java Driver)</h3>
<ul>
<li><strong>Automatic Pooling:</strong> The <code>MongoTemplate</code> handles pooling automatically. Ensure <code>spring.data.mongodb.database</code> settings do not override defaults with aggressive timeouts. The Java driver&rsquo;s default <code>maxPoolSize</code> is 100, which is often too high for production.</li>
</ul>
<h3 id="go-mongo-go-driver">Go (mongo-go-driver)</h3>
<ul>
<li>The Go driver respects <code>context</code>. If your <code>Connect</code> or <code>Find</code> context times out, the connection might be closed or returned to the pool depending on the stage.</li>
<li>Monitor <code>PoolEvent</code> via the <code>SetPoolMonitor</code> option to visualize exactly when connections are created vs. checked out.</li>
</ul>
<h2 id="serverless--atlas-implications">Serverless &amp; Atlas Implications</h2>
<p>In MongoDB Atlas, connection limits are hard tiers.</p>
<ul>
<li><strong>M0 (Free Tier):</strong> 500 connections per node</li>
<li><strong>M10:</strong> 1,500 connections per node</li>
<li><strong>M20:</strong> 3,000 connections per node</li>
<li><strong>Serverless Instances:</strong> These bill by &ldquo;Read Processing Units&rdquo; but still require connection management.</li>
</ul>
<p>If you are running on AWS Lambda or Vercel, you cannot maintain a persistent pool. Every &ldquo;warm&rdquo; container might keep a pool of 10 open. 100 concurrent Lambdas = 1,000 connections.
<strong>Solution:</strong> You typically cannot use standard pooling here. You must aggressively close connections (set <code>maxIdleTimeMS</code> very low) or use the <strong>Atlas Data API</strong> (HTTP based) instead of the TCP driver to avoid socket exhaustion.</p>
<h2 id="monitoring--diagnosis">Monitoring &amp; Diagnosis</h2>
<p>How do you know it&rsquo;s a pool issue and not a slow query?</p>
<ol>
<li>
<p><strong>Check the Server:</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="nx">db</span><span class="p">.</span><span class="nx">serverStatus</span><span class="p">().</span><span class="nx">connections</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">// look for { &#34;current&#34;: 1200, &#34;available&#34;: 50 }
</span></span></span></code></pre></div><p>If <code>current</code> is high, your app is leaking connections or scaling too wide.</p>
</li>
<li>
<p><strong>Check the Queue:</strong>
Monitor WiredTiger ticket usage via <code>db.serverStatus().wiredTiger.concurrentTransactions</code> or connection counts. If active connections significantly exceed your pool size or WiredTiger tickets are exhausted, your latency is due to queuing, not network speed.</p>
</li>
<li>
<p><strong>Zombie Connections:</strong>
When a client crashes hard (OOM Kill) without sending a TCP FIN, the server keeps the connection open until the server-side <code>tcp_keepalive</code> times out. In high-churn Kubernetes environments, this can exhaust the server&rsquo;s file descriptors even if the <em>active</em> pool count seems low. This reinforces the need for aggressive <code>maxIdleTimeMS</code> and proper TCP keepalive configuration.</p>
</li>
<li>
<p><strong>Driver Metrics:</strong>
Enable command monitoring in your driver. If <code>connectionCheckoutTime</code> spikes while <code>commandDuration</code> (server execution time) stays low, the problem is client-side exhaustion. The database is fast; the line to get to it is slow.</p>
</li>
<li>
<p><strong>Connection Churn Rate:</strong>
Monitor the rate of new connections (<code>connectionsCreated</code> per second). If this is high (&gt;10/sec per node) while your total pool count is stable, your <code>maxIdleTimeMS</code> is likely too low. You are burning server CPU on SSL handshakes rather than reusing connections. Check <code>db.serverStatus().connections.created</code> and compare it to your connection pool turnover rate.</p>
</li>
</ol>
<h2 id="conclusion">Conclusion</h2>
<p>In MongoDB, &ldquo;Connection Pool Exhaustion&rdquo; is often a misnomer for &ldquo;Transaction Pinning&rdquo; or &ldquo;Overscaling.&rdquo; Because the drivers are complex state machines interacting with Replica Sets, simply bumping <code>maxPoolSize</code> masks the problem until it crashes <code>mongod</code> via memory pressure or ticket exhaustion.</p>
<p>The fix is almost always:</p>
<ol>
<li>Lower the pool size to match hardware (<code>threads &lt;= cores</code>).</li>
<li>Shorten transaction scopes to microseconds.</li>
<li>Treat the <code>MongoClient</code> as a sacred singleton.</li>
</ol>
]]></content:encoded>
    </item>
  </channel>
</rss>
