歲月如哥
          人生非夢
          posts - 50,comments - 144,trackbacks - 0
          背景: 
              XX系統(tǒng)實施一段時間之后,出現(xiàn)數(shù)據(jù)連接池滿,第一次通過修改if(con!=null && con.isClosed()){con.close();}這樣的邏輯錯誤解決部分問題。第二次通過徹底復(fù)查代碼,修改了connection、session沒有釋放的問題,基本上保證我們自己寫的代碼沒有數(shù)據(jù)庫連接不釋放的問題。但是臨近近期還是出現(xiàn)連接池滿的問題。。。

          過程:
              從日志看,除了有大量工作流報錯之外程序很少有異常,類似如下:
          引用:
          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
          最開始基本確定了是工作流報錯導(dǎo)致數(shù)據(jù)庫連接池不釋放,理由:
                  a、上面的錯和hibernate的session有關(guān)
                  b、在sybase執(zhí)行sp_who發(fā)現(xiàn)大量不釋放連接所占用的庫為DB_LC,而這個庫就是工作流相關(guān)的庫。
                  c、從sybase的sysprocesses表查看,不釋放連接是每天新增10-30不等,隨機(jī)統(tǒng)計了日志某天的如前所述的異常為27個,而從數(shù)據(jù)庫端統(tǒng)計該天新增連接也是27個。
               因為自己對工作流不熟悉,所以每次都是把情況反映給相關(guān)人員處理。前幾天去客戶現(xiàn)場正好抓取了一下不釋放連接正在執(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)
          這個結(jié)果用處不大,很好奇這個問題,所以找了一份工作流的源碼,找到報錯的類DbPersistenceService.close方法,如下:
          復(fù)制內(nèi)容到剪貼板
          代碼:
          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);
                }
              }
            }
          一看真是嚇一跳,程序在執(zhí)行到session.flush();時候報錯的話,如果mustSessionBeClosed為true根本不能執(zhí)行到后面的session.close(),會導(dǎo)致數(shù)據(jù)庫連接不釋放的問題……基本確定問題所在了,就在本地試著復(fù)現(xiàn)一下問題(因為前面所描述的異常在開發(fā)環(huán)境無法復(fù)現(xiàn),所以只能強(qiáng)制在flush后拋異常),果然不出意料。
               因為這個是jbpm3.1.2版本,覺得應(yīng)該是jbpm的bug吧,就又下載了一份jbpm3.3.0GA源碼,找到DbPersistenceService.close()方法:
          復(fù)制內(nèi)容到剪貼板
          代碼:
            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);
              }
            }
          果然在3.3.0版本中,當(dāng)flush、close等操作出現(xiàn)異常時候,都會調(diào)用closeSession()和closeConnection()以保證連接正常釋放。照貓畫虎在該方法寫了關(guān)閉session和connection的方法,準(zhǔn)備月底發(fā)布新版本試試。

          結(jié)論:
             XX系統(tǒng)工作流jbpm3.1.2存在連接不釋放的bug,當(dāng)然前提是程序執(zhí)行數(shù)據(jù)庫操作報錯的情況下(如session.flush)。雖然解決了連接不釋放的問題,但是這個關(guān)于這個報錯的深層原因還沒搞清楚。另外和相關(guān)人員確認(rèn),工作流的這些異常可以cacth掉,到目前為止除了引起連接不釋放之外,沒有發(fā)現(xiàn)其他問題。
          posted on 2009-06-22 17:38 歲月如歌 閱讀(1870) 評論(0)  編輯  收藏 所屬分類: java
          主站蜘蛛池模板: 资溪县| 景德镇市| 荥经县| 娄烦县| 仁寿县| 炎陵县| 会昌县| 丹阳市| 凌源市| 九寨沟县| 玛曲县| 建昌县| 延吉市| 崇州市| 微博| 新绛县| 满城县| 且末县| 杂多县| 陇川县| 栾川县| 溧水县| 西乡县| 凤阳县| 保定市| 民县| 军事| 华蓥市| 青铜峡市| 凤台县| 鹤庆县| 秦皇岛市| 沙河市| 新乡市| 雷波县| 大方县| 东安县| 革吉县| 河西区| 新巴尔虎右旗| 定结县|