-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Description
Im getting this quite a lot on 4.40 sites. I've scanned throught different issues, but i dont see any fixes on this issue for newer versions really. Maybe something related did fix it though.
I did use claude code to see if he could figure out what was going on, and i dont know if he is onto someting but heres the result from claude code:
Response from claude code:
The investigation found several connection leaks, with the most critical being:
1. CRITICAL: GetTable() never disposes DataContext
Every call to .Table on any repository creates a new DataContext (database connection) that is never disposed. Connections are only released when the GC eventually collects them.
2. HIGH: CreateDbCommand() leaks DataConnection
Raw SQL execution methods (ExecuteNonQueryAsync, QueryProcAsync, QueryAsync) create a DataConnection via CreateDbCommand() that is never disposed.
The Problem
GetTable() at BaseDataProvider.cs:257-265 is the primary leak. Every call to repository.Table creates a new DataContext(...) that is never disposed. The connection sits open until the GC collects it. This is core NopCommerce code — every single LINQ query through any repository leaks a connection.
CreateDbCommand() at BaseDataProvider.cs:59-67 is the secondary leak — ExecuteNonQueryAsync and QueryProcAsync create a DataConnection that is never disposed after use.
These are NopCommerce framework-level bugs. Under normal web traffic they're tolerable because GC collects them quickly enough, but under heavy load the pool fills up before GC can clean up.
Recommended Fixes
There are two approaches — a quick mitigation and a proper fix:
Quick mitigation: Increase pool size and add connection lifetime
Add to your connection string:
Max Pool Size=500;Min Pool Size=10;Connection Lifetime=30;
This buys time but doesn't fix the root cause.
Proper fix: Patch BaseDataProvider.cs
- Fix GetTable() — Use DataContext with CloseAfterUse = true so connections are released after query execution
- Fix ExecuteNonQueryAsync / QueryProcAsync — Wrap the DataConnection in using blocks
- Both (recommended)_
Under normal web traffic it's "good enough" because GC collects the orphaned DataContext objects before the pool fills. Table in tight loops over thousands of records, creating connections faster than GC can reclaim them.
What I recommend
Since there's no upstream fix to wait for, we should patch it ourselves. The safest fix for GetTable() is to set CloseAfterUse = true on the DataContext:
return new DataContext(LinqToDbDataProvider, GetCurrentConnectionString())
{
MappingSchema = GetMappingSchema(),
CommandTimeout = DataSettingsManager.GetSqlCommandTimeout(),
CloseAfterUse = true // <-- returns connection to pool after each query
}.GetTable<TEntity>();
This is a one-line change that tells LinqToDB to close the underlying connection after each query execution, returning it to the pool immediately instead of waiting for GC.