Search code examples
c3p0

C3P0's checkoutTimeout setting is interfering with the the login failed exception


Here's a standalone Groovy script that reproduces the error.

@Grab(group='com.mchange', module='c3p0', version='0.9.5.4')
@Grab(group='com.microsoft.sqlserver', module='mssql-jdbc', version='6.4.0.jre7')

import com.mchange.v2.c3p0.*

ComboPooledDataSource datasource = new ComboPooledDataSource()

datasource.setJdbcUrl('jdbc:sqlserver://valid-db:1433;databaseName=dbName;socketTimeout=600000')
datasource.setDriverClass('com.microsoft.sqlserver.jdbc.SQLServerDriver')
datasource.setUser('myDb')
datasource.setPassword('wrong-pwd')
//datasource.setCheckoutTimeout(10000)

long time = System.currentTimeMillis()
try{
datasource.getConnection()
}finally{
println ((System.currentTimeMillis() - time) + " millis")
}

Above script prints out the following expected error

31730 millis
<and...>
Login failed for user...

When I add datasource.setCheckoutTimeout(10000) in the script, here's what it prints out.

20006 millis
<and...>
Caused by: com.mchange.v2.resourcepool.TimeoutException: A client timed out while waiting to acquire a resource from com.mchange.v2.resourcepool.BasicResourcePool@57faccf9 -- timeout at awaitAvailable()

There's no hint about the failed login in the entire stacktrace. Maybe I'm missing something.

  • Why does it take 30 seconds to report the failed login in the 1st scenario.
  • Why does it spend 20 seconds, completely swallow the login failure error, and report the wrong timeout error in the second scenario.

I might be missing some important setting to avoid this behavior.


Solution

  • It takes 30 seconds to report the failure in the first case because you have set no limit, so clients will wait indefinitely for a Connection to become available. By default, c3p0 makes 30 attempts to acquire a Connection from the underlying database with a one second delay between each attempt before declaring the a Connection acquisition failure and reporting that to waiting clients. If you want to change this behavior, you can set c3p0 settings acquireRetryAttempts and acquireRetryDelay to whatever you prefer.

    In the second case, you ask c3p0 to timeout clients after 10 seconds, and it does so. As far as c3p0 is concerned, Connection acquisition has not definitively failed. "Login failed for user" is not the cause of the failure. The timeout you have set is the cause of the failure, so that's what c3p0 reports.

    When Connection acquisition does definitively fail, nothing is swallowed, the failure is properly logged. But your client's thread has nothing to do with the Connection acquisition. The whole point of a Connection pool is to decouple DBMS-level Connection acquisition from client checkouts. c3p0 "helper" Threads interact with the DBMS, not your client Thread.

    To get the behavior it sounds like you want, leave checkoutTimeout unset but set acquireRetryAttempts to something less than 10. Then if the login information is bad, clients will see a Connection acquisition round failure within 10 seconds.