1

PostgreSqlのDoctrine ORMで悲観的ロックを使用しようとしています。デフォルト設定の Doctrine と PostgreSql (変更なし)。

これはコード例 (Symfony コマンド) です。

$sleep- これは秒単位の時間です

$manager = $this->getContainer()->get('mmi.manager.message');
$conn = $manager->em()->getConnection();

$manager->em()->getConnection()->beginTransaction();
try {
    $entity = $manager->repo()->find('cd7eb9e9', LockMode::PESSIMISTIC_WRITE);

    $entity->setState(EntityActionInterface::STATE_IN_PROGRESS);
    $manager->em()->persist($entity);
    $manager->em()->flush();

    $ts = (new \DateTime())->getTimestamp();
    $output->writeln("TS: {$ts}");

    if ($sleep) {
        $output->writeln("Sleep: {$sleep}");
        sleep($sleep);
    }

    $entity->setMessage([$ts]);
    $manager->em()->persist($entity);
    $manager->em()->flush();

    $conn->commit();
} catch (PessimisticLockException $ex) {
    var_dump(get_class($ex));

    $conn->rollBack();
    throw $ex;
} catch (\Exception $ex) {
    var_dump(get_class($ex));

    $conn->rollBack();
    throw $ex;
}

テスト方法

2 つのコマンドを実行します。最初のコマンドは、タイムアウト 20 秒で実行されます。2 番目のコマンドは、タイムアウトなしで実行されます。

期待される結果

2 番目のコマンド スローPessimisticLockException

実結果

2 番目のコマンドは、最初のトランザクションのコミットを待ってから行を更新します。

質問

PessimisticLockException行がロックされている場合、Doctrine をスローさせるにはどうすればよいですか?

4

1 に答える 1

1

Fo first:PESSIMISTIC_WRITE PostgreSql プラットフォームでの作業方法

PESSIMISTIC_WRITE - これはクエリSELECT ... FOR UPDATEです。このクエリは、選択された行と、同じ行を要求した他の接続をロックし、現在の接続が完了するのを待ちます。

私の場合、2 つのプロセスを開始し、2 つ目のプロセスは最初のプロセスの終了を待っています。そして、これは正しい動作です。

私の間違い: Doctrine のソース コードを調べて、PessimisticLockExceptionクラスを見つけました。したがって、悲観的ロックを使用すると、Doctrine がこの例外をスローすることにしました。しかし、このクラスは Doctrine のどこにも使用されていません。

それで、私はこの問題をどのように解決しましたか。

私の現在の実装では、ロックされた行に対して nowait 動作が必要でした。PostgreSql 9.5 にはこの機能があります - SKIP LOCKED。しかし、Doctrine にはこの機能の実装がありません。

私たちに何ができるでしょうか?

doctrine postgresql platfrom クラスをオーバーライドできます。

use Doctrine\DBAL\Platforms\PostgreSqlPlatform;

class PgSqlPlatform extends PostgreSqlPlatform
{
    /**
     * Returns the FOR UPDATE expression.
     *
     * @return string
     */
    public function getForUpdateSQL()
    {
        return 'FOR UPDATE SKIP LOCKED';
    }
}

サービスとして定義する

#app/config/services.yml
services:
    mmi.dbal.pgsql_platform:
        class: {Namespace}\PgSqlPlatform

そして、tot doctrine configを設定します

#app/config/config.yml
doctrine:
    dbal:
        connections:
            mmi:
                driver:   pdo_pgsql
                host:     ...
                ...
                platform_service: 'mmi.dbal.pgsql_platform'

それで全部です。これで、待つことなく悲観的ロックを使用できます。

于 2016-11-15T13:23:12.410 に答える