CodeIgniter框架数据库事务处理的设计缺陷和解决方案
起因:在我们线上的某个业务中,使用较老版本的CodeIgniter框架,其中的DB类中,对DB事物处理部分存在着一个设计上的缺陷,或许也算不上缺陷吧。但他却影响了我们生产环境,导致连锁反应。对业务产生较大影响,且不容易排查。这个问题,我在今年的3月中旬,曾向codeigniter中国的站长Hex 报告过,之后,我也忘记这件事情了。直到今天,我们线上业务又一次以为这个问题,害的我又排查一次。具体原因,各位且先听我慢慢说完。(这个问题同样存在于最新版本Version 2.1.0中) 分析:以CodeIgniter框架Version 2.1.0为例,在systemdatabaseDB_driver.php的CI_DB_driver类中第58行有个$_trans_status属性。 代码如下: 同时,这个类的query方法中,有赋值此属性的代码,见文件306、307行 代码如下: _trans_status = FALSE;
这里也给了注释,告诉我们,如果使用了事物处理,那么这属性将成为一个回滚的决定条件。 在520行的事物提交方法trans_complete中,如下代码: 代码如下: trans_enabled)
{
return FALSE;
}
// When transactions are nested we only begin/commit/rollback the outermost ones if ($this->_trans_depth > 1) { $this->_trans_depth -= 1; return TRUE; } // The query() function will set this flag to FALSE in the event that a query failed if ($this->_trans_status === FALSE) { $this->trans_rollback(); // If we are NOT running in strict mode,we will reset // the _trans_status flag so that subsequent groups of transactions // will be permitted. if ($this->trans_strict === FALSE) { $this->_trans_status = TRUE; } log_message('debug','DB Transaction Failure'); return FALSE; } $this->trans_commit(); return TRUE; } 在535行中,如果_trans_status属性如果是false,那么将发生回滚,并且返回false。 在我们的业务代码中,由于程序员疏忽,没有判断trans_complete()方法是否正确执行,直接告诉用户操作成功,但实际上,程序已经向DB下达回滚指令,并未成功更新DB记录。当用户执行下一步操作时,程序又发现相应记录并未更新,又提醒用户上个操作没有完成,通知用户重新执行。如此反复… 排查的过程,也是挺有意思的,起初从PHP代码中,总是不能确定问题所在,并没有把焦点放到trans_complete()方法的返回上。直到后来strace抓包分析,才知道是因为此属性而导致了回滚。 代码如下: |