[CloudFunctions]複数リクエストを投げる場合のコールドスタートについて検証


概要

CloudFuntionsを使ってAPIを実装していたときにコールドスタートについて気になる挙動があったので、実際に処理を実行しながら検証してみました。

実例

一度の処理で5つのAPIを叩く必要があるとします。
この5つのAPIは互いに依存していないので、処理の順番を考慮する必要はありません

順番を考慮する必要がないのでPromise.all使って並列処理させればいいやと思ったものの処理時間が想定とは違う結果になったので、
直列処理と並列処理の速度をそれぞれ検証してみた次第です。

前提

  • functionsのエンドポイントは1つで、その中でexpressを使ってルーティングしています。
  • node v10.17.0

検証

awaitによる直列処理

awaitを使って5つのAPIを直列で実行していきます。

await axios.get(`${this.apiRoot}/hello/gackt`);
await axios.get(`${this.apiRoot}/hello/shogun`);
await axios.get(`${this.apiRoot}/hello/abadango`);
await axios.get(`${this.apiRoot}/hello/takera`);
await axios.get(`${this.apiRoot}/hello/paseriman`);

コールドスタート時

最初の関数の実行に約4秒かかっていますが、それ以降の処理はそれぞれ0.15秒ほどで実行されています。
この結果は想定通り。

ウォームスタート時

コールドスタートに比べて最初の関数の実行が短縮されています。
多少のブレはあるもののそれぞれの関数が0.15-0.20秒ほどで実行が完了しています。
こちらも想定通り。

こんな例外も・・・

検証しているときにこんなパターンも発生しました。
2回目のときに実行環境が変わってコールドスタートが発生したのでしょうか。
これが起きてしまうのは想定外だったし、開発者としては結構ツラいです。。

Promise.allによる並列処理

Promise.allをつかって5つのAPIへのリクエストを同時に投げます。

Promise.all([
  axios.get(`${this.apiRoot}/hello/gackt`),
  axios.get(`${this.apiRoot}/hello/shogun`),
  axios.get(`${this.apiRoot}/hello/abadango`),
  axios.get(`${this.apiRoot}/hello/takera`),
  axios.get(`${this.apiRoot}/hello/paseriman`),
]);

コールドスタート時

最速で3.5秒ほど、最遅で6秒ほど処理に時間がかかっています。
コールドスタートなのでまあこんなもんかという感じ。

ウォームスタート時

(コールドスタート時の実行の直後に再実行してます。)

一部の関数は直列処理のウォームスタートと同じくらいの処理時間で、残りの一部がコールドスタートと同じくらいの処理時間という結果に。
何度か実行してみましたが、たまにすべての関数がウォームスタートと同じ速度で実行されますが、いずれかの関数がコールドスタートと同等になってしまうことのほうが多かったです。

ここからは自分なりの解釈なのですが、
並列処理の場合はすべてのリクエストが同時に投げられるので、それぞれ別の実行環境で実行されるため、その時のfunctions側の状況によってどれだけコールドスタートになるかが決まる、ということなのでしょうか🤔
そうだとすると、実行対象のAPIの数が増えるほどコールドスタートが発生する確率が高くなりそうです。

結論

CloudFunctions(というよりFaaS全般?)においては、同時に実行できるからといって安易に並列処理をするべきではないなと思いました。

参考記事

検証しているときに見つけた記事です。
1年前の記事ではありますが、今でもこの状況が続いているとすればCloudFunctionsは中々の厄介者ですね。。