sfDoctrineMasterSlavePluginを使う時のTips

Symfonyアドベントカレンダー2010には忙しすぎて参加していません(ごめんなさいx2)
Doctrine on symfony1.4でマスタースレーブ | やぐにっきに、ちょっと補足しておくと喜ぶ人がいる気がしますので書いてみます。

その前に

symfonyの情報を沢山提供してくださっている皆様、いつも大変お世話になっております。
ありがとうございます。
来年もどうぞよろしくお願いいたします。

来年こそは心を入れ替えて、僕もお役に立てるといいなと思います。

sfDoctrineMasterSlavePluginについて

さて記事にあるように、symfony1.x + Doctrine1.xなサービスでデータベースをmaster-slave構成で冗長化する場合、sfDoctrineMasterSlavePluginを使うというのはとても良い選択だと思います。

sfDoctrineMasterSlavePluginの素晴らしいと思う点は主にこんな所でしょうか?

  • 複数のslave複数のmasterに対応
  • 導入が容易
  • Queryがmasterに向いているのかslaveに向いているのかlogを見れば一目で分かる

本当に素晴らしいですね。
でも、局所的にバッドノウハウ的なTIPSは必要です。

補足説明させていただきたいのは、明示的にMasterからSELECTする方法についてです。

明示的にMasterからSelectするシチュエーション

記事にもあるようにsfDoctrineMasterSlavePluginは黙っていれば、参照系はmaster、更新系はslaveに振り分けてくれます。
さらにtransactionの中では参照系でもmasterを使ってくれます。

しかし、サービスによってはtransactionの外でもmasterから参照したい時というのはあります。
例えば下記のような状況が考えられます。

1. table lockが多発するためtransactionをあえて使わない
僕の場合、そこそこアクセスのあるソーシャルアプリでは緩い実装をするため、ユーザーが喜ぶ処理から悲しむ処理に向かってTableに更新をかけ、最低限不整合がおこらないレベルでトランザクションをあえて使いません。

2. ユーザーのスコア表示などわずかな遅延も許されない情報を参照したい
MasterSlaveの遅延はありきで考えています。

明示的にMasterからSELECTする方法

前置きがとっても長くなりましたが、Masterを参照するために僕はこのような方法を使っています。

1. Doctrine_Tableを継承したmyDoctrineTableを作る
2. *Table の継承元をmyDoctrineTableに全て変更する。
3. Masterを参照したい時にcreateQuery()の代わりにcreateMasterQuery()を使う

myDoctrineTable

<?php

class myDoctrineTable extends Doctrine_Table
{
    public function insert(array $fields)
    {
        return $this->getMasterConnection()->insert($this, $fields);
    }

    public function update(array $fields, array $identifier)
    {
        return $this->getMasterConnection()->update($this, $fields, $identifier);
    }

    public function delete($identifier)
    {
        return $this->getMasterConnection()->delete($this, $identifier);
    }

    public function replace(array $fields, array $keys)
    {
        return $this->getMasterConnection()->replace($this, $fields, $keys);
    }

    public function getMasterConnection()
    {
      return ProjectConfiguration::getActive()->getMasterConnection($this->getConnection());
    }

    public function getSlaveConnection()
    {
      return ProjectConfiguration::getActive()->getSlaveConnection($this->getConnection());
    }

    public function createMasterQuery($alias = '')
    {
        if ( ! empty($alias)) {
            $alias = ' ' . trim($alias);
        }

        return Doctrine_Query::create($this->getMasterConnection(), 'Doctrine_Query')
            ->from($this->getComponentName() . $alias);
    }
}

その他のメソッドはおまけです。


Doctrineはとても便利ですが、メモリを食い過ぎることと、処理が複雑すぎるという欠点があります。
メモリを食い過ぎるとスワップが発生しやすくなりますし、処理が複雑すぎると泥臭い改造が出来なくなります。

ORMを選択する時は、初期開発コストと運用コストを天秤にかけてじっくりと選択することをおすすめします。

これからはDoctrine2かもしれませんが、相変らずメモリは食うみたいですので開発前には慎重に検討した方が良いかもしれません。