Redis Streamsの内部構造を裸にする:XINFOコマンド群による極限のオブザーバビリティ
こんにちは。テックリードの私だ。
君たちが設計・実装したマイクロサービスにおいて、Redis Streamsは今やイベント駆動アーキテクチャの心臓部として稼働していることだろう。メッセージのロストを防ぎ、コンシューマーグループで負荷分散を行い、At-least-once(少なくとも1回)の配送を保証する――素晴らしい選択だ。
だが、本番環境で突然「特定のコンシューマーグループでメッセージが処理されなくなった」「Pending Entries List (PEL) が肥大化してメモリを圧迫している」といった異常事態に直面した時、君はどうする?
`XREADGROUP` や `XACK` のログを眺めるだけでは、ラビリンス(迷宮)に迷い込むだけだ。
Redis Streamsの内部で今何が起きているのか。そのブラックボックスを完全に暴き、システムの状態を掌の上で把握するための鍵が `XINFO` コマンド群 である。
今回は、実務の現場でトラブルシューティングと堅牢な設計に直結する、`XINFO` の実践的な活用術を伝授しよう。
—
1. XINFOコマンド群の全体像
`XINFO` は、単一のコマンドではない。Streamsのメタデータ、コンシューマーグループ、コンシューマー、そして内部のRadix Tree構造に至るまで、多角的なインスペクションを可能にする「偵察部隊」だ。
実務で使うべきは主に以下の3つである。
1. `XINFO STREAM
2. `XINFO GROUPS
3. `XINFO CONSUMERS
これらを使いこなせなければ、君は「内部で何が起きていないかすら分からないコード」をデバッグしている素人にすぎない。さっそく実戦的な視点で見ていこう。
—
2. XINFO STREAM:ストリームの「健康診断」
まずはストリーム自体の状態を把握する `XINFO STREAM` だ。
ただエントリ数を数えるだけなら `XLEN` で十分だが、`XINFO STREAM` はメモリの健康状態やパージの進捗を測るために使う。
実行例と出力の読み方
127.0.0.1:6379> XINFO STREAM mystream
1) length
2) (integer) 154200 # 現在のエントリ数
3) radix-tree-keys
4) (integer) 42 # 内部のRadix Treeのキー数
5) radix-tree-nodes
6) (integer) 85 # 内部ノード数(メモリ断片化の指標)
7) last-generated-id
8) “1689324000000-0” 最後に生成されたエントリID
9) max-deleted-entry-id
10) “1689320000000-0” # XDEL等で削除された最大ID
11) entries-added
12) (integer) 500000 # ライフタイムで追加された累計エントリ数
13) first-entry
14) 1) “1689320005000-0” # 現在保持されている最古のエントリ
2) 1) “sensor_id”
2) “A-42”
15) last-entry
…(最新のエントリ)
チーフアーキテクトの視点:メモリ肥大化の予兆を掴む
実務で注目すべきは `radix-tree-nodes` と `length` のバランスだ。
Redis Streamsは、メッセージのキーやIDのプレフィックスを効率的に検索するため、内部で Radix Tree を使っている。
もし `XADD` で上限なくデータを詰め込み、`XTRIM` や `MAXLEN` による切り詰め(Capping)を怠っていると、このツリーが肥大化し、Redisプロセス全体のメモリ(RSS)を食いつぶす。
もし、運用中のモニタリングで `length` が数百万を超え、かつ特定のコンシューマーグループが全く進んでいないことが分かったら、それはメモリリーク寸前の爆弾を抱えているのと同じだ。直ちに `XTRIM` を検討せよ。
—
3. XINFO GROUPS:グループの「停滞」を検知する
コンシューマーグループを導入したシステムでは、「どのグループがどの程度遅延しているか(Lag)」の監視が生命線になる。これを行うのが `XINFO GROUPS` だ。
実行例
127.0.0.1:6379> XINFO GROUPS mystream
1) 1) name
2) “analytics-workers”
3) consumers
4) (integer) 4
5) pending
6) (integer) 12 # 配信されたが未確認(XACKされてない)の数
7) last-delivered-id
8) “1689323990000-5”
9) entries-read
10) (integer) 154188 # 累計読込数
11) lag
12) (integer) 12 # ★重要:ストリームの最新末尾からどれだけ遅れているか
チーフアーキテクトの視点:`lag` メトリクスによるアラート設計
ここの出力に含まれる `lag`(あるいは `pending`)こそ、オブザーバビリティの核心である。
- `lag` が右肩上がりに増え続けている場合: コンシューマーの処理能力が追いついていないか、スレッドがデッドロックしている。
- `pending` が異常に多い場合: メッセージは読み込んだものの、データベースへの書き込みなどでエラーが起き、`XACK` が漏れている(あるいは処理が途中で落ちている)。
Prometheusなどの監視基盤と連携させ、`XINFO GROUPS` の結果を定期スクリプトでスクレイピングし、`lag > 10000` や `pending > 500` でPagerDutyを鳴らすような設計にするのが、プロフェッショナルなインフラストラクチャ設計だ。
—
4. XINFO CONSUMERS:ゴーストコンシューマーの特定
グループ内の個々のコンシューマーの健康状態を暴くのが `XINFO CONSUMERS` だ。
実行例
127.0.0.1:6379> XINFO CONSUMERS mystream analytics-workers
1) 1) name
2) “worker-node-01”
3) pending
4) (integer) 3
5) idle
6) (integer) 142000 # 最後にインタラクションがあってからのミリ秒 (約142秒)
2) 1) name
2) “worker-node-02”
3) pending
4) (integer) 9
5) idle
6) (integer) 50 # 50ミリ秒前(バリバリ稼働中)
チーフアーキテクトの視点:ゴースト(ゾンビ)コンシューマーの掃除
ここで見るべきは `idle` と `pending` だ。
例えば、Podのオートスケーリングやネットワーク切断によって死んだコンシューマー(`worker-node-01`など)が、Redis側に名前とPELを残したまま放置される現象がよく起きる。
死んだコンシューマーが `pending` 状態のメッセージを抱え込んだままになると、他の生きたコンシューマーはそのメッセージを自力で拾うことができない(アトミックにそのコンシューマーに占有されているため)。
この状態を放置すると、メッセージが永遠に処理されない「毒薬メッセージ(Poison Pill)」のような挙動を引き起こす。
これを解決するために、`XINFO CONSUMERS` でアイドリング時間が長すぎる幽霊を検知し、`XAUTOCLAIM` または `XPENDING` からの `XCLAIM` を使って、生存している別のコンシューマーへメッセージの所有権を強奪(Claim)する仕組みを自動化パイプラインに組み込むべきだ。
—
5. 実務で活きる:堅牢なデバッグ・監視設計パターン
これらの `XINFO` コマンド群を、単なる「手動でのCLI確認用」として片付けてはならない。本番稼働するシステムでは、以下のような設計パターンに組み込むことで真価を発揮する。
パターンA: ヘルスチェック・エンドポイントへの組み込み
アプリケーションの `/healthz` や管理用内部APIの中で、主要なストリームに対して `XINFO GROUPS` を叩かせろ。
特定のグループの `lag` や `pending` が閾値を超えた場合、ヘルスステータスを `WARN` や `NG` に落とすことで、K8sのLiveness/Readinessプローブや監視システムに即座に検知させることができる。
パターンB: デッドレターキュー(DLQ)前段のインスペクション
「なぜこのメッセージは何度もPELに残り続けるのか?」
それを調査するため、アプリケーションのリカバリワーカーが異常を検知した際、動的に `XINFO STREAM` や `XPENDING` の詳細を構造化ログとして吐き出させる。これにより、障害原因の特定スピードが劇的に向上する。
—
最後に:チーフアーキテクトからのメッセージ
Redis Streamsは、正しく使えばRDBのキューテーブルを遥かに凌駕するスループットと堅牢性をもたらす。しかし、その内部構造(Radix Tree、PEL、Consumer Groupのオフセット管理)を理解せず、ただコードを書くだけでは、いつか必ず本番環境の闇に足元をすくわれることになる。
「動いているからいいや」ではない。
「内部がどうなっているかを常に観測できているから安心できる」――これが、プロフェッショナルなエンジニアリングだ。
今すぐ君たちのステージング環境で `XINFO` を叩いてみろ。
そこには、君のコードの「真実の姿」がすべて映し出されているはずだ。
コメント