关于在Laravel 中支持Mongo事务的一些问题
在GitHub上面看到了这么一个 PR ,看了一下代码似乎不能够通过测试用例。 但是实际上的CI构建日志却显示全部通过。希望大佬给予解答。关键代码会在文章下方给出,文章也附有GitHub源码链接,希望大家帮我看看。
主要我有疑问的是这个 tests/TransactionTest.php
测试用例的testTransaction
这个测试。 Diff信息链接 根据 Laravel 源码中 src/Illuminate/Database/Concerns/ManagesTransactions.php
ManagesTransactions.php#L44第44行的代码,想要事务能够成功执行 $this->transactions
的值一定要是 1 。而扩展的代码重写了beginTransaction
这个函数,那么原本115行的 $this->transactions++;
就没有被执行到,导致 44 行的判断 if ($this->transactions == 1)
就不可能成立事务不会被执行。
Laravel 中的 beginTransaction
/**
* Execute a Closure within a transaction. * * @param \Closure $callback
* @param int $attempts
* @return mixed
* * @throws \Throwable
*/
public function transaction(Closure $callback, $attempts = 1)
{
for ($currentAttempt = 1; $currentAttempt <= $attempts; $currentAttempt++) {
$this->beginTransaction();
// We'll simply execute the given callback within a try / catch block and if we
// catch any exception we can rollback this transaction so that none of this // gets actually persisted to a database or stored in a permanent fashion. try {
$callbackResult = $callback($this);
}
// If we catch an exception we'll rollback this transaction and try again if we
// are not out of attempts. If we are out of attempts we will just throw the // exception back out and let the developer handle an uncaught exceptions. catch (Throwable $e) {
$this->handleTransactionException(
$e, $currentAttempt, $attempts
);
continue; }
try {
if ($this->transactions == 1) { // 44行
$this->getPdo()->commit();
optional($this->transactionsManager)->commit($this->getName());
}
$this->transactions = max(0, $this->transactions - 1);
} catch (Throwable $e) {
$this->handleCommitTransactionException(
$e, $currentAttempt, $attempts
);
continue; }
$this->fireConnectionEvent('committed');
return $callbackResult;
}
}
.....
/**
* Start a new database transaction.
*
* @return void
*
* @throws \Throwable
*/
public function beginTransaction()
{
$this->createTransaction();
$this->transactions++; // 115行
optional($this->transactionsManager)->begin(
$this->getName(), $this->transactions
);
$this->fireConnectionEvent('beganTransaction');
}
扩展中的 beginTransaction
public function beginTransaction()
{
$this->session_key = uniqid();
$this->sessions[$this->session_key] = $this->connection->startSession();
$this->sessions[$this->session_key]->startTransaction([
'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY),
'writeConcern' => new WriteConcern(1),
'readConcern' => new ReadConcern(ReadConcern::LOCAL)
]);
}
扩展中的测试用例。
public function testTransaction()
{
$count = DB::collection('users')->count();
$this->assertEquals(1, $count);
$new_age = $this->originData['age'] + 1;
DB::transaction(function () use ($new_age) {
DB::collection('users')->insert($this->insertData);
DB::collection('users')->where('name', $this->originData['name'])->update(['age' => $new_age]);
});
$count = DB::collection('users')->count();
$this->assertEquals(2, $count);
$checkInsert = DB::collection('users')->where('name', $this->insertData['name'])->first();
$this->assertNotNull($checkInsert);
$this->assertEquals($this->insertData['age'], $checkInsert['age']);
$checkUpdate = DB::collection('users')->where('name', $this->originData['name'])->first();
$this->assertEquals($new_age, $checkUpdate['age']);
}