2

私は、データベースから読み取るSpringトランザクションを持っています。探しているものが存在しない場合は、それを作成します。例:この方法は、二回同時に呼び出されると2つの同じ読み込み+書き込み同時トランザクションをお互いに待たせることができますか?

@Transactional 
public int getUserIdCreateIfNotExistent(String email) throws Exception { 
    try { 
    return jdbcTemplate.queryForObject("SELECT id FROM users WHERE email = ?", Integer.class, email); 
    } catch (IncorrectResultSizeDataAccessException e) { 
    Thread.sleep(10000); 
    return jdbcTemplate.queryForObject("INSERT INTO users (email) values(?) RETURNING id", Integer.class, email); 
    } 
} 

それはDBに同じメールを持つ2人の別個のユーザを有するもたらすであろう(一意の電子メールには制約がなく、これは単に例示の目的のためのものです)。

最初のトランザクションを待ってから、1人のユーザーしか作成されないようにします。私はトランザクションの隔離レベルをIsolation.SERIALIZABLEに変更しようとしましたが、最後にロールバックするトランザクションが行われます。

EDIT: 誰もが​​またはそのような何かを使用してJavaでのソリューションをロック示唆する前に、この方法は、Webアプリケーションの一部であり、特定のエンドポイントがヒットした時に呼び出されることを理解することが重要です。 Webアプリケーションは複数の異なるマシン上で実行され、負荷分散されており、エンドポイントが同時に2回呼び出されると問題が発生します。これは、というコードが、2台の異なるマシン上で並列に実行され、異なるマシン上の同じDBと対話することを意味します。

+0

分離レベルは助けにはなりません。ユニークな制約に遭遇しない限り –

答えて

0

:あなたは新しい行を挿入しているので、

@Transactional 
public int getUserIdCreateIfNotExistent(String email) throws Exception { 
    try { 
    jdbcTemplate.queryForRowSet("SELECT pg_advisory_xact_lock(hashtext('users'), hashtext(?))", email); 
    return jdbcTemplate.queryForObject("SELECT id FROM users WHERE email = ?", Integer.class, email); 
    } catch (IncorrectResultSizeDataAccessException e) { 
    Thread.sleep(10000); 
    return jdbcTemplate.queryForObject("INSERT INTO users (email) values(?) RETURNING id", Integer.class, email); 
    } 
} 
0

「ロック」テーブルを使用できます。これには、ロックする必要があるすべてのもの、つまりテーブル名(例:ユーザー)と行キー(例:「メール」)が含まれます。

より速いものが必要な場合は、サーバー間でmemcachedを複製することができます。あなたの挿入物が多くない場合、dbソリューションはおそらく大丈夫でしょう。勧告的ロックで解決

関連する問題