これはソフトウェアエンジニアリングスタック交換に関する私の最初の投稿なので、何か問題がある場合はお知らせください。
私は、Amazonのサーバーレス製品を調べて、それが私が考えているいくつかの新しいプロジェクトに行く方法であるかどうかを調べようとしています。イベントソースのCQRSモデルに特に興味があります。この場合、そのようなモデルの主張されている利点が非常に魅力的であると思うからです。しかし、Amazonが提供するすべてのサービス、その長所と短所、およびそれらすべてがどのように組み合わさるかを理解するのに少し問題があります。最初にいくつかの口実を付け、後で質問を述べます。
サンプルアプリケーションを使用して、私が求めていることを説明します。
これはシンプルな(静的)Webアプリケーションであり、S3でホストされ、cloudflareを介して提供されます。
これには2つのアクションがあります。1つのコマンドと1つのクエリ(CQRS用語)。
このコマンドは、イベントをイベントストリームにポストして、カウンターをインクリメントします。
クエリは、カウンターの現在の状態、つまり何回インクリメントされたかを取得します。
それだけです。サーバーレスAWSテクノロジーを使用してこれを実装するにはどうすればよいですか?これが私がこれまでに考えていることです:
カウンターをインクリメントするコマンドを送信するために、WebアプリケーションはAJAXリクエストを(APIゲートウェイ経由で)ラムダL1に送信します。このラムダL1はイベントをイベントストリームにポストします。
別のラムダL2はイベントストリームをリッスンし、必要に応じて後日再生できるようにイベント/コマンドのレコードを保存します。
さらに別のラムダL3がイベントストリームをリッスンし、コマンドを実行します。つまり、カウンタの現在の状態をフェッチし、インクリメントして、新しい状態をアトミックに保持します。
クエリを送信するために、WebアプリケーションはAJAXリクエストを(APIゲートウェイ経由で)ラムダL4に送信します。これは、状態をクエリして結果を返します。
これは、かなり単純な最小限のプロジェクトである必要があるようです。これまでの私の懸念は次のとおりです。
まず、イベントストリームはどのように見えるべきですか?私は多くの提案が浮かんでいるのを見てきました。それぞれの提案は、前回よりも複雑で、人為的なものです。さまざまなファンアウト戦略、SNS、SQS、Kinesis、DynamoDBストリームの混合、名前を付けて...可動部品が多すぎて、複雑であるという意味で拡張が困難なコスト効率の悪いシステムになると思います開発が難しい。
第二に、原子性を達成できますか?上記のイベントストリームサービスには、通常、「少なくとも1回の配信」プロパティがあり、コンシューマが処理する必要があります。私が見た1つの提案は、すべてのイベントをべき等にすることですが、これは私のサンプルアプリケーションでは実現可能ではないようです。 2つのクライアントが同時にカウンターをインクリメントする可能性があり、両方のコマンドが「カウンターは現在(たとえば)17です」と言うため、インクリメントの1つが「失われる」可能性があります。これは正しい動作であると主張できます。どちらのクライアントも数値を16とみなし、17に増やしたいと考えていましたが、この状況で両方の増分を合計にカウントしたいとします。 2つの状態の間のデルタのみを表すコマンドが必要です。これを達成する方法はありますか?
第3に、ラムダL3とL4の両方が、ある種の永続化レイヤーにアクセスできる必要があります。理想的には、これをリレーショナルデータベース(SQL)にして、現在のアプリケーションの状態に対して高度なクエリを実行できるようにします。これは、増加するカウンターの例では必要ありませんが、私が考えているプロジェクトでは必要になります。サーバーレスを維持したい場合は、サーバーレスAuroraの1つのオプションしか残せないと思います。私はそれで問題ありませんが、AuroraをVPCで実行する必要があること、およびAuroraにアクセスするにはラムダを同じVPCで実行する必要があることを理解しています。私の例ではL3が単一の輻輳ポイントであるため、ここではパフォーマンスについて非常に心配しています(他のすべては追加専用または読み取り専用です)。私の理解では、VPCはかなり高額なパフォーマンスコスト(スループット、接続数、帯域幅)を被り、VPCのラムダは10秒以上のコールドスタートになることがあります。これらの問題にどのように取り組むことができますか?私の頭の中で警報ベルが鳴っています。これは、解決する以上の問題を引き起こすだけです。コールドスタートしないように、L4に継続的にpingを実行する必要があります(10秒のロード時間は許容できません)。その時点で、本当にサーバーレスですか?これが悪いアイデアである場合、より良い代替案はありますか? DynamoDBでも状態を保持する必要がありますか?クエリ機能が失われますか?
この投稿はすでにかなり長くなっているので、ここではこれら3つの懸念事項について説明します。私の質問に直接回答する以外に、誤解を解消したり、別の解決策を提供したりできると助かります。
かなりの数の記事がありますが、これに対する権威ある答えを見つけることはないと思います。
(最初のパスでは、とにかく)ソリューションの変更可能な状態に複数のライターが含まれる可能性があることを予想する必要があります。したがって、すべての変更可能な書き込みは、ある種の述語/バリデーターを使用して条件付きPUTをサポートすることを期待する必要があります。
DynamoDBとS3はどちらも条件付きプットをサポートしているため、これらはオプションですが、ストレージ戦略を検討し、それらの上に適切なセマンティクスを実装する必要があるという意味で、必ずしも自由であるとは限りません
Re:Invent 2017でのイベントソーシングのブレイクアウト中、DynamoDBがディスカッションでの主な永続性の選択でした。いくつかの議論の後、結論は、複数の書き込みシナリオではバッチ書き込みは機能しないということでした-各条件付き書き込みは単一の行を挿入します。
また、必要な信頼性をどのように保証するかについても考慮する必要があります。イベントを保存する前にブロードキャストしても安全ですか?永続的な変更が確認される前に、L1は成功を報告する必要がありますか?
MVPでの私の推測としては、L1エンドポイントでDynamoの読み取りと書き込みを行い、その後ろに他のべき等コンシューマーをつなげることです(つまり、dynamoからイベントを読み取り、SNSやKinesisなどに書き込むラムダ)。 。
保証によっては、L1-> SNS-> L2-> Dynamo->などを使用して、ライターを多少簡略化できる場合があります。
変更可能な書き込みで述語を使用する必要があると言ったとき、「状態が16から17に更新されました」などのイベントを書き込んで、更新前に状態が現在16であることをL2に検証させるべきですか?
はい-「比較してスワップ」、 If-Match 、 JSONパッチテスト操作 などと考えてください。複数のライターが不変条件を適用する必要がある場合は、上書きされる状態が正しいものであることを確認する方法が必要です。