このライブラリで作成した consumer が queue を消化できなくなり、メモリ使用率がじわじわと上昇し(300MB/8h 位の速度) OOM で停止してしまった。
RabbitMQ の GUI やサーバーのメトリクスを見ると以下のような状況になっていた。
- 起動しているすべての consumer が Unacked メッセージを持った状態で止まっている
- Redelivered が 1,000~2,000/s になっている
- RabbitMQ ・ consumer 間のネットワーク通信量が大きく上がった
原因
- JSON として不正なメッセージを consumer が受け取る
- ライブラリ側のデシリアライズが失敗した際、nack されメッセージが queue の先頭に戻される
- requeue されたメッセージを再び consumer が受け取る
- [1] へ戻る、を永遠に繰り返す
このようなループを繰り返したことで queue の消化ができなくなってしまっていた。
メモリが上昇した理由は分からない。ループによるリソースの消費が速すぎて、ガベージコレクションが間に合わなかったとかかもしれない。
対策
No way to handle failed serialization (non-json) · Issue #137 · golevelup/nestjs · GitHub で説明されていた。
@golevelup/nestjs-rabbitmq@1.15.0
以上で追加された allowNonJsonMessages
を true に設定することで JSON として不正なメッセージをハンドラが受け取れるようになる。それを basic.ack で消化してしまったり、basic.reject で削除したり、デッドレターキューに移動することで requeue ループを防ぐ。
@RabbitSubscribe({ exchange, routingKey: [nonJsonRoutingKey], queue: 'subscribeQueue', allowNonJsonMessages: true, })