だいこんの日々2

軸足をtwitterとfacebookに移しつつも、技術的な話はここに書いていきます。

またもやトランザクションと格闘

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以外に使ってるところがあるのかな。