Bug 54337

Summary: StatementCache leaks statements/cursors
Product: Tomcat Modules Reporter: Patric Rufflar <patric>
Component: jdbc-poolAssignee: Tomcat Developers Mailing List <dev>
Status: RESOLVED DUPLICATE    
Severity: normal    
Priority: P2    
Version: unspecified   
Target Milestone: ---   
Hardware: PC   
OS: All   

Description Patric Rufflar 2012-12-21 11:31:49 UTC
It seems that the StatementCache has issues in highly multi-threaded situations where high numbers of PreparedStatements are created.

The application quickly fails with the well-known "Internal Exception: java.sql.SQLException: ORA-01000: maximum open cursors exceeded"

Without the StatementCache the application just runs fine.
All threads invoke the same SQLs. Of course, each thread uses its own Connection exclusively.

Two things are conspicuous:

1. 
The AtomicInteger field org.apache.tomcat.jdbc.pool.interceptor.StatementCache.cacheSize 
which seems to be not in sync with the real size of the cache Map.

It's too high (higher than 50) and the StatementCache (erroneously) assumes that the cache capacity is exceeded.

I suggest calling size() on the cache Map instead.

2. 
When the capacity seems (or really is) exceeded, statement.close() will not causing to close the wrapped statement causing the cursor to leak.

The reason can be seen in org.apache.tomcat.jdbc.pool.interceptor.StatementCache$CachedStatement.closeInvoked() line: 244-288:

            closed = true;
            delegate = null;
            if (shouldClose) {
                super.closeInvoked();
            }

The delegate is set to null but only _afterwards_ super.closeInvoked() is invoked which is unable to close the underlying preparedStatement => the statement remains unclosed. It should be the other way round.

Best regards,
Patric
Comment 1 Konstantin Kolinko 2013-03-22 17:34:11 UTC
(In reply to comment #0)
> 1. 
> The AtomicInteger field
> org.apache.tomcat.jdbc.pool.interceptor.StatementCache.cacheSize 
> which seems to be not in sync with the real size of the cache Map.
> (..)

You are wrong:
Map is per-connection. Size is per-pool.

> 2. 
> When the capacity seems (or really is) exceeded, statement.close() will not
> causing to close the wrapped statement causing the cursor to leak.

Yes, it is a bug. Duplicate of 54732.

*** This bug has been marked as a duplicate of bug 54732 ***