分散型のイベントソースシステムで一貫性を維持するためのパターンですか?


12

私は最近イベントの調達について読んでおり、その背後にあるアイデアが本当に好きですが、次の問題にこだわっています。

コマンド(Webサーバーなど)を受信し、結果としてイベントを生成し、それらを中央ストアに格納するN個の並行プロセスがあるとします。また、ストアからイベントを順番に適用することにより、すべての一時的なアプリケーションの状態が個々のプロセスのメモリで維持されると仮定します。

ここで、次のビジネスルールがあるとしましょう。各個別のユーザーには一意のユーザー名が必要です。

2つのプロセスが同じユーザー名Xのユーザー登録コマンドを受信した場合、両方がXがユーザー名のリストにないことを確認し、ルールは両方のプロセスを検証し、両方とも「ユーザーXの新しいユーザー」イベントをストアに保存します。

現在、ビジネスルールに違反しているため、一貫性のないグローバル状態に入りました(同じユーザー名を持つ2人の異なるユーザーがいます)。

従来のNサーバー<-> 1 RDBMSスタイルのシステムでは、データベースは、このような不整合の防止に役立つ同期の中心点として使用されます。

私の質問は、イベントソースシステムは通常、この問題にどのように対処するのかということです。すべてのコマンドを順番に処理するだけですか(たとえば、ストアに書き込むことができるプロセスの量を1に制限するなど)。


1
そのような制限はコードで制御されていますか、それともdb制約ですか?N個のイベントは、順番にディスパッチ処理される場合とされない場合があります... N個のイベントは、相互に区別することなく、同時に検証を行う場合があります。順序が重要な場合は、検証を同期する必要があります。または、キューを使用してイベントをキューに入れるには、順次ディスパッチを実行します
-Laiv

@ライヴ右。簡単にするために、データベースがなく、すべての状態がメモリに保持されていると仮定しました。特定のタイプのコマンドを順番にキューで処理するのはオプションですが、どのコマンドが他のコマンドに影響を与えるかを判断するのは複雑で、すべてのコマンドを同じキューに入れてコマンドを処理することになりそうです:/たとえば、ユーザーがブログ投稿にコメントを追加している場合、「ユーザーの削除」、「ユーザーの一時停止」、「ブログ投稿の削除」、「ブログ投稿コメントの無効化」などはすべて同じキューに入れます。
オリビエラロンド

1
キューまたはセマフォを操作することは簡単ではありません。並行性またはイベントソースのパターンで動作しません。しかし、基本的にすべてのソリューションは、イベントのトラフィックを調整するシステムになります。しかし、それは興味深いパラダイムです。エンティティの最終状態のキャッシュや、そのようなエンティティが現在処理されている場合など、ノード間のこのトラフィックを管理するのに役立つRedisなどのタプル向けの外部キャッシュもあります。この種の開発では、共有キャッシュが非常に一般的です。それは複雑に見えるかもしれませんが、ギブアップしないでください;-)、それは非常に興味深いです
LAIV

回答:


6

従来のNサーバー<-> 1 RDBMSスタイルのシステムでは、データベースは、このような不整合の防止に役立つ同期の中心点として使用されます。

イベントソースシステムでは、「イベントストア」が同じ役割を果たします。イベントソースオブジェクトの場合、書き込みはイベントストリームの特定のバージョンへの新しいイベントの追加です。したがって、並行プログラミングと同様に、コマンドを処理するときにその履歴のロックを取得できます。イベントソースシステムがより楽観的なアプローチを取るのがより一般的です-以前の履歴をロードし、新しい履歴を計算してから、比較と交換を行います。他のコマンドもそのストリームに書き込んだ場合、比較とスワップは失敗します。そこから、コマンドを再実行するか、コマンドを破棄するか、結果を履歴にマージします。

N個のサーバーとM個のコマンドがすべて単一のストリームに書き込もうとすると、競合が大きな問題になります。ここでの通常の答えは、モデル内の各イベントソースエンティティに履歴を割り当てることです。したがって、User(Bob)はUser(Alice)とは異なる履歴を持ち、一方への書き込みは他方への書き込みをブロックしません。

私の質問は、イベントソースシステムは通常、この問題にどのように対処するのかということです。すべてのコマンドを順番に処理するだけですか?

集合検証に関するグレッグヤング

ビジネスロジックをサービスレイヤーに移動せずに、ドメインオブジェクト属性の一意の制約をチェックするエレガントな方法はありますか?

多くの場合、その要件を調査する短い答えは、(a)他の要件に対する十分に理解されていないプロキシであるか、または(b)検出できれば「ルール」の違反は許容できることを明らかにします(例外レポート) 、一定の時間枠内で緩和される、または低頻度です(たとえば、クライアントは、使用するコマンドをディスパッチする前に名前が使用可能かどうかを確認できます)。

イベントストアがセット検証(つまり、リレーショナルデータベース)に優れている場合、イベントを永続化する同じトランザクション内の「一意の名前」テーブルに書き込むことで要件を実装します。

場合によっては、すべてのユーザー名を同じストリームに公開することでのみ要件を適用できます(ドメインモデルの一部として、メモリ内の名前のセットを評価できます)。-この場合、2つのプロセスが "the"ストリーム履歴の更新を試みますが、比較とスワップ操作の1つが失敗し、そのコマンドの再試行が競合を検出できます。


1)提案と参考文献をありがとう。「比較と交換」と言うとき、プロセスは、イベントの保存時に、コマンドの処理を開始してから新しいイベントが到着したことを検出しますか?これには、「比較と交換」のセマンティクスをサポートするイベントストアが必要だと思いますか?(たとえば、「このイベントのみを書き込み、最後のイベントのIDがXである場合のみ」)?
オリビエラロンド

2)一時的な矛盾を受け入れて最終的に修復するというアイデアも好きですが、それを信頼できる方法でコーディングする方法がわかりません...イベントを順次検証し、検出時にロールバックイベントを作成する専用のプロセスがあるかもしれません何かがうまくいかなかった?ありがとう!
オリビエラロンド

(1)「新しいイベント」ではなく、「履歴の新しいバージョン」と言いますが、あなたは考えがあります。予想される履歴である場合にのみ、履歴を置き換えます。
VoiceOfUnreason

(2)うん。ストアからイベントをバッチで読み取り、バッチの最後に例外レポート(「ボブという名前のユーザーが多すぎます」)をブロードキャストするか、問題を補うためにコマンドをディスパッチします(正しい応答が仮定されている場合)人間の介入なしで計算可能)。
VoiceOfUnreason

2

ユーザーが。のように扱われるユーザー登録用のビジネスプロセスを(のsagaコンテキストでDomain Driven Design)実装できるように思えますCRDT

資源

  1. https://doc.akka.io/docs/akka/current/distributed-data.html http://archive.is/t0QIx

  2. 「Akka分散データを使用したCRDT」 https://www.slideshare.net/markusjura/crdts-with-akka-distributed-dataで学習

    • CmRDTs-操作ベースのCRDT
    • CvRDTs-状態ベースのCRTD
  3. Scalaではコード例 https://github.com/akka/akka-samples/tree/master/akka-sample-distributed-data-scala。たぶん「ショッピングカート」が最適です。

  4. Akka Clusterのツアー– Akka分散データ https://manuel.bernhardt.io/2018/01/03/tour-akka-cluster-akka-distributed-data/
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.