質問

私はまだ、CQRSスタイルのアーキテクチャに関連する基本的な(そして解決された)問題でなければならないものに苦労しています。

依存するビジネスルールをどのように実装しますか セット 総根の?

例として、予約アプリケーションを取り上げます。コンサートのチケット、映画の席、レストランでのテーブルのチケットを予約することができます。すべての場合において、 限られた数 販売のための「アイテム」の。

イベントや場所が非常に人気があると想像しましょう。新しいイベントまたはタイムスロットの販売が開かれている場合、予約は非常に速く到着し始めます - おそらく1秒あたり多くの到着。

クエリ側では、大規模にスケーリングでき、予約がキューに置かれ、自律コンポーネントによって非同期に処理されます。最初は、キューから予約コマンドを削除すると、それらを受け入れますが、特定の時間に開始する必要があります 残りを拒否します.

限界に達するとき、どのようにして知ることができますか?

予約コマンドごとに、リクエストに対応できるかどうかを把握するために、何らかのストアを照会する必要があります。これは、当時すでに受け取った予約の数を知る必要があることを意味します。

ただし、ドメインストアがEG Windows Azure Tableストレージなどの非関連データストアである場合、非常にうまくできません SELECT COUNT(*) FROM ...

1つのオプションは、aを保持することです 個別の集合ルート これは、このように現在のカウントを追跡するだけです。

  • AR:予約(WHO?何人?)
  • AR:イベント/タイムスロット/日付(集約カウント)

2番目の集計ルートは、最初のルートの非正規化集約ですが、基礎となるデータストアがトランザクションをサポートしていない場合、これらは大量のシナリオで同期しなくなる可能性が非常に高い(これは私たちがしようとしているものですそもそもアドレス)。

考えられる解決策の1つは、次のことです シリアライズ 一度に1つだけが処理されるように予約コマンドを処理することは、スケーラビリティ(および冗長性)の目標に反します。

このようなシナリオは、標準的な「在庫外」シナリオを思い出させますが、違いは、予約をバックオーダーに非常にうまく配置できないことです。イベントが完売したら、完売したので、補償的な行動がどうなるかわかりません。

そのようなシナリオをどのように処理しますか?

役に立ちましたか?

解決

しばらくの間、これについて考えた後、根本的な問題はCQRとはより少ないCQRSにはあまり関係がないことがわかりました。 非追跡 異なる休憩サービスの性質。

本当にこの問題に要約します。いくつかのリソースを更新する必要がある場合、2番目の書き込み操作が失敗した場合、どのように一貫性を確保しますか?

リソースAとリソースBの更新を順番に書きたいと思っています。

  1. リソースAが正常に更新されます
  2. リソースBを更新しようとする試みは失敗します

最初の書き込み操作は、例外に直面して簡単に巻き戻すことができないので、何ができますか?リソースAに対して補償アクションを実行するために例外をキャッチして抑制することは、実行可能なオプションではありません。まず、実装するのは複雑ですが、第二には安全ではありません。ネットワーク接続が失敗したために最初の例外が起こった場合はどうなりますか?そのシナリオでは、リソースAに対して補償アクションを書くこともできません。

キーは明示的にあります iDempotency. 。 Windows Azureキューは保証されませんが 一度だけ セマンティクス、彼らは保証します 少なくとも一度は セマンティクス。これは、断続的な例外に直面して、メッセージは後で 再生されました.

前のシナリオでは、これが起こることです:

  1. リソースAが更新されます。ただし、リプレイが検出されるため、Aの状態は影響を受けません。ただし、「書き込み」操作は成功します。
  2. リソースBが正常に更新されます。

すべての書き込み操作がidempotentである場合、 最終的な一貫性 で達成できます メッセージリプレイ.

他のヒント

興味深い質問とこれにより、CQRSの痛みのポイントの1つを釘付けにしています。

Amazonがこれを処理する方法は、要求されたアイテムが完売した場合、ビジネスシナリオにエラー状態に対処することです。エラー状態は、単に顧客に現在在庫がない要求されているアイテムと送料の推定日をメールで通知することです。

ただし、これはあなたの質問に完全には答えません。

チケットを販売するシナリオを考えて、彼らが与えたリクエストが 予約要求. 。予約リクエストができるだけ早く処理され、後でメールで最終回答を復活させることになります。これを警告することにより、一部の顧客は、リクエストを拒否して電子メールを受け取るかもしれません。

今。この再ジェストをより痛みを軽減することはできますか?そうです。分散キャッシュのキーを在庫のアイテムの割合または量で挿入し、アイテムが販売されたときにこのカウンターを減少させることにより。これにより、予約リクエストが指定される前にユーザーに警告することができます。最初のアイテム数の10%しか残っていない場合、顧客が問題のアイテムを取得できない場合があるとします。カウンターがゼロの場合、これ以上予約要求を受け入れることを拒否します。

私のポイントは:

1)ユーザーに、それが彼らが行っている要求であり、これが拒否される可能性があることを知らせてください2)問題のアイテムを取得するための成功の可能性が低いことをユーザーに通知します

あなたの質問に対する正確な答えではありませんが、これはCQRSを扱うときにこのようなシナリオを処理する方法です。

ETAGは、トランザクションロックの代わりに使用できる楽観的な並行性を可能にし、ドキュメントを更新し、潜在的な人種条件を安全に処理できます。こちらの発言を参照してください http://msdn.microsoft.com/en-us/library/dd179427.aspx 詳細については。

ストーリーは次のようになるかもしれません。ユーザーAは、最大チケットが2のイベントEを作成します。ETAGは123です。ユーザーBは予約リクエストを作成しますB.ユーザーCは予約リクエストを作成しますC.ユーザーDは予約リクエストを作成しますD.

System Sは予約リクエストBを受信し、ETAG 123でイベントを読み取り、イベントを1つの残りのチケットを持つように変更します。Sは、元のETAGに一致するETAG 123を含むアップデートを提出します。 ETAGは現在456です。予約リクエストが承認され、ユーザーが成功しました。

別のシステムS2は、システムSがリクエストbを処理していたのと同時に予約要求Cを受信します。そのため、ETAG 123が残りの1つのチケットに変更してイベントを読み取り、ドキュメントを更新しようとします。ただし、今回はETAG 123が一致しないため、更新は例外を除いて失敗します。 System S2は、現在ETAG 456とカウントが1のドキュメントを再読して操作を再試行しようとするため、これを0に減らし、ETAG 456で再提出します。

残念ながらユーザーCユーザーにとって、システムはユーザーBの直後にユーザーDのリクエストを処理し始め、ETAG 456でドキュメントを読み取りましたが、システムSがシステムS2よりも高速であるため、システムS2の前にETAG 456でイベントを更新することができました。彼のチケットを正常に予約しました。 ETAGは現在789です

したがって、システムS2は再び失敗し、もう1回試みますが、今回はETAG 789でイベントを読むと、チケットが利用できないことがわかり、ユーザーCの予約リクエストを拒否します。

予約リクエストが成功したかどうかをユーザーに通知する方法はあなた次第です。数秒ごとにサーバーを投票し、予約ステータスが更新されるのを待つことができます。

ビジネスの観点を見てみましょう(私は同様のことを扱っています - 無料のスロットで予約を予約)...

あなたの分析の最初のことは、私がオフになっていると私を襲っていることは、予約可能なチケット/シート/テーブルの概念がないということです。これらは予約されているリソースです。

トランザクションである場合、何らかの形の一意性を使用して、同じチケット/シート/テーブルで二重予約が発生しないことを確認できます(詳細については、 http://seabites.wordpress.com/2010/11/11/consistent-indexes-constraints)。このシナリオには、同期(ただしまだ同時)コマンド処理が必要です。

トランザクションでない場合は、イベントストリームを遡及的に監視し、コマンドを補正できます。エンドユーザーに、システムが確実に知っているまで、つまりイベントストリーム分析の後に、コマンドが完了し、補償されていないか、補償されていないことを知っているまで、予約の確認を待つ経験を与えることさえできます(予約は作成されましたか?はい、もしくは、いいえ?")。言い換えれば、補償は確認サイクルの一部になる可能性があります。

もう少し後退しましょう...

請求が関与している場合(オンラインチケット販売など)、このシナリオ全体がとにかくサガに進化すると思います(予備チケット +請求券)。請求がなくても、体験を信頼できるものにするために、サガ(予備の表 +確認予約)があります。したがって、チケット/テーブル/シートの予約の1つの側面だけでズームインしている場合でも(つまり、まだ利用可能です)、「長期間」のトランザクションは私がそれを支払うか、それを確認するまで完了しません。とにかく補償が行われ、どちらの理由でトランザクションを中止するときにチケットを再び解放します。興味深い部分は、ビジネスがこれに対処したい方法になります。たぶん、他の顧客が彼/彼女に同じチケットを与えた場合、取引を完了したかもしれません。この場合、チケット/シート/テーブルを二重に予約すると、払い戻しがより興味深いものになる可能性があります。ご不便をおかけするために、次/同様のイベントの割引を提供します。答えは、技術モデルではなく、ビジネスモデルにあります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top