Freeze doesn't release TLS index on Freeze::SharedDbEnv::~SharedDbEnv

allroallro Alex LeidermanOrganization: Cell2BetProject: Management ServerMember
Hi,

It looks like a bug in SharedDbEnv class - please confirm:
allocated by constructor (_tsdKey = TlsAlloc();),
TLS index is not released by TlsFree on destructor.

Result: A simple loop like
for (int j=0; j<2000; ++j) {
   ConnectionPtr connection = createConnection(communicator(), "db");
}
will give us
SharedDbEnv.cpp:440: IceUtil::ThreadSyscallException:
syscall exception: Not enough storage is available to process this command.
If thread local storage is used for application needs it may lead to hardly reproducible bugs.
Possible workaround: Connection pool created on application startup - please confirm.

Environment:
ICE: 3.3.1 - according to source code, will happen on 3.4.1 as well
Compiler: VS2008
OS: Vista

Thanks in advance,

Comments

  • bernardbernard Jupiter, FLBernard NormierOrganization: ZeroC, Inc.Project: IceAdministrators, ZeroC Staff ZeroC Staff
    Hi Alex,

    Thank you for the bug report. It looks like the SharedDbEnv destructor should indeed call TlsFree on this key.

    A good work-around is to avoid creating and destroying "shared db env" all the time, e.g. the following code would create just one SharedDbEnv:
    ConnectionPtr cachedConnection = createConnection(communicator(), "db");
    for (int j=0; j<2000; ++j) {
       ConnectionPtr connection = createConnection(communicator(), "db");
    }
    

    It's also better for performance.

    Best regards,
    Bernard
  • allroallro Alex LeidermanOrganization: Cell2BetProject: Management ServerMember
    Hi Bernard,

    Regarding workaround - I have some DBLayer class that looks like following:
    class DBLayer
    {
    public ... f1(...){
        ConnectionPtr connection = createConnection(communicator(), "db");
        //Do something
    }
    public ... f2(...){
        ConnectionPtr connection = createConnection(communicator(), "db");
        //Do something
    }
    
    ...
    
    public ... fN(...){
        ConnectionPtr connection = createConnection(communicator(), "db");
        //Do something
    }
    }
    
    All DBLayer functions may be executed in context of multiple threads => using 1 cached connection means serialization of access to it. So, according to Ice Manual -
    If your application requires concurrent access to the same database file (persistent map), you must create several connections and associated maps.
    - it looks like I have to use a pool of connections.

    Am I correct?
  • bernardbernard Jupiter, FLBernard NormierOrganization: ZeroC, Inc.Project: IceAdministrators, ZeroC Staff ZeroC Staff
    Hi Alex,

    Creating a connection in each function is a good idea, and will allow you to use the same underlying Berkeley DB environment concurrently.

    What is missing from your code is one "cached connection", to keep the underlying Berkeley DB env opened. This is much better for performance and is also a work-around for this missing TlsFree.

    E.g.
    class DBLayer
    {
    public:
         DBLayer()
         {
              // Ensures the underlying Berkeley DB environment remains opened
              //
              _cachedConnection = createConnection(communicator(), "db");
         }
    
          ... f1(...)
        {
              // Create a connection for the calling thread. This is very cheap
              // because the underlying Berkeley DB env is already open (thanks to
              // _cachedConnection)
              //
              ConnectionPtr connection = createConnection(communicator(), "db");
              // Do something 
         }
     
    private:
          Freeze::ConnectionPtr _cachedConnection;
    };
    
    

    Best regards,
    Bernard
  • allroallro Alex LeidermanOrganization: Cell2BetProject: Management ServerMember
    Bernard,

    Thanks a lot for the clarification.
Sign In or Register to comment.