<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>HikariCP | John Nessime</title>
	<atom:link href="https://john-nessime.com/blog/tag/hikaricp/feed/" rel="self" type="application/rss+xml" />
	<link>https://john-nessime.com/blog/tag/hikaricp/</link>
	<description>Cloud, DevOps, Data &#38; AI — Built, Tested, Explained</description>
	<lastBuildDate>Thu, 08 Oct 2026 06:17:49 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>https://john-nessime.com/blog/wp-content/uploads/2026/07/cropped-jn-32x32.png</url>
	<title>HikariCP | John Nessime</title>
	<link>https://john-nessime.com/blog/tag/hikaricp/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Plenty of CPU, Zero Connections: Debugging Connection Pool Exhaustion on AWS</title>
		<link>https://john-nessime.com/blog/cloud-computing/connection-pool-exhaustion-aws/</link>
		
		<dc:creator><![CDATA[John Nessime]]></dc:creator>
		<pubDate>Thu, 08 Oct 2026 06:17:49 +0000</pubDate>
				<category><![CDATA[Cloud Computing]]></category>
		<category><![CDATA[Databases]]></category>
		<category><![CDATA[Site Reliability Engineering]]></category>
		<category><![CDATA[Troubleshooting]]></category>
		<category><![CDATA[Amazon Aurora]]></category>
		<category><![CDATA[Amazon ECS]]></category>
		<category><![CDATA[Amazon RDS]]></category>
		<category><![CDATA[CloudWatch]]></category>
		<category><![CDATA[Connection Leaks]]></category>
		<category><![CDATA[Connection Pinning]]></category>
		<category><![CDATA[Connection Pooling]]></category>
		<category><![CDATA[HikariCP]]></category>
		<category><![CDATA[Idle in Transaction]]></category>
		<category><![CDATA[Incident Response]]></category>
		<category><![CDATA[max_connections]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Performance Tuning]]></category>
		<category><![CDATA[PgBouncer]]></category>
		<category><![CDATA[Pool Exhaustion]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[RDS Proxy]]></category>
		<category><![CDATA[SRE]]></category>
		<guid isPermaLink="false">https://john-nessime.com/blog/?p=1146</guid>

					<description><![CDATA[<p>Pool timeouts while RDS sits at low CPU? The database isn't the bottleneck. Here's how to tell whether your app pool, max_connections or RDS Proxy ran out, the SQL that proves it, and the fixes that survive the next deploy.</p>
<p>The post <a href="https://john-nessime.com/blog/cloud-computing/connection-pool-exhaustion-aws/">Plenty of CPU, Zero Connections: Debugging Connection Pool Exhaustion on AWS</a> appeared first on <a href="https://john-nessime.com/blog">John Nessime</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">The alert says p99 latency is climbing. The application logs are full of <code>Connection is not available, request timed out after 30000ms</code>. Someone opens the RDS console, looks at CPU sitting low, freeable memory looking healthy and <code>DatabaseConnections</code> nowhere near the limit, and says the obvious thing: &#8220;The database is fine.&#8221;</p>



<p class="wp-block-paragraph">They&#8217;re right. The database is fine. That&#8217;s exactly why this incident drags on for an hour instead of ten minutes.</p>



<p class="wp-block-paragraph">Connection pool exhaustion on AWS is rarely a capacity problem. It&#8217;s a queueing problem, and the queue forms in one of three places: inside your application&#8217;s pool, at the database&#8217;s connection limit, or inside a proxy sitting between the two. Each layer has its own limit, its own error message and its own fix. Resizing the instance fixes none of them reliably, and sometimes makes one worse.</p>



<p class="wp-block-paragraph">Below: how to tell which layer ran out, the SQL and metrics that prove it, and fixes that survive the next deploy. Examples use RDS and Aurora for PostgreSQL and MySQL, plus HikariCP, SQLAlchemy and node-postgres where their defaults matter.</p>



<h2 class="wp-block-heading">First question: which layer ran out?</h2>



<p class="wp-block-paragraph">Before touching anything, figure out where requests are waiting. The error text usually tells you, if you read it literally instead of pattern-matching on &#8220;connection&#8221;.</p>



<ul class="wp-block-list">
<li><strong>The application pool is empty.</strong> The error comes from your pool library, not the database. HikariCP says the connection is not available and the request timed out. SQLAlchemy raises a <code>QueuePool limit ... reached</code> timeout. The database never saw these requests at all.</li>



<li><strong>The database refused new connections.</strong> The error comes from the server. PostgreSQL returns a <code>FATAL</code> about too many clients or about the remaining connection slots being reserved (the exact wording varies by major version). MySQL returns error 1040, <code>Too many connections</code>.</li>



<li><strong>The proxy is holding the queue.</strong> With RDS Proxy in the path, clients connect fine but wait for a database connection to be borrowed, then time out after the proxy&#8217;s borrow timeout.</li>
</ul>



<p class="wp-block-paragraph">Then compare what the database is actually holding against its limit. On PostgreSQL:</p>



<pre class="wp-block-code"><code>SHOW max_connections;

SELECT count(*) AS client_connections
FROM pg_stat_activity
WHERE backend_type = 'client backend';</code></pre>



<p class="wp-block-paragraph">On MySQL, <code>Max_used_connections</code> is the high-water mark since the last restart, which is useful when the spike has already passed:</p>



<pre class="wp-block-code"><code>SHOW VARIABLES LIKE 'max_connections';
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Max_used_connections';</code></pre>



<p class="wp-block-paragraph">If the app is throwing pool timeouts while the server is well under its limit, you&#8217;re in the first failure family. If the server is at the limit, it&#8217;s the second. That single comparison saves you from the most common wrong move, which is raising <code>max_connections</code> when the database was never the bottleneck.</p>



<h2 class="wp-block-heading">Pool exhaustion with an idle database: the one that hides</h2>



<p class="wp-block-paragraph">This is the failure that fools people, because every database graph looks healthy while the application is effectively down.</p>



<p class="wp-block-paragraph">A pool hands out a fixed number of connections per process. A request borrows one, and nobody else can use it until it&#8217;s returned. The pool doesn&#8217;t care whether the request is running SQL during that time. If your code opens a transaction, runs a query, then calls a payment API, uploads to S3 or sends an email before committing, the connection sits borrowed and idle for the whole round trip.</p>



<p class="wp-block-paragraph">Now let that third-party API slow down. Each request holds its connection longer, the pool drains, new requests queue for a connection, and they time out. The database sees a handful of sessions doing nothing. CPU drops, because less SQL is running than usual. The dashboard gets greener as the outage gets worse.</p>



<p class="wp-block-paragraph">So <strong>a slow dependency that has nothing to do with the database can exhaust your database pool</strong>, and database metrics will never show it.</p>



<h3 class="wp-block-heading">Prove it with pg_stat_activity</h3>



<p class="wp-block-paragraph">Group sessions by state and by who owns them. The <code>state</code> column is the evidence; <code>idle in transaction</code> means a client opened a transaction and then went quiet without committing.</p>



<pre class="wp-block-code"><code>SELECT state,
       application_name,
       count(*) AS sessions,
       max(now() - xact_start) AS oldest_transaction
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY state, application_name
ORDER BY sessions DESC;</code></pre>



<p class="wp-block-paragraph">Then list the worst offenders. The <code>query</code> column shows the last statement the session ran, not what it&#8217;s doing now, which is exactly what you want here: it tells you where in the code the transaction was left open.</p>



<pre class="wp-block-code"><code>SELECT pid,
       application_name,
       client_addr,
       now() - xact_start AS transaction_age,
       left(query, 80) AS last_statement
FROM pg_stat_activity
WHERE state = 'idle in transaction'
ORDER BY xact_start;</code></pre>



<p class="wp-block-paragraph">On MySQL, open transactions live in <code>information_schema.INNODB_TRX</code>. The thread ID joins back to the process list so you can see the host and user:</p>



<pre class="wp-block-code"><code>SELECT trx_mysql_thread_id, trx_started, trx_state
FROM information_schema.INNODB_TRX
ORDER BY trx_started;</code></pre>



<p class="wp-block-paragraph">One thing makes all of this much faster: set <code>application_name</code> per service in the connection string. Without it, every session looks the same and you&#8217;re guessing from IP addresses that change with every task restart.</p>



<h3 class="wp-block-heading">Leak or slow holder?</h3>



<p class="wp-block-paragraph">Two different bugs produce the same symptom. A <strong>leak</strong> is a code path that borrows a connection and never returns it, usually an exception path that skips the close. A <strong>slow holder</strong> returns the connection eventually but keeps it through non-database work.</p>



<p class="wp-block-paragraph">The tell is recovery. Leaks never recover without a restart; the pool ratchets down one connection at a time until it&#8217;s empty. Slow holders recover when the slow dependency recovers. If restarting the service &#8220;fixes&#8221; the incident every few days, suspect a leak.</p>



<p class="wp-block-paragraph">HikariCP has <code>leakDetectionThreshold</code>, which is off by default. Set it to a value longer than your slowest legitimate request, and it logs the stack trace of wherever a connection was borrowed and held past that threshold. That stack trace is usually the whole diagnosis. Other pools have their own hooks; if yours doesn&#8217;t, log borrow duration yourself around the checkout.</p>



<h3 class="wp-block-heading">Fixes that actually hold</h3>



<ol class="wp-block-list">
<li><strong>Stop holding connections across network calls.</strong> Do the external call first, then open the transaction, write, and commit. If you need the call&#8217;s result inside the transaction, restructure so the transaction is short. This is the real fix; everything else is a guardrail.</li>



<li><strong>Put a server-side ceiling on idle transactions.</strong> PostgreSQL&#8217;s <code>idle_in_transaction_session_timeout</code> terminates a session that sits idle inside a transaction past the limit. The app will see an error, and that&#8217;s the point: a loud failure beats a silent pool drain. You can set it in the parameter group or scope it to the application role.</li>



<li><strong>Fail fast on checkout.</strong> A web request that waits 30 seconds for a connection is worse than one that fails in two. Match the pool&#8217;s acquisition timeout to what the caller will tolerate.</li>
</ol>



<pre class="wp-block-code"><code>ALTER ROLE app_user SET idle_in_transaction_session_timeout = '60s';</code></pre>



<p class="wp-block-paragraph">The role-level setting applies to new sessions for that role, so existing pooled connections keep the old value until they&#8217;re recycled. Pick a limit comfortably above your longest legitimate pause, or you&#8217;ll kill batch jobs that were behaving.</p>



<p class="wp-block-paragraph">MySQL has no direct equivalent. <code>wait_timeout</code> closes idle sessions, but it&#8217;s a blunt setting that also affects pooled connections sitting legitimately idle, so tune it alongside your pool&#8217;s own idle and max-lifetime settings rather than instead of fixing the code.</p>



<p class="wp-block-paragraph">Watch the node-postgres default specifically. Its pool <code>connectionTimeoutMillis</code> defaults to 0, which means no timeout: when the pool is empty, requests wait forever. You don&#8217;t get an error to alert on; you get hung requests and upstream timeouts that point everywhere except the pool.</p>



<h2 class="wp-block-heading">Server slots run out while CPU idles</h2>



<p class="wp-block-paragraph">The second family is the one people expect: the database refuses connections. The surprise is that it happens at low CPU, and that the cause is arithmetic, not load.</p>



<h3 class="wp-block-heading">Pool multiplication</h3>



<p class="wp-block-paragraph">Every pool size is per process. The number the database actually faces is the product of everything that opens a pool:</p>



<pre class="wp-block-code"><code>worst case = pool size
           x worker processes per container
           x containers at the autoscaler's maximum
           x 2 during a rolling deploy (old and new running together)
           + cron jobs, migrations, BI tools, admin sessions</code></pre>



<p class="wp-block-paragraph">Take an illustrative case: a pool of 10, four worker processes per ECS task, 30 tasks at the scaling maximum. That&#8217;s 1,200 potential connections before a deploy briefly doubles it. Nobody chose that number. It emerged from three settings owned by three different people, and it only shows up on the day traffic and a deploy coincide.</p>



<p class="wp-block-paragraph">Use the autoscaler&#8217;s maximum, not today&#8217;s task count. Today&#8217;s count is what makes the math look safe.</p>



<h3 class="wp-block-heading">The limit moves when you resize</h3>



<p class="wp-block-paragraph">On RDS and Aurora, the default <code>max_connections</code> in the parameter group is a formula based on the instance class&#8217;s memory, not a fixed number. So a rightsizing exercise that drops the instance a size to save money also quietly lowers the connection ceiling. The CPU graph said it was safe. The connection budget didn&#8217;t get a vote.</p>



<p class="wp-block-paragraph">Don&#8217;t calculate the value by hand; the memory figure RDS uses excludes what its own processes consume. Run <code>SHOW max_connections</code> on the actual instance, and re-run it after every class change.</p>



<h3 class="wp-block-heading">Why raising max_connections is the wrong reflex</h3>



<p class="wp-block-paragraph">You can override <code>max_connections</code> in a custom parameter group, and sometimes a modest bump is fine. But it treats the symptom. Each PostgreSQL connection is a separate backend process with its own memory, and sorts or hashes inside it can each use up to <code>work_mem</code>. Thousands of connections all trying to work at once contend for the same CPU cores, and throughput often drops as concurrency rises past what the hardware can run in parallel.</p>



<p class="wp-block-paragraph">Also keep in mind that the last few slots are reserved for administrative roles. The application starts failing before the counter reaches the limit, and when it does, you may struggle to get your own session in to diagnose it. Keep headroom for humans.</p>



<h3 class="wp-block-heading">Budget connections on purpose</h3>



<ol class="wp-block-list">
<li>Read the real <code>max_connections</code> from the instance and subtract a margin for admin sessions, monitoring agents and migrations.</li>



<li>List every consumer: each service, its worker count, its scaling maximum, and its batch jobs.</li>



<li>Divide the budget and set each pool&#8217;s maximum from that, including the deploy overlap.</li>



<li>Cap the autoscaler maximum at something the database can serve. An autoscaler that can add tasks faster than the database can accept connections just converts a traffic spike into a connection storm.</li>



<li>If the budget can&#8217;t cover the fleet, add a pooler in front instead of more slots.</li>
</ol>



<p class="wp-block-paragraph">Smaller pools feel scary, but a connection is only useful while SQL is running. A small pool with short transactions usually beats a large pool with long ones.</p>



<h3 class="wp-block-heading">Zombie connections</h3>



<p class="wp-block-paragraph">Some slots belong to clients that are gone. A container killed without a clean shutdown, or a Lambda execution environment frozen between invocations, can leave server-side sessions open until something times them out. With Lambda, each concurrent environment holds its own connection, so cap concurrency and put a pooler in front; the <a href="https://john-nessime.com/aws/serverless-api/">serverless API</a> work I do starts exactly there.</p>



<p class="wp-block-paragraph">For long-lived services, set the pool&#8217;s idle timeout and max lifetime below any idle timeout on the network path, so connections are retired by the pool rather than cut silently in the middle.</p>



<h2 class="wp-block-heading">When the proxy becomes the queue</h2>



<p class="wp-block-paragraph">RDS Proxy is the managed answer to pool multiplication. Many client connections share a smaller set of database connections, and the proxy hands a database connection to a client per transaction. When that works, the server-side limit stops being your problem.</p>



<p class="wp-block-paragraph">It stops working in two ways. First, <strong>pinning</strong>: when a session changes state the proxy can&#8217;t safely share, such as setting a variable on PostgreSQL, the proxy ties that client to one database connection until the session ends. Enough pinned sessions and the proxy behaves like no proxy at all. Second, the <strong>borrow queue</strong>: when every database connection the proxy is allowed to open is busy, clients wait up to <code>ConnectionBorrowTimeout</code> (120 seconds by default) and then fail. Your application sees slow requests, not refused connections, and the database graphs look calm again.</p>



<p class="wp-block-paragraph">The proxy&#8217;s own CloudWatch metrics tell you which it is. <code>DatabaseConnectionsCurrentlySessionPinned</code> shows pinning. <code>DatabaseConnectionsCurrentlyBorrowed</code> against <code>DatabaseConnections</code> shows how close the proxy is to its share of the database. And <code>MaxConnectionsPercent</code> on the target group caps that share as a percentage of <code>max_connections</code>, so a conservative setting there creates the borrow queue by itself.</p>



<p class="wp-block-paragraph">The proxy is billed on the capacity of the database behind it (per vCPU-hour for provisioned instances, per ACU-hour for Aurora Serverless), so run your current rates through something like the <a href="https://john-nessime.com/tools/aws-cost-estimator/">AWS Cost Estimator</a> before assuming it&#8217;s free relief. PgBouncer in transaction mode on a small EC2 instance is the self-managed alternative: cheaper to run, but it&#8217;s one more thing to patch, monitor and make highly available.</p>



<h2 class="wp-block-heading">Metrics that warn you before connection pool exhaustion</h2>



<p class="wp-block-paragraph">Database CPU is the wrong alarm for this problem. By the time connection pressure shows up there, if it ever does, users already noticed. The signals that lead are on the client side:</p>



<ul class="wp-block-list">
<li><strong>Threads waiting for a connection.</strong> HikariCP exposes pending threads, active and idle counts, and acquisition time through its Micrometer integration. Sustained pending threads above zero is the earliest reliable warning you get.</li>



<li><strong>Connection acquisition time.</strong> Healthy pools hand out connections almost instantly. A rising acquisition time is the queue forming, long before timeouts start.</li>



<li><strong>Server connections as a ratio of the limit.</strong> Alarm on CloudWatch <code>DatabaseConnections</code> relative to the instance&#8217;s real <code>max_connections</code>, not an absolute number that goes stale after a resize.</li>



<li><strong>Oldest open transaction.</strong> A scheduled query against <code>pg_stat_activity</code> exporting the age of the oldest <code>xact_start</code> catches slow holders and leaks early.</li>
</ul>



<p class="wp-block-paragraph">Where these land matters less than having them next to each other. Grafana with a Prometheus scrape of the application&#8217;s pool metrics works well, as do Datadog or New Relic APM, which can show pool wait time inside the request trace. Sentry is useful for grouping the pool-timeout exceptions so you see the first one, not the thousandth. The goal is one screen where app pool state and database connection counts share a time axis. If you&#8217;d rather not build that, it&#8217;s the kind of thing covered under <a href="https://john-nessime.com/aws/monitoring-alerting/">AWS monitoring and alerting</a>.</p>



<h2 class="wp-block-heading">Troubleshooting by symptom</h2>



<ul class="wp-block-list">
<li><strong>Pool timeouts, database far below its limit, many idle-in-transaction sessions:</strong> slow holder or leak. Find the last statement in <code>pg_stat_activity</code> and trace the code path that follows it.</li>



<li><strong>Pool timeouts, few database sessions at all:</strong> the pool is too small for the worker count, or connections are borrowed and held without a transaction open. Check borrow duration, not the database.</li>



<li><strong>Refused connections only during deploys:</strong> the old and new versions overlap. Budget for the overlap or slow the rollout.</li>



<li><strong>Refused connections right after an instance class change:</strong> the formula-derived limit dropped. Check <code>SHOW max_connections</code>.</li>



<li><strong>Requests hang with no error at all:</strong> a pool with no acquisition timeout, such as node-postgres at its default. Set one.</li>



<li><strong>Everything was fixed by a restart, then came back days later:</strong> a leak. Turn on leak detection and wait for the stack trace.</li>



<li><strong>RDS Proxy in the path, slow requests, calm database:</strong> check pinned and borrowed connection metrics before anything else.</li>
</ul>



<h2 class="wp-block-heading">Common mistakes</h2>



<ul class="wp-block-list">
<li>Scaling the instance up because &#8220;it&#8217;s the database&#8221;, when the database was idle the whole time.</li>



<li>Raising <code>max_connections</code> to absorb pool multiplication, then losing throughput to contention.</li>



<li>Making the pool bigger to fix pool timeouts caused by long transactions. It buys minutes and moves the failure to the server limit.</li>



<li>Sizing pools from today&#8217;s task count instead of the autoscaler&#8217;s maximum.</li>



<li>Adding RDS Proxy without checking pinning, and ending up with a paid queue instead of a pool.</li>
</ul>



<h2 class="wp-block-heading">Best practices</h2>



<ul class="wp-block-list">
<li>Keep transactions short and never hold a connection across an external call.</li>



<li>Write the connection budget down, with every consumer, and revisit it when scaling limits or instance classes change.</li>



<li>Set an acquisition timeout that matches what callers will tolerate, and alert on pending threads, not on timeouts.</li>



<li>Use <code>idle_in_transaction_session_timeout</code> on PostgreSQL as a safety net, scoped to application roles.</li>
</ul>



<h2 class="wp-block-heading">FAQ</h2>



<h3 class="wp-block-heading">What causes connection pool exhaustion when database CPU is low?</h3>



<p class="wp-block-paragraph">Usually connections held while no SQL runs: long transactions, external API calls inside a transaction, or leaked connections that were never returned. The pool is empty, but the database is doing very little.</p>



<h3 class="wp-block-heading">Should I increase max_connections on RDS?</h3>



<p class="wp-block-paragraph">Only after confirming the server limit is actually the one being hit, and only modestly. If the cause is pool multiplication, budget the pools or add a pooler. More connections means more memory per backend and more contention, not more throughput.</p>



<h3 class="wp-block-heading">How big should my connection pool be?</h3>



<p class="wp-block-paragraph">Smaller than most people set it. Start from the connection budget divided across every process at maximum scale, then load test. A pool large enough to cover every thread usually just moves the queue from the application to the database.</p>



<h3 class="wp-block-heading">Does RDS Proxy fix pool exhaustion?</h3>



<p class="wp-block-paragraph">It fixes server-side slot exhaustion from many clients, as long as sessions aren&#8217;t pinned. It does nothing for an application pool drained by long transactions, because those transactions hold the proxy&#8217;s database connections just as firmly.</p>



<h3 class="wp-block-heading">How do I find leaked connections in PostgreSQL?</h3>



<p class="wp-block-paragraph">Look for sessions in <code>pg_stat_activity</code> that stay idle or idle in transaction for far longer than any request should, grouped by <code>application_name</code>. Then turn on your pool&#8217;s leak detection to get the stack trace of where the connection was borrowed.</p>



<h3 class="wp-block-heading">Does this happen on self-hosted databases too?</h3>



<p class="wp-block-paragraph">Exactly the same way. On PostgreSQL running on a VPS from providers like Hetzner, Contabo or InterServer, the pool math and idle transactions behave identically; the difference is that you run PgBouncer yourself instead of paying for RDS Proxy.</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p class="wp-block-paragraph">Connection pool exhaustion on AWS almost never means the database is too small. It means a queue formed somewhere, and the fix depends entirely on where. Read the error literally, compare the database&#8217;s connection count against its real limit, and within minutes you&#8217;ll know if you&#8217;re dealing with a drained application pool, a server limit reached by arithmetic, or a proxy holding the line.</p>



<p class="wp-block-paragraph">If you remember one thing: a connection costs you for as long as it&#8217;s borrowed, not for as long as SQL runs. Shorten the borrow and most of these incidents stop happening.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Need help tracking down a connection problem on AWS?</h2>



<p class="wp-block-paragraph">This is work I do for teams running RDS, Aurora or self-managed PostgreSQL and MySQL. Typical engagements look like this:</p>



<ul class="wp-block-list">
<li>Working out which layer is exhausting during an incident, from pool logs, <code>pg_stat_activity</code> and CloudWatch.</li>



<li>Building a written connection budget across services, workers, autoscaling limits and deploy overlap.</li>



<li>Finding leaks and long-held connections in application code, with leak detection and transaction timeouts set sensibly.</li>



<li>Evaluating RDS Proxy or PgBouncer for your workload, including pinning and cost.</li>



<li>Dashboards and alerts that put application pool state and database connections on the same screen.</li>



<li>Checking what an instance resize will do to your connection limit before you make it.</li>
</ul>



<p class="wp-block-paragraph">Send me the pool timeout from your logs and the output of the <code>pg_stat_activity</code> query above, and I&#8217;ll tell you which layer ran out.</p>



<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://john-nessime.com/projects/hosted-monitoring/">See hosted monitoring</a></div>



<div class="wp-block-button is-style-outline is-style-outline--1"><a class="wp-block-button__link wp-element-button" href="https://www.upwork.com/freelancers/~01f15a912ad84a6620" target="_blank" rel="noreferrer noopener">Work with me on Upwork</a></div>
</div>
<p>The post <a href="https://john-nessime.com/blog/cloud-computing/connection-pool-exhaustion-aws/">Plenty of CPU, Zero Connections: Debugging Connection Pool Exhaustion on AWS</a> appeared first on <a href="https://john-nessime.com/blog">John Nessime</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
