Momento 入門 その2:キャッシュへの通信とパフォーマンスを探る
この記事では、このGetting Startedで出来上がった環境に対して、クライアントからの通信やそのパフォーマンスを探っていきます。
Momento への通信を見てみる
まず通信を見るために、iftopコマンドをインストールし実行しておきます。
sudo yum install iftop
sudo iftop -P -N
別のターミナルを開き前回と同様にテストスクリプトを実行します。
node test.js
ip-172-31-41-119.ap-northeast-1.compute.internal:44070      => ec2-35-73-165-251.ap-northeast-1.compute.amazonaws.com:443     0b   1.77Kb   452b
                                                            <=                                                                0b   5.45Kb  1.36Kb
ip-172-31-41-119.ap-northeast-1.compute.internal:58360      => ec2-35-75-220-172.ap-northeast-1.compute.amazonaws.com:443     0b   1.36Kb   347b
                                                            <=                                                                0b   5.06Kb  1.27Kb
172.31.41.119は検証環境のプライベートIPアドレスです。そこから高いポート番号を用いて2つのEC2インスタンスにアクセスしているようです。これがキャッシュのエンドポイントのようです。(あくまで通信先でありMomento本体がEC2で動いているかどうかは不明ですので注意してください)
試しに検証環境のアウトバウンドをTCP/443のみに絞りましたが問題なく動作しています。
AWS内部のパブリック通信について
上の検証環境を見ると、MomentoキャッシュへはPublicIPを用いて通信を行っています。これはインターネット、つまり公衆網を通信が通っているのでしょうか?
あまり知られていないことですが、AWS内部のパブリックエンドポイントの通信はAWSが管理している内部ネットワークを通ります。EC2からS3、CloudFrontからEC2やS3なども同様です。
このためMomentoキャッシュへの通信はVPC外ではあるが、AWS内部通信といえます。企業にとってはVPCしか利用が許可されないため、Momentoを使いたくてもElastiCacheしか許可されないケースがあると思いますので、これは知っておくと便利な知識です。
Momento のパフォーマンスを見てみる
ではキャッシュへの読み書きスピードを以下3パターンで見ていきます。
100回書き込み
100回読み込み(同じデータ)
100回読み込み(異なるデータ)
余談ですが、ElastiCacheとのパフォーマンス比較は気になる点ではあるものの、専門知識を持たない人間が前提条件をそろえず、少しだけ行ったパフォーマンス比較をブログで残すのは危険だと考えています。お互いの得意領域が異なるケースもあります。(例えばダブルバイトに強い、長い文字列に強いとか)またクラウドは日進月歩であり、執筆時点でのパフォーマンスが1年後の開発者の参考になってしまうことも考慮し、定期的にアップデートしないのであれば記載しないほうが吉です。
と、いうことでこの記事ではMomentoのパフォーマンスのみを記載します。
100回書き込み
まずtest.jsのrun()関数を以下のように書き換えます。
async function run() {
    const cacheClient = await createCacheClient();
    //await createCache(cacheClient);
    await listCaches(cacheClient);
    const start = performance.now();
        for (let i = 0; i < 100; i++) {
        await writeToCache(cacheClient, CACHE_NAME, "code" + i, "12345" + i);
    }
    //await readFromCache(cacheClient, CACHE_NAME, "code");
    const end = performance.now();
    console.log(`実行時間: ${end - start} ミリ秒`);
}
実好結果は以下になります。
実行時間: 216.5053449999541 ミリ秒
1件当たり2ミリ秒ですからこれは早いです。驚きの速さといってもいいレベルです。スクリプトを何度実行しても大体同じパフォーマンスになります。これはすでに同じKey-Valueがあってもなくてもキャッシュのパフォーマンスは同じだ、ということがわかります。
尚forループを以下のように書き換えてもパフォーマンスはほぼ同じですのでシーケンシャルだから早いわけではないことがわかります。
    for (let i = 0; i < 100; i++) {
        await writeToCache(cacheClient, CACHE_NAME, "code" + i, Math.random().toString(36).substring(2));
    }
また数字を10,000に増やして実行するとこのようなエラーが出ました。
Error setting key:  Request rate, bandwidth, or object size exceeded the limits for this account.  To resolve this error, reduce your usage as appropriate or contact us at support@momentohq.com to request a limit increase: 8 RESOURCE_EXHAUSTED: Api rate limit exceeded. Please contact support@momentohq.com for a limit increase
大規模な同時書き込みが多いケースではMomentoのサポートにLimit Increase申請を出す必要があるようです。これ自体はAWSでも普通に発生することですので大規模案件ではAWSと一緒にMomentoにLimit Increase申請を出す必要がある、ということは覚えておきましょう。
100回読み込み(同じデータ)
次に同じデータを100回読み込むためにtest.jsを以下に変更します。
async function run() {
    const cacheClient = await createCacheClient();
    //await createCache(cacheClient);
    await listCaches(cacheClient);
    const start = performance.now();
   // for (let i = 0; i < 100; i++) {
   //     await writeToCache(cacheClient, CACHE_NAME, "code" + i, Math.random().toString(36).substring(2));
   // }
   for (let i = 0; i < 100; i++) {
   await readFromCache(cacheClient, CACHE_NAME, "code1");
   }
        const end = performance.now();
    console.log(`実行時間: ${end - start} ミリ秒`);
}
パフォーマンスは大体これぐらいでした。
実行時間: 213.49685900006443 ミリ秒
1アイテムあたり2ミリ秒ですから、おおよそ書き込みと同じなのが面白いところです。ディスクを伴うデータベースであれば書き込みの方が一般的には重たいので、インメモリのキャッシュならではでしょうか。
100回読み込み(異なるデータ)
では最後に異なるデータを100回読み込みます。
async function run() {
    const cacheClient = await createCacheClient();
    //await createCache(cacheClient);
    await listCaches(cacheClient);
    const start = performance.now();
   // for (let i = 0; i < 10000; i++) {
   //     await writeToCache(cacheClient, CACHE_NAME, "code" + i, Math.random().toString(36).substring(2));
   // }
   for (let i = 0; i < 100; i++) {
   await readFromCache(cacheClient, CACHE_NAME, "code"+i);
   }
        const end = performance.now();
    console.log(`実行時間: ${end - start} ミリ秒`);
}
実行結果は以下でした。
実行時間: 386.3389590000734 ミリ秒
同じデータ読み込みよりは時間がかかるようになりましたが、それでも勿論平均4ミリ秒未満と超速です。これは予測できる結果です。毎回キーが異なれば個別データ探索の時間がかかるためです。それを差し引いて考えても圧倒的に拘束といえるのではないでしょうか。
追加で試しに100件のReadリクエストをすべてmissさせてもパフォーマンスは同じでした。遅くなるかな?と思ったのですが意外でした。
ゲームや大規模ソーシャルサイトなどではかなり役に立ちそうです。
Discussion