Amazon DynamoDB と Momento Cache の組み合わせに対する考察
先日JAWS FESTA 2023 in Kyushu で Momentoのセッションに参加してきました!デベロッパーアドボケイトの小板橋さんがAWSユーザーに対してMomentoの利点を説明されていました。
そのセッションでAWS SAの方から「Amazon DynamoDB にはDAX(Dynamo DB Accelerator)という内蔵キャッシュがあるのでわざわざMomento を使うのはDouble Effortではないのか?」という質問があり、参加者からもコメントがあるなどとても良いアーキテクチャの議論がありました。私も参加したかったのですが、かなり盛況なセッションで会場も満員だったので、恥ずかしくて参加できなかったので、ここでまとめたいと思います。
DynamoDB と Momento Cache のパフォーマンス比較
永続性を持つデータストアであるDynamoDB と 一時的にインメモリでデータを保持するMomento Cacheではアーキテクチャが異なります。このため、データの書き込みはもちろん、読み込みもCacheのほうが早くなるのは当たり前です。
先日行われたもめんと会のCloudflareゲスト会でもWorkers KVとの比較を念のため行っていましたが、当然Cacheの方が早くなります。試しに以下のテストスクリプトで100回書き込みを行うとその差は8~10倍程度のパフォーマンス差が出ます。
const AWS = require("aws-sdk");
AWS.config.update({
region: "ap-northeast-1",
});
const docClient = new AWS.DynamoDB.DocumentClient();
async function insertitem(){
const start = performance.now();
for (let i = 0; i < 100; i++) {
var params_put = {
TableName: "testtable",
Item:{
firstkey: "value_"+i,
"attr": "test_1",
},
ConditionExpression: 'attribute_not_exists(firstkey)',
};
result = await docClient.put(params_put).promise();
}
const end = performance.now();
console.log(`処理時間は ${end - start} ミリ秒です。`);
}
insertitem();
同様に以下の100回読み込みも同程度のパフォーマンス差異が発生します。
const AWS = require("aws-sdk");
AWS.config.update({
region: "ap-northeast-1",
});
const docClient = new AWS.DynamoDB.DocumentClient();
async function getitem(){
const start = performance.now();
for (let i = 0; i < 100; i++) {
var params_get = {
TableName: "testtable",
Key: {
firstkey: "firstvalue_1"
}
};
var result = await docClient.get(params_get).promise();
}
const end = performance.now();
console.log(`処理時間は ${end - start} ミリ秒です。`);
return result.Item;
}
getitem();
繰り返しですがこれ自体には何の意味もなく、AWSではDynamoDBに対する高速キャッシュであるDAXというものを提供しています。
Momento CacheからみたDAX のメリットとデメリット
メリット:
DAXはDynamoDB専用キャッシュであるため、処理が透過的です。つまりDynamoDBからGetするときとDAXからGetするときの処理が同じコマンドで行えます。逆のいい方をすればDAXが自動でCacheがあればCacheで返答。CacheがなければDynamoDBから読み込みを行ってくれます。PutItemオペレーションも同様であるため、開発工数を削減することが可能です。
この方式を一般的にはインラインキャッシュと呼びます。
またMomentoのようにデータストアとは別にキャッシュが存在しておりアプリケーションでの追懐分け処理が必要なキャッシュはサイドキャッシュと呼ばれます。
デメリット:
Momento Cacheから見たElastiCacheに対する優位性と同じように、DAXはクラスター型です。
これはオーバープロビジョニングを引き起こし、コスト効率を低下させます。クラスターを抱えるということは、使用されていないコンピュートリソースへの支払いが発生することになります。Momento Cacheは完全なるPay-Per-Useです。
またキャッシュの運用上避けて通れないホットキーの問題をDAXは抱えます。
ホットキー問題とは?
ホットキーとは人気のあるアイテムのことで、ほかのアイテムより多い読み込み(時には追加書き込み)が行われます。
例えばクラスター型である場合、仮に3台で構成されたとすると、そのクラスターの総アウトプット能力の1/3ずつをそれぞれ1台のインスタンスが担うことになります。
仮に人気のあるホットキーが全リクエストの50%を占めていると仮定します。ホットキーが格納されているインスタンスは1/3の能力しか発揮できないわけですから、システム前提のボトルネックを引き起こします。これはあくまでわかりやすく話を進めるための極端な例ですので、一般的にはそこまでトラフィックが偏るならアプリケーションを見直すことが必要ですが、その話は一旦置いておきます。
クラスター型であるならば、ホットキーが格納されるインスタンスを大きくするスケールアップがこの場合必要となりますが、当然ほかのインスタンスも同じく大きくなりますので、オーバープロビジョニングの問題がますます深刻になります。
Momentoではこの問題を軽減させるため、自動でアイテムの再配置、コピーなどが行われます。このあたりが真のサーバレスと謳っているゆえんです。これはMomento側には大量の共有リソースが存在しており、ユーザーからするとかなり巨大なコンピュートリソース空間が存在しているからこそ実現できることであり、ユーザーが個別にリソースを抱えるクラスター型では実現が困難になります。
Momento CacheをDynamoDBと組み合わせる価値はまさにここにあります。結果として大きなコスト削減が期待できるのではないでしょうか。
インラインキャッシュとサイドキャッシュ
前述の通りインラインキャッシュがデータ処理が投下的でありアプリケーション開発の負担が少なくなります。これは間違いなくDynamoDBとDAXを組み合わせる際の最大のメリットなります。
一方インラインキャッシュはデータソースと一体型であるため、障害時のコントロールが難しくなります。極端な話、すべてのデータがキャッシュされるのであれば、データソース障害時もシステムは稼働し続けます、またキャッシュ障害時、逆もしかりです。
複数データソースの結合やポータビリティを維持する場合もサイドキャッシュが望ましいと言えます。
アプリケーションのポータビリティ
MomentoではAWS/GCP両方で動作することからアプリケーションのポータビリティを一つの特徴としています。このためMomentoを用いることでAWS独自ソースコード部分を減らすことができ、将来的なポータビリティの確保に備えることができます。
しかしここに一つの疑問があります。そもそもDynamoDBを利用している時点でそのコードはAWSへの依存が大きくなるため、キャッシュだけポータビリティを確保することに意味があるのか?ということです。
これは難しい問題です。ケースバイケースとなりそうですが、私の理解は「どこからキャッシュにアクセスをさせる必要があるか?」という点が重要となりそうです。アプリケーションからDynamoDBの操作を行うのみでその高速性を実現させたい場合はDAXがいいかもしれません。一方複数個所から、例えば裏に存在するアプリケーションサーバーからもキャッシュにアクセスしたい場合、MomentoはAWS全域からアクセスができる、もっと言えば他の場所からもアクセスができるという利点は、コード全体のAWSへの依存性を抑えることに役立ちます。DAXはあくまで独立したCacheではなく、DynamoDBとしてアクセスする必要があるからです。
またMomentoはgRPCで通信されます。通信経路が増えれば増えるほどHTTPに対するトータルメリットは向上していきます。
Discussion