TDAConnectionManager.NewConnection raises "Maximum pool size reached.."

Hello,

on my RO server there is DA-aware service, instanced by ROPerClientFactory, connected to Firebird database using DBX DA-driver. ConnectionManager has default configuration - PoolingEnabled=TRUE, PoolBehavior=pbWait, MaxPoolSize=10. One sole unique client is connected to server, after 10th (=MaxPoolSize) service invocation server raises error “Maximum pool size reached. Cannot create a new connection”. My investigation shows that bottleneck is probably inside of TDAConnectionManager.NewConnection method. When connection manager is searching pool for proper connection to use, it compares ConnectionString, ConnectionType value… as well as UserID and Password. Unfortunately, optional incoming parameters UserID & Password are ALWAYS empty, so connection manager can NEVER find proper cached connection. Due this, it ALWAYS try to create new fresh connection, but - if MaxPoolSize is already reached - error is raised.

As you can see inside of DataAbstractService_Impl.TDataAbstractService.GetConnection function, DA-service acquires connection calling ServiceSchema.ConnectionManager.NewConnection(lConnectionName)… optional parameters UserID & Password are always omitted.

Greetings from Prague,
Jaroslav

Hello,
DA adds a connection to the cache list after releasing the connection. If you use
ROPerClientFactory it doesn’t release the connection after calling a remote method.
So connections aren’t add to the cache list.
You can use ROClassFactory for using cache.

Hello,

sorry, I cannot understand exactly your reply… can you explain it more detailed?

Based on my long-time experiences with RO, I can say almost certainly, that after remote method call is finished, service is deactivated by invoker (not by factory!), using IROObjectActivation.OnDeactivate method. As you can see inside the TDataAbstractService.DoOnDeactivate method, current database connection (used for current call) is ALWAYS released by calling ReleaseConnection, regardless how type of factory was used to create service instance. So subsequent calls are made: TDataAbstractService.DoOnReleaseConnection -> TDAEConnection._Release -> TDAConnectionManager.ReleaseConnection… and there, depending on TDAConnectionManager.PoolingEnabled value, database connection is stored to cache or not… really, its have nothing to do with kind of class factory used, its fully controlled by TDAConnectionManager.PoolingEnabled/PoolBehavior/MaxPoolSize settings.

Key attribute/behavior of TROPerClientClassFactory is that it stores (doesn’t releases) SERVICE instances, it has no influence on database connection used inside service. Regardless used class factory, regardless the service will be destroyed or not after remote call, service is always deactivated after remote call by invoker, so - in case of DA service - ReleaseConnection is ALWAYS processed.

Shortly: when DA-service is created using TROPerClientClassFactory and connection manager has PoolingEnabled set to TRUE, when number of client calls reached MaxPoolSize value, error is raised… Im sure its not desired/right behavior.

Greetings from Prague,
Jaroslav

Hello,
Thanks, you are right.
The issue was logged and fixed as #64713
As a workaround please modify uDAConnectionManager.pas file:

function TDAConnectionManager.NewConnection(const aConnectionName: string;
  OpenConnection: boolean = TRUE; const UserID: string = ''; const Password: string = ''): IDAConnection;
var
  i: integer;
  conn: TDAConnection;
  list: TList;
  tempconn: TCachedConnection;
  lUser, lPass: string;
  lcp: TDAConnectionStringParser;
begin
............
// Pooling is enabled and has to be thread safe
      result := nil;
      conn := fConnections.ConnectionByName(aConnectionName);
      if (UserID <> '') and (Password <> '') then begin
        lUser := UserID;
        lPass := Password;
      end
      else begin
        lcp := TDAConnectionStringParser.Create(conn.ConnectionString);
        try
          lUser := lcp.UserID;
          lPass := lcp.Password;
        finally
          lcp.Free;
        end;
        if UserID <> '' then lUser := UserID;
        if Password <> '' then lPass := Password;
      end;
      while result = nil do begin
        list := fConnectionCache.LockList;
.................
for i := 0 to list.Count -1 do begin
            tempconn := TCachedConnection(list[i]);

            if SameText(tempconn.Connection.ConnectionString, conn.ConnectionString) and
               SameText(tempconn.Connection.ConnectionType, conn.ConnectionType) and
               (tempconn.Connection.UserID = lUser) and
               (tempconn.Connection.Password = lPass) then begin
              list.Delete(i);
          

Just found this via a search and am a bit confused.

The issue was noted as #64713 and fixed and the change logs indeed show this as fixed in the 7.0.73.1111 release.

However the code section shown isn’t the same as the code in my 7.0.75.1117 version. Specifically, the TDAConnectionStringParser is nowhere to be seen, instead my code shows “_Parse(conn.Connectionstring, lUser, lPass)”.

Is this correct? Have further changes been made since this issue was fixed?