javaconnection-poolingc3p0interrupt-handlinginterrupted-exception

Connection pooling and Thread.interrupt()


I am using c3p0 to handle database connection pooling in a multithreaded environment. The question might pertain to other pooling libs, but this is what I have.

Recently I've needed to implement interruption handling on such threads using directly or indirectly c3p0, and have noticed that if interrupt() is called right when c3p0Datasource.getConnection() is trying to fetch me a Connection from the pool, it throws an InterruptedException.

Obviously, this happens because of the wait()

at java.lang.Object.wait(Native Method)
at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicResourcePool.java:1414)

Cool. The question is how do you handle this properly - both is case a) you want to continue with the transaction before your thread terminates, and b) you want to abort.

I've tried a solution that seems to be working fine (posted as answer) - actually, I think this subject is closed. Feel free to chip in otherwise, thanks!


Solution

  • I did a simple test by firing a lot of Connection requests within 1 second, executing a SELECT each time tim make sure the pool bottlenecks, and then calling interrupt().

    What I found was that the connection object is fine and dandy after the InterruptedException being caught, even though the stacktrace shows me c3p0 meltdown at awaitAvailable(..). At this very second I'm checking out their sources, and surely, they handle the InterruptedException. They even throw a proper warning:

    WARNING: com.mchange.v2.resourcepool.BasicResourcePool@5bcf4b61 -- an attempt to checkout a resource was interrupted, and the pool is still live: some other thread must have either interrupted the Thread attempting checkout!
    

    telling us it's still live, albeit inbetween a lot of word fuzz. Solved.

    Here's the test anyway.

    ComboPooledDataSource ds = new ComboPooledDataSource();
    
    // testing with various pool sizes - same effect
    ds.setMinPoolSize(1);
    ds.setMaxPoolSize(5);
    ds.setInitialPoolSize(2);
    
    Thread connectingThread = new Thread() {
    
        public void run() {
            Connection cnxn = null;
            while (true) {
                try {
                    cnxn = ds.getConnection();
                    System.out.println("Got connection.");
                    executeQuery(cnxn);
                } catch (SQLException e) {
                    System.out.println("Got exception.");
                    e.printStackTrace();
    
                    // SOLUTION:
                    Throwable cause = e.getCause();
                    if (cause instanceof InterruptedException) {
                        System.out.println("Caught InterruptedException! Cnxn is " + cnxn);
    
                        // note that cnxn is a com.mchange.v2.c3p0.impl.NewProxyConnection
                        // also note that it's perfectly healthy.
                        //
                        // You may either want to:
                        // a) use the cnxn to submit your the query
    
                        executeQuery(cnxn);
                        cnxn.close()
    
                        // b) handle a proper shutdown
    
                        cnxn.close();
    
                    }
                    break;
                }
            }
        };
    };
    
    connectingThread.start();
        
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {          e.printStackTrace();        }
        
    connectingThread.interrupt();