web-dev-qa-db-ja.com

アイテムごとに楽観的ロックを使用して、結果ストリームを複数のテーブルに効率的に格納します

多数のアイテムを含む結果ストリームがある場合、それらを保存して、潜在的な同時実行の競合を処理します。

_public void onTriggerEvent(/* params */) {
  Stream<Result> results = customThreadPool.submit(/*...complex parallel computation on multiple servers...*/).get();
  List<Result> conflicts = store(results);
  resolveConflictsInNewTransaction(conflicts);
}
_

store(...)を効率的に実装する方法について、私はstuckです。 Resultは、それぞれのDBテーブルで更新する必要のあるデータを記述する2つの不変で分離されたオブジェクトで構成されています。

_@Value
public static class Result {
    A a; // describes update for row in table a
    B b; // describes update for row in table b
}
_

AおよびBはそれぞれ2人のユーザーを参照します。ここで、_(u1, u2)_はそれぞれのDBテーブルのキーです。

_@Value
public static class A {
    long u1;
    long u2;
   // ... computed data fields ...
}
// B accordingly
_

ストリーム計算自体は同時にトリガーされる可能性があります(複数のonTriggerEventの並列呼び出し)がほとんど問題ありませんが、一部の結果で競合が発生する場合があります(約0.1%が競合します。たとえば、ストリームに_(53,21)_の結果と別の呼び出しも_(53,21)_を更新しました)。 ABの競合は、操作の開始と比較すると異なるupdatedAtフィールドによって示されます。もちろん、ここでは、すべての結果を破棄して再試行するのではなく、競合している行を解決したいだけです。

したがって、(1)競合していないすべての_Result.a_および_Result.b_を格納し、(2)ListResultsを取得するための適切なアプローチは何でしょうか。紛争中で特別な扱いが必要です。

_public List<Result> store(Stream<Result> results) {
  // store all a
  // store all b (ideally without using results * 2 RAM)
  // do update other stuff if a and b are not in conflict and do it in the same ACID transaction as the update of the related a and b.
  // return those in Conflict
}
_

各結果をアンパックせずにそれを実装し、独自のトランザクションなどでデータベースに送信するにはどうすればよいですか?理想的には、一度にすべてをDBに送信して、格納されていない(および他のものが保持されているはずの)競合のリストを取得する必要があります。私も別のアプローチを受け入れています。

必要に応じて、JPA/Hibernateを使用します。

6
Stuck

最も簡単なのは、永続性をFIFOキューに合理化することです(多くの技術が存在しますが、一般的には「トランザクションごとに1つのエントリ」になるため、望ましいアプローチではありません)。 。

したがって、2番目のオプションでは、同時実行競合定義のロジックをデータベースの永続化アクションから別のサービスに移動します。

UserId-to-Reentrantロックのメモリ内マップのようなものを実装できます(これらの操作は、同期されたブロックと比較して、非常に高速です)。

永続化する最初の呼び出し中に、ロックがロックされます。永続化が成功すると、ロックが解放されます。その間(別のスレッドで)ロックの状態を確認し、それによってフィルターで除外するか、ロックが解放されるまで待つことができます。待機状態には注意してください。ストリームがあるため、ストリームを処理するスレッド全体が待機状態になります。

個人的には、最初の「トランザクションごとに1つのエントリ」に固執し、途中に(永続的な)メッセージングキューがあり、ロックチェック用の個別のサービスを備えています。まず、これにより、書き込み操作の同時実行を簡単に構成できます。ロックされるエントリは1つだけなので、2番目はライターで待機状態を簡単に使用できます。

2
Ilya Dyoshin