はじめに
先日、モンスターを片手にモニターに向かっていると、開発環境でテスト中に思わぬ出来事が・・・!
ログインしたはずなのに、画面に見知らぬユーザー情報が表示されました。
「これ、誰?」 🤔
調査の結果、なんと Cloud Runでグローバル変数として保持していた QueryClient が犯人でした!!
サーバーレス環境特有の落とし穴にハマっていたのです🦵🕳️
ユーザー間でキャッシュが共有される恐怖 😱
Cloud Runでは、リクエスト処理後もインスタンスが生き続け、次のリクエストを待ちます。
このとき、グローバル変数の状態はリセットされません。それが原因で問題が発生しました
// 危険なコード例
const queryClient = new QueryClient(); // グローバルスコープで定義!
export function handler(req, res) {
const userId = getUserIdFromRequest(req);
const userData = queryClient.fetchUserData(userId);
res.send(userData);
}
このquery clientが内部的にユーザー情報をキャッシュしていたため、別のユーザーのリクエストでも前のユーザーの情報を返してしまいました 🔄
💡 解決策: コンテキストベースの状態管理
幸い、解決策はシンプルです。リクエストごとにquery clientを初期化します。
// 改善後のコード
export function handler(req, res) {
// リクエストごとに新しいクライアントを作成
const queryClient = new QueryClient();
const userId = getUserIdFromRequest(req);
const userData = queryClient.fetchUserData(userId);
res.send(userData);
}
サーバーレス環境でのキャッシュ設計原則
この経験から学んだ教訓をまとめると
- ⚠️ グローバル変数に要注意 - 特にquery clientなどのステートフルなオブジェクト
- 🔒 リクエスト単位でコンテキストを分離
- 📚 使用ライブラリのキャッシュ動作を理解する
- 🔍 定期的なセキュリティレビュー
なお、Vercelではグローバル変数がリクエスト間で共有されない仕組みのため、同様の問題は発生しませんが、環境依存のコードは避けるべきでしょう。
結論
隠れた落とし穴に注意
今回は開発環境で発見できたため大事には至りませんでしたが、サーバーレス環境では、環境ごとの仕様の違いに注意する必要があります。自社のアプリケーションも一度見直してみてはいかがでしょうか?
思わぬところに潜む「キャッシュの罠」が見つかるかもしれませんよ😎
サーバーレスは非常に便利ですが、その特性を理解せずに使うと思わぬ落とし穴にはまることがあります。日々の開発作業に没頭するなかでも、「このクライアントが想定外の状態を保持していないか」と定期的に確認する習慣をつけましょう ⏰
安全なサーバーレス開発への第一歩です! 🚀