Does your application break after every few hours of inactivity even though you have enough database connections in the pool? It is a common problem with database connection pooling (and idle sockets connections in general).
I have been running a few Grails app with PostgreSQL database using Apache Commons DBCP connection pooling. Most of these apps are pretty busy, and are working quite well so far. But I have one critical app that doesn’t get used as much. Recently after a bug report, I was watching through the logs and I realized that this app was complaining about Socket Connection Exception after every hour of idle time. Try it again, and it would work. So why was it rejecting the DB connection the first time? Creepy!
I checked out the other apps I have, and all of them were suffering from the same problem - depending on how idle they were. I couldn’t ignore any longer.
I started off with the basics.
Basic configuration:
I have my datasource defined in the DataSource.groovy file under grails-app/conf. I have enabled connection pooling and I am using the appropriate PostgreSQL-JDBC driver. Grails comes with Apache DBCP connection pooling by default. So it just works.
01.
environments {
02.
production {
03.
dataSource {
04.
pooled = true
05.
driverClassName =
"org.postgresql.Driver"
06.
dbCreate =
"update"
07.
url =
"jdbc:postgresql://sacharya.com:5432/sacharya_prod_db"
08.
username =
"myuser"
09.
password =
"mypassword"
10.
logSql=true
11.
}
12.
}
13.
}
This is what my production configuration looks like - running whatever the default behavior that Grails comes with. I printed the datasource object to see what DBCP configurations my app is using by default.
1.
dataSource.properties.
each
{
println
it }
minIdle=0 initialSize=0 maxIdle=8 numIdle=0 maxActive=8 numActive=0
As the result shows, it is using the default values as defined in Apache DBCP
Problem:
Using netstat, I started watching if the application has any connections to the database open:
$ netstat -na | grep 5432 tcp4 0 0 24.25.26.27.50095 98.129.169.246.5432 ESTABLISHED
So DBCP says it has no connections open (active or idle) to the database yet in the pool. But netstat shows there is a TCP connection established to the database. Where does this connection come from - loading & initializing the Driver?
Now lets use the application for a while. Once you start asking the pool for connections, existing connections idle in the pool are at your service, or if there no connections in the pool, new connections are created and given to you, which get returned back to the pool after you complete the query. So at any given time, there will be minIdle to maxIdle ( 0 to 8 in our case) connections in the pool.
$ netstat -na | grep 5432 tcp4 0 0 24.25.26.27.51308 98.129.169.246.5432 ESTABLISHED tcp4 0 0 24.25.26.27.50095 98.129.169.246.5432 ESTABLISHED
Now with some connections in the pool, I left the app idle for an hour and then I tried to access the app (netstat still shows 2 TCP connection). The first query got the following exception:
01.
org.springframework.dao.DataAccessResourceFailureException: could not execute query;
02.
nested exception is org.hibernate.exception.JDBCConnectionException: could not execute query
03.
04.
Caused by: org.hibernate.exception.JDBCConnectionException: could not execute query
05.
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:
97
)
06.
07.
Caused by: org.postgresql.util.PSQLException: An I/O error occured
while
sending to the backend.
08.
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:
217
)
09.
10.
Caused by: org.hibernate.exception.JDBCConnectionException: could not execute query
11.
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:
97
)
12.
13.
Caused by: org.postgresql.util.PSQLException: An I/O error occured
while
sending to the backend.
14.
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:
217
)
15.
16.
Caused by: java.net.SocketException: Broken pipe
17.
at java.net.SocketOutputStream.socketWrite0(Native Method)
The subsequent query succeeds. There was one connection (ignore the other TCP connection used during Driver initialization) in the pool, but the database responded by saying that the connection was not valid.
Reasoning:
At this point it was clear that the TCP connection that was sitting idle was already broken, but our app still assumed it to be open. By idle connections, I mean connections in the pool that aren’t in active use at the moment by the application. After some search, I came to the conclusion that the network firewall between my app and the database is dropping the idle/stale connections after 1 hour. It seemed to be a common problem that many people have faced.
By default, DBCP holds the pooled connections open for infinite time. But a database connection is essentially a socket connection, and it doesn’t come for free. The host OS, database host, and firewall have to allocate a certain amount of memory and other resources for each socket connection. It makes sense to those devices not to hold onto idle connections for ever. So the idea is to make sure that you don’t have stale connections in your pool that would otherwise be silently dropped by OS or firewall. The system has no way of knowing if the connection is broken unless is sends a packet and waits for an acknowledgement. So even when the connection is timed out or closed by one side, the other side may still think the connection is open.
While there may not be a firewall between your server and database, even the OS has a timeout on TCP connections. You could probably increase the TCP keepalive of the OS itself, but that will affect the whole system, and yet you are only postponing the problem.
Solution:
Now lets try to modify some of the DBCP settings for the dataSource.
1. Validating Connections
:
DBCP allows you do define a validation query and do a sanity check of
the connection before you actually use it in your application.
By default,
validationQuery=null testOnBorrow=false testOnReturn=false testWhileIdle=false
The validation query must return at
least one row, and using the query you can have DBCP test the connection
for you while its idle, before you borrow and before you return it.
So lets change it to:
validationQuery="SELECT 1" testOnBorrow=true testOnReturn=true testWhileIdle=true
If any of the connection object fails to validate, it will be dropped from the pool. There might be some performance implications of running these three SQLs (which I am not worried at the momet), and hence you might just want to try testOnBorrow.
2. Evicting Idle Connections : DBCP can run an idle object evictor at a regular interval and evict any connections that are older than some threshold. By default this behavior is turned off since timeBetweenEvictionRunsMillis is set to -1.
timeBetweenEvictionRunsMillis=-1 numTestsPerEvictionRun=3 minEvictableIdleTimeMillis=1000 * 60 * 30
Now lets run the evictor every 30 minutes and evict any connections older than 30 minutes.
timeBetweenEvictionRunsMillis=1000 * 60 * 30 numTestsPerEvictionRun=3 minEvictableIdleTimeMillis=1000 * 60 * 30
It turns out that you cannot change the DBCP settings from the DataSource.groovy file. The datasource object injected in the DataSource.groovy file is an instance of the javax.sql.DataSource. I can however do so by overriding the default DataSource from the BootStrap.groovy, which sets up the settings during start up.
01.
import
org.codehaus.groovy.grails.commons.ApplicationAttributes
02.
class
BootStrap {
03.
04.
def
init = { servletContext ->
05.
06.
def
ctx=servletContext.getAttribute(
07.
ApplicationAttributes.APPLICATION_CONTEXT)
08.
def
dataSource = ctx.dataSource
09.
10.
dataSource.setMinEvictableIdleTimeMillis(
1000
*
60
*
30
)
11.
dataSource.setTimeBetweenEvictionRunsMillis(
1000
*
60
*
30
)
12.
dataSource.setNumTestsPerEvictionRun(
3
)
13.
14.
dataSource.setTestOnBorrow(true)
15.
dataSource.setTestWhileIdle(false)
16.
dataSource.setTestOnReturn(false)
17.
dataSource.setValidationQuery(
"SELECT 1"
)
18.
19.
dataSource.properties.
each
{
println
it }
20.
}
21.
}
You can do the same from grails-app/conf/spring/Resource.groovy:
01.
import
org.apache.commons.dbcp.BasicDataSource
02.
beans = {
03.
dataSource(BasicDataSource) {
04.
minEvictableIdleTimeMillis=
1800000
05.
timeBetweenEvictionRunsMillis=
1800000
06.
numTestsPerEvictionRun=
3
07.
08.
testOnBorrow=true
09.
testWhileIdle=true
10.
testOnReturn=true
11.
validationQuery=
"SELECT 1"
12.
}
13.
}
This seems to have solved the problem for me. Since my firewall was dropping the socket connections at 60 minutes, all I did was proactively run the idle object evictor every half 30 minutes, flush connections that are idle for more than 30 minutes and regenerate new connections in the pool. I also did sanity check over the connections in the pool.
相关推荐
### Grails 入门经典知识点解析 #### 一、Grails 概述 - **Grails** 是一种基于 **Groovy** 的应用框架,专为简化企业级 Java 应用程序的开发而设计。它利用了 Groovy 语言的强大功能,并且与 Java 虚拟机无缝集成...
《Getting Started with Grails》是一本专为初学者设计的指南,旨在介绍使用Groovy语言构建的敏捷Web开发框架——Grails。Grails是基于Java平台的,它利用了Groovy的简洁性和灵活性,为开发者提供了高效且强大的工具...
grails-petclinic, Grails的介绍性示例应用程序 Petclinic示例应用程序这是Grails标准的介绍性示例应用程序。 要开始使用它,只需克隆存储库,然后从本地副本运行中进行操作: ./gradlew run 在unix系统上,或者 ...
《Grails与Spring Boot的深度集成解析》 在现代Java开发领域,Grails和Spring Boot都是非常流行的框架。Grails作为Groovy语言构建的Web开发框架,以其强大的生产力和灵活的特性受到开发者的喜爱,而Spring Boot则...
restful-api, Grails插件,方便暴露非平凡的RESTful api #RESTful API插件文档尽管随后的更改可能不是向后兼容的,但 ##Status的生产质量也是如此。##Overview在附带的"介绍了rest式API插件插件。"演示文稿中提供了...
wsdl2java源码 请注意,当前的 3.x 版本使用 cxf 3.0.4 并且需要 WSS4J ...grails-app\endpoints** 和 grails-app\services** 目录中自动装配配置的类 端点创建脚本 create-endpoint 和 create-endpoin
Grails Pjax插件Pjax在Grails中。 Pjax类似于(turbolinks)[ ,在'A'标签,按钮和表单上工作。 参见_pjaxHeader grails templtate文件中的演示代码。 ## Pjax是什么? Pjax是一个jQuery插件,它使用ajax和pushState...
Grails是一套用于快速Web应用开发的开源框架,它基于Groovy编程语言,并构建于Spring、Hibernate等开源框架之上,是一个高生产力一站式框架。 Grails这个独特的框架被视为是提升工程师生产效率的动态工具,因为其...
【标题】:“curso-groovy-grails-2013” 暗示这是一个关于Groovy和Grails的在线课程,可能是在2013年创建或更新的。Groovy是一种面向对象的、动态的编程语言,常用于Java平台,而Grails是一个基于Groovy的开源Web...
标题中的"grails-fck-editor-0.3.zip_grails_grails-fck-editor"表明这是一个与Grails框架相关的插件,具体来说是FCKeditor的一个版本。FCKeditor是一款广泛使用的开源富文本编辑器,它允许用户在网页上创建和编辑...
Spring Batch PoC Grails Spring Batch、启用 CORS 的 REST API、Spring Security Core/LDAP 演示先决条件 # GVM - Tool for managing Groovy related frameworks curl -s get.gvmtool.net | bash # install Grails ...
设置GRAILS_HOME环境变量以指向提取的下载文件的根目录,并将GRAILS_HOME/bin添加到您的可执行文件PATH 。 然后在外壳中,键入以下内容: grails create-app sampleapp cd sampleapp grails run-app 要构建Grails...
### Grails框架中文第二版知识点概述 #### 一、简介 **Grails 2.0新特性** 1. **面向开发的特性** - 改进了开发者体验,例如通过简化配置来提高开发效率。 - 引入了新的工具和功能,如Groovy模板引擎Thymeleaf...
Grails专为下一代JavaWeb应用程序而设计的框架,其借助于Groovy动态语言,使Web开发变得简单而方便。Grails尽量为更多现有的Java项目创建一个全面的框架(不仅局限于视图处理),这和当前一些Java框架提供给用户的一...
Grails Shiro UI 插件是为基于Groovy的Grails框架开发的一款强大而易用的安全管理插件,它集成了Apache Shiro安全框架,为开发者提供了一整套用户认证和授权的解决方案。Apache Shiro是一款轻量级、全面的Java安全...
groovy-grails-tool-suite-3.6.4.RELEASE-e4.4.2-win32-x86_64.part1 共两个压缩包,解压后将扩展名.zip.bak改为.zip再次解压。
Java8 Temporal Grails插件 Java 8时态插件在Grails中集成了Java 8的新DateTime API(Instant,LocalDate,LocalTime等)。 该插件与Grails 2.5.x(支持JDK8的Grails的第一个版本)兼容。 提供将输入绑定到Java 8 ...