またもやトランザクションと格闘
EthnaのAppManagerに、S2Dao.PHP5のTxManagerを兼ねさせて使っているのですが、昨日からまたトランザクション関連で問題発生。2点ほど悩んでいたのですが、やっと解決しました。
まず、トランザクションAdviceとしてpdo.requiresNewTxを適用していると、resume後に「There is no active transaction」が出てしまう。resumeで書き戻すPDOオブジェクトがcommit/rollback済みになってしまっているようだ。
ソースを見てると、suspend時にPDOオブジェクトを参照渡しで保存している。__clone()使ってコピーを保存しておくか、新しくPDOオブジェクトを作るべきじゃないのかな?
おそらく後者で対応すべきだと思うのだが、どう動くのが正しいのか確信が持てないので、別々のトランザクションが重ならないようにした。まぁ、なかなかそんな用途は無いんだけど。
そして、AppTxManager1のmethodAからAppTxManager2のmethodBを呼ぶと(どっちもトランザクションAdvice適用)、それぞれが別のトランザクションとして動いてしまう。methodAがrollbackしてもmethodBの処理はrollbackしないのだ。
これは、PDOオブジェクトが、AppManagerごとに別々なものが生成されていることが原因。
機能的にAppManagerを分けているので、1つのAppManagerにまとめるのは避けたい。でもまとめてトランザクションを効かせられないのは困る。
そこで、PDOオブジェクトを1つだけ作るように修正してみる。
まず、PDOオブジェクトを作っているS2Container_PDODataSourceを継承したクラスを作成して、PDOオブジェクトをstaticに保持するように修正。
class App_PDODataSource extends S2Container_PDODataSource
{
static protected $db_ = null;public function getConnection()
{
$this->db =& self::$db_;
return parent::getConnection();
}
}
次に、pdo.diconをApp_PDODataSourceを使うように修正。
<components namespace="pdo">
<component name="dataSource" class="App_PDODataSource">
(中略)
</component>
</components>
これでPDOオブジェクトは、1セッションごとに1つだけになる。PHPは、アプリケーション内でマルチスレッドになることは無いと思うので、これで問題ないはず。最初のpdo.requiresNewTxの対策で、新しいPDOオブジェクトが欲しいときに困るけど(^-^;
ところで、なぜS2Container_PDODataSourceはS2Container.PHP5の方に入っているのだろう?S2Dao.PHP5以外に使ってるところがあるのかな。