5

Spring (3.1)/Postgresql (8.4.11) のトランザクションの問題で誰かが私を助けてくれるかもしれません

私の取引サービスは次のとおりです。

@Transactional(isolation = Isolation.SERIALIZABLE, readOnly = false)
@Override
public Foo insertObject(Bar bar) {

            // these methods are just examples
            int x = firstDao.getMaxNumberOfAllowedObjects(bar)
            int y = secondDao.getNumerOfExistingObjects(bar)
            // comparison
            if (x - y > 0){
                  secondDao.insertNewObject(...) 
            }
            ....
}

Spring 構成 Webapp には以下が含まれます。

@Configuration 
@EnableTransactionManagement 
public class ....{
    @Bean
    public DataSource dataSource() {
        org.apache.tomcat.jdbc.pool.DataSource ds = new DataSource();

        ....configuration details

        return ds;
    }

    @Bean
    public DataSourceTransactionManager txManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

リクエスト "x" とリクエスト "y" が同時に実行され、両方ともコメント "comparison" (メソッド insertObject) に到達したとします。次に、両方が新しいオブジェクトを挿入することを許可され、トランザクションがコミットされます。

RollbackException が発生しないのはなぜですか? 私の知る限り、これが Serializable isolotation レベルの目的です。前のシナリオに戻ると、x が新しいオブジェクトの挿入に成功し、そのトランザクションをコミットした場合、"y" のトランザクションは、彼が読み取っていない新しいオブジェクトがあるため、コミットを許可されるべきではありません。

つまり、"y" が secondDao.getNumerOfExistingObjects(bar) の値を再度読み取ることができれば、さらに新しいオブジェクトがあることがわかります。ファントム?

トランザクション構成は正常に機能しているようです:

  • リクエストごとに、firstDao と secondDao の同じ接続を確認できます
  • insertObject が呼び出されるたびにトランザクションが作成される

最初と 2 番目の DAO は次のとおりです。

@Autowired
public void setDataSource(DataSource dataSource) {
    this.jdbcTemplate = new JdbcTemplate(dataSource);
}

@Override
public Object daoMethod(Object param) {

        //uses jdbcTemplate

}

私は何かが欠けていると確信しています。何か案が?

御時間ありがとうございます、

ハビエル

4

1 に答える 1

8

TL;DR: Pg 9.1 でシリアライズ可能性の競合の検出が劇的に改善されたので、アップグレードしてください。


あなたの説明から、実際の SQL が何であり、なぜロールバックを期待するのかを理解するのは難しいです。おそらく、すべての述語を完全にテストすると考えていますが、特に Pg 8.4 ではそうではありません。

SERIALIZABLEトランザクションが連続して実行されたかのように実行されることを完全に保証するわけではありません。限定的なチェックのみを提供します。正確に何がチェックされ、どのようにチェックされるかは、データベースやバージョンによって異なるため、データベースのバージョンのドキュメントを読む必要があります。

SERIALIZABLEモードで実行されている 2 つのトランザクションが、それらのトランザクションが実際に連続して実行された場合とは異なる結果を生成する異常が発生する可能性があります。

詳細については、Pg のトランザクション分離に関するドキュメントを参照してください。Pg 9.1 で動作が大幅に変更されたことに注意してください。そのSERIALIZABLEため、Pg バージョンに適したバージョンのマニュアルを必ずお読みください。これが 8.4 バージョンです。特に13.2.2.1 を読んでください。シリアライズ可能な分離と真のシリアライズ可能性これを、Pg 9.1 docs で説明されている、大幅に改善された述語ロック ベースのシリアル化サポートと比較してください。

次の疑似コードのようなロジックを実行しようとしているようです。

count = query("SELECT count(*) FROM the_table");
if (count < threshold):
    query("INSERT INTO the_table (...) VALUES (...)");

その場合、Pg 8.4 で同時に実行すると機能しません。上記のリンク先のドキュメントで使用されている異常の例とほとんど同じです。驚くべきことに、実際には Pg 9.1 で動作します。9.1 の述語ロックでさえ集約の使用をキャッチするとは思っていませんでした。

あなたはそれを書きます:

前のシナリオに戻ると、x が新しいオブジェクトの挿入に成功し、そのトランザクションをコミットした場合、"y" のトランザクションは、彼が読み取っていない新しいオブジェクトがあるため、コミットを許可されるべきではありません。

psqlしかし、8.4 は 2 つのトランザクションが相互に依存していることを検出しません。これは、2 つのセッションを使用してテストすることで簡単に証明できます。これが機能するのは、9.1 で導入された true-serializability のものだけです。率直に言って、9.1 で機能することに驚きました。

ページ 8.4 で最大行数を強制するようなことをしたい場合は、手動またはトリガー関数を介してロックを行い、同時実行を防ぐためLOCKにテーブルが必要です。トリガーでそれを行うと、本質的にロックの昇格が必要になるため、頻繁にデッドロックが発生しますが、ジョブは正常に実行されます。テーブルから偶数 ing を取得する前に を発行できるアプリケーションで実行することをお勧めします。そのため、テーブルで必要な最高のロック モードが既に設定されているため、デッドロックが発生しやすいロック プロモーションは必要ありません。lock モードは、sのみを許可するため、適切です。INSERTLOCK TABLE my_table IN EXCLUSIVE MODESELECTEXCLUSIVESELECT

2 つの psql セッションでテストする方法は次のとおりです。

SESSION 1                               SESSION 2

create table ser_test( x text );

BEGIN TRANSACTION 
ISOLATION LEVEL SERIALIZABLE;


                                        BEGIN TRANSACTION 
                                        ISOLATION LEVEL SERIALIZABLE;

SELECT count(*) FROM ser_test ;

                                        SELECT count(*) FROM ser_test ;

INSERT INTO ser_test(x) VALUES ('bob');


                                        INSERT INTO ser_test(x) VALUES ('bob');

 COMMIT;

                                        COMMIT;

Pg 9.1 で実行すると、st commits succeeds then the secondCOMMIT` は次のエラーで失敗します。

regress=# COMMIT;
ERROR:  could not serialize access due to read/write dependencies among transactions
DETAIL:  Reason code: Canceled on identification as a pivot, during commit attempt.
HINT:  The transaction might succeed if retried.

しかし、8.4 で実行すると、両方のコミットのコミットが成功します。これは、8.4 には、9.1 で追加されたシリアル化可能性のためのすべての述語ロック コードが含まれていなかったためです。

于 2012-10-11T01:47:58.377 に答える