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秒才能报告登录失败。
- 为什么花了20秒,把登录失败错误完全吞掉,第二种情况报错超时错误
我可能缺少一些重要的设置来避免这种行为。
第一种情况需要 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 秒内失败。
这是重现错误的独立 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秒才能报告登录失败。
- 为什么花了20秒,把登录失败错误完全吞掉,第二种情况报错超时错误
我可能缺少一些重要的设置来避免这种行为。
第一种情况需要 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 秒内失败。