0

データ ストア エントリの作成時にクライアントの再試行を処理するための戦略を考え出す必要があります。

  • クライアントは、データベースに新しいエントリを作成する要求を送信します
  • サーバーはエントリの作成を実行し、成功応答を準備します
  • リクエストが処理されなかったとクライアントに信じ込ませる何らかのエラーが発生します (パケット損失など)。
  • クライアントは、データベースに新しいエントリを作成するために同じ要求を再度送信します
  • サーバーは再試行を検出し、別のデータストア エントリを作成せずに元の応答を再作成して送信します
  • クライアントが返信を受け取る
  • 誰もが満足し、データベースには 1 つのエントリしか作成されませんでした

制限が 1 つあります。サーバーはステートレスです。クライアントにセッション情報はありません。

私の現在の考えは次のとおりです。

  • すべての create-request に、保証されたグローバルに一意の ID をタグ付けします (質問にはあまり関係ありませんが、作成方法は次のとおりです)。
    • データ ストア (および memcache) を使用して、サーバー インスタンスが読み込まれると、単調に増加する一意の ID をすべてのサーバー インスタンスに割り当てます (これをSIと呼びましょう) 。
    • クライアントが開始ページを要求すると、要求を処理したインスタンスは、単調に増加する一意のページ ロード ID ( PL ) を生成し、 SI.PLをページ コンテンツと共にクライアントに送信します。
    • create-request ごとに、クライアントは単調に増加する一意の request-id ( RI ) を生成し、create-request とともにSI.PL.RIを送信します。
  • create-request ごとに、サーバーは最初に create-tag を認識しているかどうかを確認します。
  • そうでない場合は、新しいエントリを作成し、何らかの形で create-tag を一緒に保存します。
  • タグがわかっている場合は、それを使用して最初に作成されたエントリを見つけ、対応する応答を再作成します。

現在考えている実装オプションとその問題点は次のとおりです。

  1. create-tag をエントリ内のインデックス付きプロパティとして保存します。
    • サーバーがリクエストを受け取ると、クエリを使用して既存のエントリを見つける必要があります
    • 問題: AppEngine のクエリは結果整合性しかないため、エントリを見逃す可能性があります
  2. create-tag をエントリのキーとして使用します。
    • 数値がラップされない場合は一意であることが保証されているため、問題ありません (long の場合はそうではありません)。
    • マイナーな不便: 将来の使用でエントリのキーの長さが増加します (不要なオーバーヘッド)。
    • 重大な問題: これにより、データストアに連続したエントリ キーが生成されます。これは、格納されたデータにホット スポットが作成され、パフォーマンスに大きな影響を与える可能性があるため、絶対に回避する必要があります。

オプション 2 について私が考えている解決策の 1 つは、ホットスポットを排除する代わりに、連番を取得し、それらを一意で決定論的だがランダムに見えるシーケンスに再マッピングする何らかの式を使用することです。そのような式がどのように見えるかについてのアイデアはありますか?

それとも、全体的により良いアプローチがありますか?

4

2 に答える 2

1

新しいエンティティにキーをどのように割り当てますか?

自分でキーを作成すると、問題は解決します。繰り返しエンティティは、同じキーを持つため、単に既存のエンティティを上書きします。例として、製品の SKU を使用してキーを生成する製品エンティティの作成があります。

キーがデータストアによって割り当てられている場合、リクエストがタイムアウトしたときに、エラー メッセージをユーザーに表示し、データをクライアントにリロードします。次に、ユーザーは、エンティティが既に作成されているかどうかを確認します。

「ランダムに見えるシーケンス」ほど派手ではありませんが、よりシンプルで信頼性が高くなります:)

于 2014-09-22T19:38:52.897 に答える
0

上記の実装オプション (1)を機能させることは可能ですが、巧妙に設計されたデータ構造、適切なトランザクション、およびガベージ コレクションを使用することで、確実に機能させるのは非常に困難です。

したがって、正しい解決策は、代わりにオプション (2)の一般化されたバージョンを使用することです。

作成されたエンティティの一意の識別子をエンティティのキ​​ーとして使用します。

そうすれば、再試行で同じエンティティが再度作成された場合、既存のコピーを確実に見つけるのは簡単です (gets強い一貫性があるため)。または、やみくもに再度書き込むと、最初のバージョンが単純に上書きされます。

@Gregはコメントで、エンティティを一意に識別するデータに対してハッシュをキーとして使用することを提案しました。これにより、パラメーター空間全体に均等に分散されたキーを持つ問題が解決され、物理ストレージの場所全体にデータが効率的に分散されるようになりますが、特にハッシュ衝突を管理 (または無視) する必要があるという新しい問題が生じます。キーが非常に長くならないようにしようとする場合。

これらの衝突を処理する方法があります。例: 競合が発生した場合、実際のコンテンツを比較して本当に重複しているかどうかを確認し、そうでない場合はキーに「1」を追加します。次に、そのキーも存在するかどうかを確認し、存在する場合は、同じ内容かどうかを再度確認します。そうでない場合は、代わりに「2」を追加し、衝突を再度確認するなど...これは機能しますが、かなり面倒です。

または、ハッシュの衝突は非常にまれであるため、データベースに十分なユーザー データが存在しないため、衝突が発生することは決してない、と言うことができます。個人的には、この種の「指を交差させない」アプローチは好きではありませんが、多くの場合、それは受け入れられる方法かもしれません。

しかし幸いなことに、衝突のないデータのグローバルに一意の識別子である create-tag が既にあります。そして、私がそれを使用して見た2つの問題は、巧妙なビットシャッフルによって両方とも簡単に修正できることがわかりました:

元の質問と同じ識別子を使用して、私の作成タグSI.PL.RIは、永遠に増加し続けるSI 、新しいサーバー インスタンスが作成されるたびに 0 にリセットされるPL、および毎回リセットされるRIで構成されます。新しいクライアント セッション。したがって、RIは常に小さい可能性が高く、PLはやや小さいままで、SIはゆっくりと大きくなります。

それを考えると、たとえば、次のようにキーを作成できます (最上位ビットから始めます)。

- Lowest 10 bits of PL
- Lowest  4 bits of RI
- Lowest 17 bits of SI
- 1 bit indicating whether there are any further non-zero values
- Next lowest 10 bits of PL
- Next lowest  4 bits of RI
- Next lowest 17 bits of SI
- 1 bit indicating whether there are any further non-zero values
- ... until ALL bits of RI, PL, and SI are used (eventually breaking 10-4-17 pattern)

このように、生成されたキーは (AppEngine のように) 字句順にソートされている場合、パラメータ スペース全体に適切に分散されます。また、最初のキーは自動生成されたキーの半分の長さであり、必要に応じて長くなります。

余談1:

実際、1,000 を超えるページ読み込みを処理するのに十分な時間サーバー インスタンスが存在せず、1 つのクライアントが 1 つのセッションで 16 を超える新しいエンティティを作成せず、サーバー インスタンスが 5 分ごとに 1 つより速く生成されない場合、平均して、キーが平均 4 バイトを超えるまでには 1 年以上かかります。

また、 100 万を超えるページ読み込みを処理できるほど長く存続しているサーバー インスタンスがなく、1 つのクライアントが 1 つのセッションで 256 を超える新しいエンティティを作成せず、サーバー インスタンスが平均して毎秒1 つより速く生成されない場合、キーが平均で 8 バイトを超える (したがって、自動生成されたものよりも長くなる) までには、さらに 500 年以上かかります。うまくいくはずです... :)

余談 2:

代わりにキーを使用して Java HashMaphashCode()のインデックスを作成する必要がある場合、キーオブジェクトの関数は、代わりに最初の 4 つのキー バイトから構築された整数を逆の順序で返し、キーをバケット全体に分散させることができます。

于 2014-09-23T17:42:23.003 に答える