OOM canary は実験的な機能で、デフォルトでは無効になっています。本番環境での検証が完了するまで、
その動作は ClickHouse のバージョンによって変更される可能性があります。
概要
SIGKILL でプロセスを強制終了します。通常は最も多くメモリを消費しているプロセスが対象となり、
専用ホストではたいてい clickhouse-server 自身です。その結果、サーバーは回復の機会を与えられないまま、
丸ごと失われてしまいます。
OOM canary は、先に終了させられる対象を変えます。小さな 犠牲用の 子プロセスを実行し、
それ自身が OOM の最優先ターゲットになることで、カーネルはサーバーではなくそのプロセスを
kill します。するとサーバーはその終了を検知し、それが OOM
イベントだったことを確認したうえでメモリ逼迫を緩和し、生き残れるようになります。
canary はメモリ制限を引き上げるものではなく、適切な limits の代替でもありません
(メモリオーバーコミット および
max_server_memory_usage を参照) 。これは最後の防衛線であり、少量の固定メモリと引き換えに、
一時的なメモリ急増を生き延びられる可能性を確保するものです。
仕組み
clickhouse oom-canary プロセスです。自身の
oom_score_adj を最大値 (1000) に設定してカーネルが最初にこれを対象にするようにしたうえで、
oom_canary_size バイト (デフォルトでは 100 MB) を割り当て、実際にアクセスし、mlock して、
resident set が実体を持つようにします。サーバーが終了すると、自動的に終了させられます。
サーバー側では、監視スレッドが canary を (pidfd 経由で) 監視し、
canary が終了すると対応します。
SIGKILLによって kill され、かつ cgroup OOM の証拠がある → OOM 対応を実行した後、 新しい canary を再起動します。- OOM の証拠なしで kill された場合 (たとえば手動の
kill -9) 、または一時的な障害で終了した場合 → 対応は行わず、再起動のみ行います。 - 恒久的な初期化失敗、またはサーバーのシャットダウン → canary は自動的に無効化されます。
memory.events.local oom_kill
カウンターのみです。これは意図的に cgroup ローカルに限定されています。階層的なカウンターやホスト全体のカウンターは
無関係なプロセスによって増加する可能性があり、誤った対応をトリガーしかねないためです。
OOM が確認されると、対応として次の独立した手順が実行されます: FATAL
メッセージをログに記録する、アロケータ (jemalloc) のアリーナを purge する、実行中の
すべてのクエリをベストエフォートでキャンセルする、すべての merge と mutations をキャンセルする、そして
system.crash_log にイベントを
キューに入れます。システムログは同期的には flush されません。メモリ逼迫下で I/O を強制すると、
状況が悪化する可能性があるためです。
要件
- Linux ≥ 5.3. モニターは
pidfd_openを介して canary を管理します。古いカーネルでは、 canary は起動時に自身を無効化します。非 Linux プラットフォームでは no-op です。 - OOM 対応 には
memory.events.localを備えた cgroup v2 が必要です。 これがない場合でも、 canary はSIGKILLの後に再起動しますが、OOM を確認できないため、 対応 は実行されません (起動時に警告がログに記録されます) 。 mlockcapability (任意) 。 canary のメモリを lock するにはCAP_IPC_LOCKまたは十分なRLIMIT_MEMLOCKが必要です。失敗した場合、canary は 警告をログに記録し、そのメモリがスワップアウトされる可能性があるため、 OOM の対象としての有効性が弱まります。
設定
オブザーバビリティ
system.crash_log に signal = 9 の
行が記録され、signal_description には OOM Canary への言及が含まれます: