Doctrine Transaction Unexpected Nesting Level Increasing


作業環境

PHP5.3 + Fuel 1.8.1 + Doctrine

問題

業務上、1つのトランザクションの中に、下記のPseudoコードの感じで処理を実行し、最後纏めてコミットしたい。

$em = \Fuel\Doctrine::manager("default");
$em->getConnection()->beginTransaction();
…
DB登録処理呼び出し関数1($em);
DBクエリ処理呼び出し関数1($em);
繰り返し処理{
…
    DB登録処理呼び出し関数2($em);
    DB更新処理呼び出し関数1($em);
…
}
…
DB更新処理呼び出し関数2($em);
$em->commit();

が、コミットはどうしても成功せず、3日を悩まされました。

調査経過

たまたま、Doctrineトランザクション管理のNestingレベルを見ようとして、
Doctrineトランザクション公式参照⇒https://www.doctrine-project...

⇓をDB変更や登録関数の実行前後に貼って、驚きの結果がわかりました。
echo "TransactionNestingLevel Before Insert :".$em->getConnection()->getTransactionNestingLevel()."\n";

繰り返し処理の中で、Nestingレベルの数値がどんどん上がる!!!

TransactionNestingLevel Before Insert :11
TransactionNestingLevel After Insert :11
TransactionNestingLevel Before Insert :41
TransactionNestingLevel After Insert :41
TransactionNestingLevel Before Insert :215
TransactionNestingLevel After Insert :215
TransactionNestingLevel Before Insert :301
TransactionNestingLevel After Insert :301
TransactionNestingLevel Before Insert :345
TransactionNestingLevel After Insert :345
TransactionNestingLevel Before Insert :395
TransactionNestingLevel After Insert :395
TransactionNestingLevel Before Insert :461
TransactionNestingLevel After Insert :461

Are you kidding me!!なんじゃと

トランザクション階層が461階層に⇓

トランザクション階層1
    トランザクション階層2
        トランザクション階層3
                    …
                                        トランザクション階層461

変更をDBに反映するために、$em->commit();460回実行せよ!!だからコミットできないか!

待って!今回操作対象データが230件。最後に461件というのは、230の2倍がプラスされたんじゃないか?都合がいい数値に手がかりがありそう!!

いろいろ実験を経て、下記の感じのコードを実行すると、不具合の箇所が特定できました。

$em = \Fuel\Doctrine::manager("default");
$em->getConnection()->beginTransaction();
echo "TransactionNestingLevel beginTransaction:".$em->getConnection()->getTransactionNestingLevel()."\n";
$info = array();
$info[0] = array(
    "Sequence"=>5
    …
);
$info[1] = array(
    "Sequence"=>6
    …
);
echo "TransactionNestingLevel After Setting array :".$em->getConnection()->getTransactionNestingLevel()."\n";

結果:

TransactionNestingLevel beginTransaction :1
TransactionNestingLevel After Setting array :3

結論

配列代入処理が一回実行で、DoctrineのトランザクションNestingレベルが1 Plusされることが分かりました。だからか!前230件の2倍Plusされるのも、登録関数が呼ばれる前に、データを配列で渡すために、配列代入処理を2回実施しましたからです。

わけわからねぇ!!!!!

対策

はい!退避策として、繰り返し処理の中のDB変更処理を全部外に出して、$em->getConnection()->setAutoCommit(false);で、繰り返し処理でPlusされたトランザクションレベルを1に戻した。

$em = \Fuel\Doctrine::manager("default");
$em->getConnection()->beginTransaction();
…
DB登録処理呼び出し関数1($em);
DBクエリ処理呼び出し関数1($em);
繰り返し処理{
…
…
}
…
$em->getConnection()->setAutoCommit(false);
DB登録処理呼び出し関数2($em);
DB更新処理呼び出し関数1($em);
…
DB更新処理呼び出し関数2($em);
$em->commit();

こんなつまらないことで、3日間が蒸発した!!!