アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

Doctrineのオブジェクト単位での悲観的ロックについて

皆さん、こんばんは。
笹亀です。

ついにiphone4の白を待ちきれずに黒を買いました。
いまさらながらiphoneの素晴らしさを体感しております。

さて本日はDoctrineのオブジェクト単位での悲観的ロックについて解説をさせていただきます。
Doctrineのトランザクション処理を行う際にConnection単位で行うのが一般的な方法ですが、あまり知られていませんが、Doctrineには通常のデータベースで行う方法とは別にオブジェクト単位でロックをかけることができます。
http://www.doctrine-project.org/documentation/manual/1_1/ja/component-overview#%E3%83%9E%E3%83%8D%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%BC%E3%82%92%E3%83%AD%E3%83%83%E3%82%AF%E3%81%99%E3%82%8B

今回は実際にコードを作成して試してみます。
テスト用に事前に作成したMemberオブジェクトに対してロックをかけます。

実装の確認をするためにActionは2つ用意します。
1.テーブルにロックをかける側のAction


  public function executeIndex(sfWebRequest $request)
  {
    echo 'Lock Start!';
    
    //Doctrineのコネクションを取得
    $conn = Doctrine_Manager::getInstance()->getCurrentConnection();

    //コネクションを渡し、ロックマネージャーを生成する
    $lockingManager = new Doctrine_Locking_Manager_Pessimistic($conn);

    //適当なMemberオブジェクトを取得
    $member = Doctrine::getTable('Member')->find(1);

    try {
      //ロックのタイムアウトを設定
      $lockingManager->releaseAgedLocks(300);

      //オブジェクトに対してロックする
      $getLock = $lockingManager->getLock($member, 'jwage');

      //ロックがかかったならばデータを更新
      if ($getLock) {
        echo 'Got Lock!!';
        $member->name  = 'testhoge';
        $member->email = 'sasa@asial.co.jp';
        $member->save();
        sleep(10);
      } else {
        echo 'Sorry, someone else is currently working on this record';
      }
      //ロックを解除する
      $lockingManager->releaseLock($member, 'jwage');

    //例外が発生
    } catch(Doctrine_Lock_Exception $e) {
      echo $e->getMessage();
      //ロックを解除する
      $lockingManager->releaseLock($member, 'jwage');
    }

    echo '';
    echo 'Lock Finished!';
    exit;
  }



2.テーブルロック中にアクセスする側のAction(Memberオブジェクトにアクセスしてデータを表示する


  public function executeTestLock()
  {
    echo 'Get Start';

    $member_obj = Doctrine::getTable('Member')->find(1);

    if ($member_obj) {
      print_r($member_obj->toArray());
    }
    echo '';
    echo 'Get Finished!';
    exit;
  }


実際にロックをかけてからオブジェクトに対してアクセスをして試してみます。

まずは、作成した1のロックする処理を実行します。


次にsleepしている間に2を実行します。



画像では伝えきれませんが、1の処理でロックが掛かっている間は2は実行されずに、1の処理が完了後に2が実行されデータが更新されます。



一般的な方法に比べてスクリプトのコストが低く利用しやすいのが特徴だと思います。オブジェクト単位でロックをかけて情報を操作したいときなどに利用してみてはいかがでしょうか。
※重要なデータを扱う場合はやはりちゃんとトランザクション処理をしてテーブルロックをかけて行う方が一般的ですので、オブジェクトでのロックを使用して実装する際には自己責任でお願い致します。