Six months in a leaky boat
Yesterday I discovered a potential resource leak when using the dbExpress Ado.NET provider, which can cause connections to be created and never freed. This leak was discovered when I implemented and started consuming a helper method which allows me to easily determine whether a data reader contains a specified column.
The above code seemed pretty innocuous, but a bit of spelunking using Reflector discovered that the call to TAdoDbxDataReader.GetSchemaTable resulted in the associated TAdoDbxConnection cloning itself into a private variable, and this connection was never closed. So if this method is called multiple times using different connections, each call would result in an additional database connection being made which wasn’t being closed until the application exited. For the app that this bug was discovered in, this meant that an ever increasing number of connections were being established to an InterBase database, eventually resulting in stored procedure calls that never finished executing.
If you think you may be affected by this bug, please take a look at http://qc.embarcadero.com/wc/qcmain.aspx?d=82886, and vote/rate as you see fit. In addition to providing more details as to the cause of the leak, it also suggests the following possible workaround.
Now, obviously this workaround is pretty brittle, and could easily break if the DBX team change their internal implementation of TAdoDbxConnection (especially if said change involved renaming FClonedConnection), but I have verified that it works against the release of DBX that currently ships with Delphi Prism. The DBX team have been made aware of this bug, so hopefully it will be resolved in a not too distant release, and give the above kludge a short life expectancy.
class method DbUtils.ColumnExists(AReader: DbDataReader; AColumnName: String): Boolean;
begin
var Table: DataTable := AReader.GetSchemaTable;
Table.DefaultView.RowFilter := String.Format('ColumnName =''{0}''', AColumnName);
Result := (table.DefaultView.Count > 0)
end;
The above code seemed pretty innocuous, but a bit of spelunking using Reflector discovered that the call to TAdoDbxDataReader.GetSchemaTable resulted in the associated TAdoDbxConnection cloning itself into a private variable, and this connection was never closed. So if this method is called multiple times using different connections, each call would result in an additional database connection being made which wasn’t being closed until the application exited. For the app that this bug was discovered in, this meant that an ever increasing number of connections were being established to an InterBase database, eventually resulting in stored procedure calls that never finished executing.
If you think you may be affected by this bug, please take a look at http://qc.embarcadero.com/wc/qcmain.aspx?d=82886, and vote/rate as you see fit. In addition to providing more details as to the cause of the leak, it also suggests the following possible workaround.
class method CloseConnection(AConnection: TAdoDbxConnection);
begin
var Field: FieldInfo := AConnection.GetType.GetField('FClonedConnection',
(BindingFlags.GetField or (BindingFlags.NonPublic or BindingFlags.Instance)));
if (Field <> nil) then
begin
var Con: TAdoDbxConnection := (Field.GetValue(AConnection) as TAdoDbxConnection);
if (Con <> nil) then
begin
Con.Close;
Field.SetValue(AConnection, nil)
end;
end;
AConnection.Close;
end;
Now, obviously this workaround is pretty brittle, and could easily break if the DBX team change their internal implementation of TAdoDbxConnection (especially if said change involved renaming FClonedConnection), but I have verified that it works against the release of DBX that currently ships with Delphi Prism. The DBX team have been made aware of this bug, so hopefully it will be resolved in a not too distant release, and give the above kludge a short life expectancy.

Software developer, family man, All Blacks fan, Playstation junkie, and headbanger since ages ago.
Comments
-
Please login first in order for you to submit comments