Acumatica · Performance

Acumatica Performance — Connection Pooling

Acumatica Performance — Connection Pooling is the Acumatica performance topic that nobody asks about until they have to.

John Kihiu12 min read

"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool" is one of those errors that gets blamed on "the database being slow" when the actual cause is almost always upstream of SQL Server entirely: the app tier asked for more concurrent connections than the pool was configured to hand out, and everyone waited in line.

Where the pool is actually configured

Acumatica's connection strings live in web.config (or, on newer builds, the site's connection configuration in the Database Configuration Wizard output), and pooling parameters ride on the connection string itself as standard ADO.NET/SQL Server connection string keywords:

XML — web.config connection string
<add name="AcumaticaDB"
     connectionString="Data Source=SQLPROD\ACU;Initial Catalog=AcumaticaProd;
       Integrated Security=False;User ID=acu_app;Password=***;
       Pooling=True;Min Pool Size=10;Max Pool Size=200;
       Connect Timeout=30;Connection Lifetime=0"
     providerName="System.Data.SqlClient" />

Max Pool Size is the number that actually matters under load — the default of 100 is frequently too low for a busy multi-user instance with several worker processes, because each app pool process gets its own pool. Three worker processes at the default cap out at 300 total connections to SQL Server combined, which needs to be checked against SQL Server's own max server memory and connection limits, not just bumped blindly.

Confirming it is pool exhaustion, not a slow query

Windows Performance Monitor exposes .NET Data Provider for SqlServer\NumberOfPooledConnections and NumberOfActiveConnections per process. If active connections sit flat at your configured max while requests queue, you are pool-bound. If active connections stay well under the max but requests are still slow, the pool is not your problem — go look at query execution time instead.

SQL — connections currently open per app login
SELECT login_name, COUNT(*) AS open_connections
FROM sys.dm_exec_sessions
WHERE program_name LIKE '%Acumatica%' OR login_name = 'acu_app'
GROUP BY login_name
ORDER BY open_connections DESC;

What actually exhausts a pool in practice

Raising Max Pool Size treats the symptom

Doubling Max Pool Size makes the timeout go away for a while and pushes the real cost onto SQL Server, which now has to context-switch and manage locks across more concurrent sessions. If you are raising the pool size past a few hundred per app server to make timeouts stop, you have an underlying long-running-query or connection-leak problem that the bigger pool is hiding, not fixing. Fix the leak or the slow query first; resize the pool as capacity planning after that, not instead of it.

What I actually set, and why

Starting point for a mid-size production instance (a few dozen concurrent users, 2-3 app servers): Max Pool Size=200, Min Pool Size=10 (keeps a warm floor so the first requests after an app pool recycle are not all paying connection-open latency), Connection Lifetime=0 (let the pool manage recycling rather than forcing periodic resets, which is mostly relevant behind older load balancers that dislike long-lived TCP sessions). Adjust up only after confirming via Perfmon that you are actually pool-bound, not query-bound.

Wrapping up

A pool exhaustion timeout is an app-tier symptom with two possible app-tier causes — genuinely high concurrency, or a leak/long-holder eating connections that should have been returned — and resizing the pool only addresses the first. Measure active versus max pooled connections before touching the config, and audit custom integration code for connections that never get released.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.