XX系統(tǒng)實(shí)施一段時(shí)間之后,出現(xiàn)數(shù)據(jù)庫(kù)連接池滿,第一次通過(guò)修改if(con!=null && con.isClosed()){con.close();}這樣的邏輯錯(cuò)誤解決部分問(wèn)題。第二次通過(guò)徹底復(fù)查代碼,修改了connection、session沒(méi)有釋放的問(wèn)題,基本上保證我們自己寫的代碼沒(méi)有數(shù)據(jù)庫(kù)連接不釋放的問(wèn)題。但是臨近近期還是出現(xiàn)連接池滿的問(wèn)題。。。
過(guò)程:
從日志看,除了有大量工作流報(bào)錯(cuò)之外程序很少有異常,類似如下:
引用:
2009-06-12 15:44:34,187 [http-80-Processor44] [org.hibernate.event.def.AbstractFlushingEventListener] [ERROR] - Could not synchronize database state with session
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [org.jbpm.graph.exe.Token#35000000000033432]
..............................................
at org.jbpm.persistence.db.DbPersistenceService.close(DbPersistenceService.java:180)
..............................................
2009-06-12 15:44:34,187 [http-80-Processor44] [org.jbpm.svc.Services] [ERROR] - problem closing service 'persistence'
org.jbpm.persistence.JbpmPersistenceException: couldn't flush hibernate session
at org.jbpm.persistence.db.DbPersistenceService.close(DbPersistenceService.java:182)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [org.jbpm.graph.exe.Token#35000000000033432]
at org.jbpm.persistence.db.DbPersistenceService.close(DbPersistenceService.java:180)
... 54 more
a、上面的錯(cuò)和hibernate的session有關(guān)
b、在sybase執(zhí)行sp_who發(fā)現(xiàn)大量不釋放連接所占用的庫(kù)為DB_LC,而這個(gè)庫(kù)就是工作流相關(guān)的庫(kù)。
c、從sybase的sysprocesses表查看,不釋放連接是每天新增10-30不等,隨機(jī)統(tǒng)計(jì)了日志某天的如前所述的異常為27個(gè),而從數(shù)據(jù)庫(kù)端統(tǒng)計(jì)該天新增連接也是27個(gè)。
因?yàn)樽约簩?duì)工作流不熟悉,所以每次都是把情況反映給相關(guān)人員處理。前幾天去客戶現(xiàn)場(chǎng)正好抓取了一下不釋放連接正在執(zhí)行的sql,基本都是亂碼,如下:
引用:
DBCC execution completed. If DBCC printed error messages, contact a user with System Administrator (SA) role.
SQL Text: !
DBCC execution completed. If DBCC printed error messages, contact a user with System Administrator (SA) role.
(1 row affected)
47
DBCC execution completed. If DBCC printed error messages, contact a user with System Administrator (SA) role.
SQL Text: *
DBCC execution completed. If DBCC printed error messages, contact a user with System Administrator (SA) role.
(1 row affected)
49
DBCC execution completed. If DBCC printed error messages, contact a user with System Administrator (SA) role.
SQL Text:
DBCC execution completed. If DBCC printed error messages, contact a user with System Administrator (SA) role.
(1 row affected)
復(fù)制內(nèi)容到剪貼板
一看真是嚇一跳,程序在執(zhí)行到session.flush();時(shí)候報(bào)錯(cuò)的話,如果mustSessionBeClosed為true根本不能執(zhí)行到后面的session.close(),會(huì)導(dǎo)致數(shù)據(jù)庫(kù)連接不釋放的問(wèn)題……基本確定問(wèn)題所在了,就在本地試著復(fù)現(xiàn)一下問(wèn)題(因?yàn)榍懊嫠枋龅漠惓T?span id="wmqeeuq" class="t_tag" onclick="tagshow(event)" href="tag.php?name=%BF%AA%B7%A2">開發(fā)環(huán)境無(wú)法復(fù)現(xiàn),所以只能強(qiáng)制在flush后拋異常),果然不出意料。代碼:
public void close() {
if ( (session!=null)
&& (transaction==null)
&& (isRollbackOnly)
) {
throw new JbpmException("setRollbackOnly was invoked while configuration specifies user managed transactions");
}
if (messagingSession!=null) {
messagingSession.closeOpenIterators();
}
if (schedulerSession!=null) {
schedulerSession.closeOpenIterators();
}
if ( (isTransactionEnabled)
&& (transaction!=null)
) {
if (isRollbackOnly) {
try {
log.debug("rolling back hibernate transaction");
mustSessionBeFlushed = false; // flushing updates that will be rolled back is not very clever :-)
transaction.rollback();
} catch (Exception e) {
throw new JbpmPersistenceException("couldn't rollback hibernate session", e);
}
} else {
try {
log.debug("committing hibernate transaction");
mustSessionBeFlushed = false; // commit does a flush anyway
transaction.commit();
} catch (Exception e) {
try {
// if the commit fails, we must do a rollback
transaction.rollback();
} catch (Exception e2) {
// if the rollback fails, we did what we could and you're in
// deep shit :-(
log.error("problem rolling back after failed commit", e2);
}
throw new JbpmPersistenceException("couldn't commit hibernate session", e);
}
}
}
if (mustSessionBeFlushed) {
try {
log.debug("flushing hibernate session");
session.flush();
} catch (Exception e) {
throw new JbpmPersistenceException("couldn't flush hibernate session", e);
}
}
if (mustSessionBeClosed) {
try {
log.debug("closing hibernate session");
session.close();
} catch (Exception e) {
throw new JbpmPersistenceException("couldn't close hibernate session", e);
}
}
if (mustConnectionBeClosed) {
try {
log.debug("closing jdbc connection");
connection.close();
} catch (Exception e) {
throw new JbpmPersistenceException("couldn't close jdbc connection", e);
}
}
}
因?yàn)檫@個(gè)是jbpm3.1.2版本,覺(jué)得應(yīng)該是jbpm的bug吧,就又下載了一份jbpm3.3.0GA源碼,找到DbPersistenceService.close()方法:
復(fù)制內(nèi)容到剪貼板
果然在3.3.0版本中,當(dāng)flush、close等操作出現(xiàn)異常時(shí)候,都會(huì)調(diào)用closeSession()和closeConnection()以保證連接正常釋放。照貓畫虎在該方法寫了關(guān)閉session和connection的方法,準(zhǔn)備月底發(fā)布新版本試試。代碼:
public void close() {
if ( (session!=null)
&& !isTransactionActive()
&& (isRollbackOnly())
) {
throw new JbpmException("setRollbackOnly was invoked while configuration specifies user managed transactions");
}
if ( (isTransactionEnabled)
&& (transaction!=null)
) {
if (! isRollbackOnly()) {
Exception commitException = commit();
if (commitException!=null) {
rollback();
closeSession();
closeConnection();
throw new JbpmPersistenceException("hibernate commit failed", commitException);
}
} else { // isRollbackOnly==true
Exception rollbackException = rollback();
if (rollbackException!=null) {
closeSession();
closeConnection();
throw new JbpmPersistenceException("hibernate rollback failed", rollbackException);
}
}
}
Exception flushException = flushSession();
if (flushException!=null) {
// JBPM-1465 transaction has been either committed or rolled back at this point
// on the other hand, it is possible that no transaction is underway
// hence rolling back here is redundant and possibly dangerous
closeSession();
closeConnection();
throw new JbpmPersistenceException("hibernate flush failed", flushException);
}
Exception closeSessionException = closeSession();
if (closeSessionException!=null) {
closeConnection();
throw new JbpmPersistenceException("hibernate close session failed", closeSessionException);
}
Exception closeConnectionException = closeConnection();
if (closeConnectionException!=null) {
throw new JbpmPersistenceException("hibernate close connection failed", closeConnectionException);
}
}
結(jié)論:
XX系統(tǒng)工作流jbpm3.1.2存在連接不釋放的bug,當(dāng)然前提是程序執(zhí)行數(shù)據(jù)庫(kù)操作報(bào)錯(cuò)的情況下(如session.flush)。雖然解決了連接不釋放的問(wèn)題,但是這個(gè)關(guān)于這個(gè)報(bào)錯(cuò)的深層原因還沒(méi)搞清楚。另外和相關(guān)人員確認(rèn),工作流的這些異常可以cacth掉,到目前為止除了引起連接不釋放之外,沒(méi)有發(fā)現(xiàn)其他問(wèn)題。