- RabbitMQを撤去し、PostgresDB上のSQLベースのキューに置き換え
- 置き換えには半日ほどしかかからず、ソースコードは580行削減
- それ以上に重要だったのは、安定性と復元力(Resiliency)が大幅に向上したこと
- これはRabbitMQのようなキューシステムの問題を語る話ではなく、単純さを維持するための取り組みの一環
- この記事を書いたPrequelは、大規模データパイプラインによってB2B SaaS企業が顧客のDBと同期するのを支援する製品
- RabbitMQ + AQMP + Helm + GoLangラッパーで構成していたが、しばらくはうまく動いていたものの、問題が出始めた
- メッセージ配送の遅延が長すぎて、メッセージがキャンセルされる事態が発生
- コンシューマーがメッセージをプリフェッチすることで、現在のメッセージを完了するまで次のメッセージを抱え込む状況が発生
- このプリフェッチを0にすると無限大になってしまうため、プリフェッチを無効化できない
- メッセージを受け取って処理する作業の多くが長時間の処理(データ転送)であることから生じた問題
- 何が起きているのかは正確に把握できたが、すぐに直す方法はなかった。本番環境の顧客に影響が出ていたため、待つことは不可能だった
- そこでPostgresで1つのシンプルなテーブルを使い、読み書きする方式で動かすことにした
- 読み書き時のRow-Level Lockを使って、1つのコンシューマーだけが作業を読み取れるよう保証
- ばかばかしいほどシンプルだが、完璧に動作している
- アプリケーションの状態がRabbitMQとPostgresに分断されず、1つに統合される利点もある
6件のコメント
私も MySQL で row level lock を使ってキューを直接実装し、長年プロダクトで問題なく動かしているのですが、
単純に複数のワーカーにキューに似た機能を提供しつつ、キュー内での待機中・進行中・失敗(完了なら削除)といった状態を含む payload を一緒に保存できることを望んでいました。
本文で短期間のうちに RabbitMQ から DB に切り替えたことを見ると、あえて別個の専用キューサービスの汎用性や多様な機能、設定の自由度が必要ではなかったからではないかと思います。
手動ACK送信とDBトランザクションの間でズレが生じて問題になったようですが、
これをエンジニアリングで解決するのではなく、RabbitMQ自体を取り除く形にしたのが面白いですね。
RabbitMQに罪はないはずですが…
https://news.ycombinator.com/item?id=35526846
コメントに多くの意見がありますね。
似たような方式で動作する https://github.com/pjongy/jasyncq のようなものもありますね
同時に
selectされる可能性がありそうですが、それをどのように解決したのか気になりますね。row level lockと書かれていますね