この記事は新歓ブログリレー2021の9日目(3/17)の記事です。
また、SysAdTechBlogの第二回目の記事です。
こんにちは、19のSysAd班の翠(sappi_red)です。
ブログ、そうです、この今見ているブログです。
このブログのレスポンスが返ってくる時間を10分の1にする改善をしました。
技術
このブログではGhostというNode.js製のCMSを利用しています。
実際にページが表示されるためには、以下のような流れで動作します。
- Node.jsでアクセスされたパスに対応するテンプレートを解釈する
- テンプレートで利用している情報をDB(MySQL)からデータを取得する
- 取得したデータをテンプレートに合うようにHTMLにして返す
Handlebarsというテンプレートエンジンによってテンプレートが解釈・実行されています。
調査
2021-02-07 23:59:43
[2021-02-07 14:59:43] WARN {{#get}} helper took 234ms to complete
調査というほどではないのですが、ログにこのようなwarningが出力されていました。
この{{#get}}
というのはテンプレート内でAPIを利用する場合に利用する記法です。
APIの取得はサーバー側で実行されて、レスポンスのHTMLに反映されます。
実際にレスポンスが返るのに300ms程度かかっていて、230ms程度をこれにかけているということは大半の時間がここにかかえているということなので、これを改善することにしました。
改善方法
こんな関数でレスポンスを返す箇所をくるみました
const cachedResults = {};
function returnCacheAndStartRefetch(key, fetchData) {
const cache = cachedResults[key];
if (cache) {
if (!cache.isFetching) {
cache.isFetching = true;
const promise = fetchData();
promise.then(function () {
cache.promise = promise;
cache.isFetching = false;
});
}
return cache.promise;
}
const promise = fetchData();
cachedResults[key] = {
isFetching: true,
promise: promise
};
promise.then(function () {
cachedResults[key].isFetching = false;
});
return promise;
}
やってることとしては、
- 以前に取得した情報がないときは、取得して取得完了後、その情報を格納した上で、レスポンスを返す
- 以前に取得した情報があるときは、その情報でレスポンスを返しつつ、情報を新しいものに更新する
といった感じです。また、取得中の場合はその取得が完了するのを待ちます。
Cache-Control
ヘッダのstale-while-revalidate
の挙動に近いものですね。
前回取得がいくら前だとしても一度はキャッシュを返す挙動になっているので、前回取得が一定期間より前だった場合は待ってから返すようにするべきという点は要改善です。
結果
これはSysAd班が部内サービスの監視に使っているgrafanaでのグラフですが、改善前が300ms程度だったのが30ms程度になっています。
実際に手元からトップページを開いたときの.html
のレスポンスも同じくらいの時間でした。
.html
のレスポンスが速くなると.js
や.css
のダウンロードの開始も早くなるので得られた結果はそこそこ大きいと思います。
明日は @Rozelin さんの記事です。お楽しみに!