この記事で扱うこと
サーバーレスでデプロイされるサイトに全文検索を付けるにあたり、「何を関数の内側に置けて、何を外側に置くべきか」を整理した。Elasticsearch のように常に起動していなければならないプロセスと、リクエストごとに目覚める関数の違いが核心だ。
1. サーバーレス検索バックエンドと非同期検索UI
背景知識(概念)
- サーバーレス関数: リクエストごとに実行・終了する無状態環境。メモリに常駐するデーモンをそのまま載せるのは難しい。
- 転置インデックス(inverted index): 単語 → ドキュメントidのリスト。ビルド時に作っておき、関数から読み込める。
- Out-of-order 応答: 遅い以前のリクエストが遅れて到着し、最新の結果を上書きするUIバグ。
どんな状況だったか
サーバーレスでデプロイされるサイトに検索が必要だった。常駐する検索エンジンプロセスを関数の中にそのまま立ち上げることはできない。外部ホスティングなしでデプロイするには、関数メモリで動作するライブラリ型インデックスが必要だった。
クライアントはタイピングのたびに検索APIを叩くので、debounce・キャンセル・最新リクエスト優先なしではUIが崩れうる。
目標は4つだった — サーバーレス内で動作するインメモリ転置インデックス(検索バックエンド)、ビルド成果物のインデックスファイルがコールドスタートでも含まれること(デプロイ安定性)、UI・テストはprovider 契約だけを見て、後で常駐型エンジンに差し替え可能にすること(差し替え可能性)、最新の結果だけを画面に反映すること(検索UI)。
主な作業
- インデックス: 事前ビルドしたファイルを関数から読み込み、インメモリ転置インデックスで検索した。
- バンドル: ランタイムで読み込むビルド成果物をバンドルトレース設定に明示し、コールドスタートでの欠落を解決した。(詰まった部分 — コールドスタートでインデックスファイルが見つからない → ビルド成果物を関数バンドルに明示的に含めることで解決。)
- 契約: クライアントは検索 provider 契約と単一のAPIエンドポイントだけに依存させ、後で常駐型エンジンに変えてもUI・テストはそのままにした。
- UI: debounce・AbortController・最新リクエストidのパターンを適用した。
教訓
- 常駐型 vs ライブラリ型の検索の境界は**「メモリに載り続けていなければならないか」**だ。
- 非同期検索UIは debounce・キャンセル・最新リクエストidを一緒に使わなければならない。
- コンテンツメタはランタイムDBより単一ソースからビルド時に決定する方が一貫する。
- 後続: 韓国語検索の品質が必要になる時点で常駐型エンジンへの移行基準を整理する。
→ Serverless stateless execution · Inverted index full-text search · Race-safe async UI requests · Single source of truth content metadata