Oracle.ManagedDataAccess.Core new OracleConnection instances appear to create new OraclePoolManager instances that are never released
See original GitHub issueOracle.ManagedDataAccess.Core
versions 3.21.1, 2.19.110 and 2.19.101.
It’s possible I am missing something/misunderstanding here and hopefully you could fill me in. (removed some content for data protection/brevity)
During each request to our API where needed we create a new connection as credentials are supplied via the caller of the API. This is done through a factory where we return a new instance of our “DirectDbConnection” object.
Factory Code:
OracleCredential oracleCredentials = GetCurrentCredentials();
var ora = new OracleConnection(_connectionStrings.SocialCareDirectConnectionString, oracleCredentials);
var DirectDatabaseConnection = new MosaicDirectDatabaseConnection(ora, _logger, _correlationIdService);
return DirectDatabaseConnection;
Direct Connection Code (removed logging and exception handling code):
public DirectDatabaseConnection(OracleConnection oracleConnection)
{
_oracleConnection = oracleConnection;
}
public void Dispose()
{
OracleConnection.ClearPool(_oracleConnection);
_oracleConnection?.Close();
_oracleConnection?.Dispose();
}
public IEnumerable<T> Query<T>(string query, bool buffered, int commandTimeout)
{
IEnumerable<T> result = new List<T>();
if (_oracleConnection.State == ConnectionState.Closed)
{
_oracleConnection.Open();
}
if (_oracleConnection.State == ConnectionState.Open)
{
result = _oracleConnection.Query<T>(query, buffered: buffered, commandTimeout: commandTimeout);
}
return result;
}
Our typical connection string looks like this and is pulled from config that is DI’d to the factory:
Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=)(PORT=))(CONNECT_DATA=(SERVICE_NAME=)));
The connection is then used like so:
using var connection = _connectionFactory.GetInstance();
List<> result = null;
string query = "'";
var dbResult = connection.Query<>(query, buffered: true, commandTimeout: 60)
.ToList();
result = MapDbResult(dbResult);
return new List<Actual>(result);
I’m sure that technically this is not an optimal approach in a performance manner, however situation states I need to fix with as little change as possible in the short term.
From my understanding connection pooling by default would only create a new connection pool if the connection string is in any way different. In my testing we’ve always used the same connection string AND credentials so form a connection pool standpoint it should always be the same if it exists. However the issue that is occurring is that the OraclePoolManager
instances that are created when we create a new connection (it appears) are not being garbage collected. This led to the application reaching gigabytes of RAM usage overtime as this application is queried a lot by some dependent systems
Since there is no documentation on this internal class from what I can find it’s hard to understand why that may be.
Any insight you could shed would be greatly appreciated 😃
Issue Analytics
- State:
- Created 2 years ago
- Comments:27 (14 by maintainers)
@BashouT Clearing the pool removes the connections in the pool, but you still need a pool manager to manage the pool, which why it remains around.
Maintaining the same credential object is standard ADO.NET behavior. From the SqlClient connection pooling doc:
Different instances of SqlCredential will use different connection pools, even if the user ID and password are the same.
In general, ODP.NET tries to stay in line with standard ADO.NET provider behavior as that is what developers expect.
@alexkeh why does this occur even if pooling is disabled? Even clearing the pools doesn’t release these connection managers/disposing the credentials objects. It still feels much like a bug.
Also it seems odd that you should have to maintain the credentials object to reuse when pooling is enabled. Shouldn’t the driver identify there’s a pool available for that credential pair even if the object is a different reference?