C3P0 的 checkoutTimeout 设置干扰登录失败异常

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

这是重现错误的独立 Groovy 脚本。

@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")
}

以上脚本打印出以下expected错误

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

当我在脚本中添加 datasource.setCheckoutTimeout(10000) 时,打印出来的内容如下。

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()

整个堆栈跟踪中没有关于登录失败的提示。也许我错过了什么。

我可能缺少一些重要的设置来避免这种行为。

第一种情况需要 30 秒才能报告失败,因为您没有设置限制,因此客户端将无限期地等待连接可用。默认情况下,c3p0 会进行 30 次尝试从底层数据库获取连接,每次尝试之间有 1 秒的延迟,然后声明连接获取失败并将其报告给等待的客户端。如果您想更改此行为,可以将 c3p0 设置 acquireRetryAttempts and acquireRetryDelay 设置为您喜欢的任何设置。

在第二种情况下,您要求 c3p0 在 10 秒后让客户端超时,它会这样做。就 c3p0 而言,Connection 获取并没有最终失败。 "Login failed for user" 不是失败的原因。您设置的超时是失败的原因,这就是 c3p0 报告的内容。

当连接获取确实失败时,没有吞下任何东西,失败被正确记录。但是您的客户端线程与 Connection 获取无关。连接池的全部意义在于将 DBMS-level 连接获取与客户端签出分离。 c3p0 "helper" 线程与 DBMS 交互,而不是您的客户端线程。

要获得您想要的行为,请保留 checkoutTimeout 未设置,但将 acquireRetryAttempts 设置为小于 10 的值。然后,如果登录信息错误,客户端将看到一轮连接获取10 秒内失败。