0

これは this oneからのフォローアップの質問なので、ロック (ブロック) を使用できることはわかっていますが、述語ロックとシリアル化可能なトランザクション分離を使用したいと考えています。

私が欲しいのは、関数/クエリを X 回再試行するシリアル化エラーの汎用ハンドラーです。

例として、私はこれを持っています:

CREATE SEQUENCE account_id_seq;

CREATE TABLE account
(
  id integer NOT NULL DEFAULT nextval('account_id_seq'),
  title character varying(40) NOT NULL,
  balance integer NOT NULL DEFAULT 0,
  CONSTRAINT account_pkey PRIMARY KEY (id)
);

INSERT INTO account (title) VALUES ('Test Account');

CREATE OR REPLACE FUNCTION mytest() RETURNS integer AS $$
DECLARE
    cc integer;
BEGIN
    cc := balance from account where id=1;

    RAISE NOTICE 'Balance: %', cc;
    perform pg_sleep(3);

    update account set balance = cc+10 where id=1 RETURNING balance INTO cc;

    return cc;
END
$$
LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION myretest() RETURNS integer AS $$
DECLARE
    tries integer := 5;
BEGIN
    WHILE TRUE LOOP
        BEGIN -- nested block for exception
            RETURN mytest();
        EXCEPTION
            WHEN SQLSTATE '40001' THEN
                IF tries > 0 THEN
                    tries := tries - 1;
                    RAISE NOTICE 'Restart! % left', tries;
                ELSE
                    RAISE EXCEPTION 'NO RESTARTS LEFT';
                END IF;
        END;
    END LOOP;
END
$$
LANGUAGE plpgsql;

したがって、mytest()同時に直接呼び出すと、最後のコミットでシリアライゼーション エラーが発生します。

4SO$ psql -c "select mytest()" & PIDA=$! && psql -c "select mytest()" && wait $PIDA
[1] 4909
NOTICE:  Balance: 0
NOTICE:  Balance: 0
 mytest 
--------
     10
(1 row)

ERROR:  could not serialize access due to concurrent update
CONTEXT:  SQL statement "update account set balance = cc+10 where id=1 RETURNING balance"
PL/pgSQL function mytest() line 10 at SQL statement

私が呼び出すmyretest()と、例外が発生する5回目の試行まで実行を試みる必要mytest()があります。

したがって、ここに 2 つのポイントがあります (ポイント 2 もポイント 1 を無効にする可能性があります)。

  • myretest()期待どおりに動作しません。同時スレッドが終了した後でも、すべての反復で serialiation_failure 例外が発生します。トランザクションを「リセット」するために追加する必要があるものはありますか?

  • myretest()「ラッパー」関数自体を必要とせずに、システム内のすべての呼び出された関数に適用されるように、これを (ロジック) ジェネリックにするにはどうすればよいですか?

4

1 に答える 1

1

またはSQLSTATEのエラーを受け取ったときにトランザクションを最初からやり直すフレームワークを使用している限り、シリアライズ可能なトランザクションはまさに探しているものを提供します。4000140P01

PostgreSQL では、関数は常にトランザクションのコンテキストで実行されます。「ラッパー」関数のコンテキスト内で新しいトランザクションを開始することはできません。それには、一般に「ストアド プロシージャ」と呼ばれる、わずかに異なる機能が必要になります。これは PostgreSQL には存在しません。したがって、再起動を管理するロジックを、トランザクションをデータベースに送信するコードに組み込む必要があります。幸いなことに、Java、perl、python、tcl、ODBC など、多くのコネクタがあります。PostgreSQL 手続き型言語内で PostgreSQL データベースへの個別の接続を作成するためのコネクタもあります。これにより、次のようなことが可能になる場合があります。あなたが欲しいもの:

http://www.postgresql.org/docs/current/static/dblink.html

これがさまざまな「クライアント」フレームワークで行われるのを見てきました。アプリケーションが論理的にデータベースを処理しているすべての場所にこれを広めるのは明らかに悪い考えですが、すべてのデータベース要求を 1 つの「アクセサー」メソッド (または少なくとも非常に少数のメソッド) を介してルーティングする正当な理由がたくさんあります。 )、ほとんどのフレームワークは、そのレイヤーでこれを処理する方法を提供します。(たとえば、Spring では、依存性注入を使用してトランザクション マネージャーを作成する必要があります。) これはおそらく、アプリケーション ロジックに使用している言語に属しますが、本当に必要な場合は、おそらく plpgsql と dblink を使用できます。ただし、それはおそらく最も簡単な方法ではないでしょう。

于 2015-04-06T21:23:32.007 に答える