HangfireからRabbitMQへ:データベースポーリングの廃止

Hangfireは小規模なチームにとって非常に優れています。NuGetパッケージを追加してデータベースを指定するだけで、ジョブラナーとダッシュボードが無料で手に入ります。単一のサービスであれば、これに勝るものはありません。

私はSavePostyにおいて、メール送信、Webhook、コンテンツ取得の処理にHangfireを使用していました。すべてのジョブは同じ仕組みで動作していました。新しいタスクがないか確認するために、数秒おきにPostgresデータベースをポーリングしていたのです。

最終的に、私はすべてをRabbitMQに移行しました。その理由と、移行によって失ったものについて説明します。

移行した理由:

  • ポーリングの負荷は、作業量ではなく時間に比例して増大します。Hangfireの各ジョブはタイマーに従ってデータベースにアクセスします。たとえ処理すべきタスクがなくても、データベースは稼働し続けます。このコストは、ジョブの数とポーリングの頻度に応じて増大していきます。
  • モデルが多すぎる。アプリの一部ではすでにRabbitMQを使用しており、残りはHangfireを使用していました。これは、バックグラウンド処理の管理方法が2種類あることを意味します。RabbitMQに移行することで、すべてを一本化できました。
  • レイテンシ。ブローカーはタスクが到着した瞬間にそれをプッシュします。Hangfireは次のポーリングを待つ必要があります。

トレードオフ:

Hangfireはセットアップが簡単です。既存のSQLテーブルを使用し、優れた組み込みダッシュボードを備えています。

RabbitMQはブローカーの管理が必要です。データベースのCPUではなく、RAMとネットワークを使用します。スケーラビリティは向上しますが、運用上のオーバーヘッドは増えます。

安全な移行方法:

ビジネスロジックは全く変えずに維持しました。フロントドアとして機能する軽量なコンシューマー(Consumer)を作成しました。コンシューマーがメッセージを受け取り、既存のジョブクラスに渡すという仕組みです。

私は次の2点に注力しました:

  • リトライの整合性。メッセージの消失を防ぐため、RabbitMQコンシューマーのリトライ設定をHangfireの設定と一致させました。
  • スキーマの安全性。デプロイ中にデータベースを壊さないよう、古いカラムはNULLを許容(nullable)したままにしました。

失ったもの:

最大のデメリットは可視性です。Hangfireでは、失敗したジョブをクリックして何が起きたのかを正確に確認できます。RabbitMQでは、Dead Letter Queueにいくつのメッセージがあるかは分かりますが、Hangfireのようなジョブごとの簡単なビューは提供されません。現在、私はダッシュボードの代わりに構造化ログ(structured logs)を活用しています。

私からのアドバイス:

次のような場合はHangfireを使い続けるべきです:

  • 単一のサービスを運用している。
  • チームが小規模である。
  • デバッグ用の簡単なダッシュボードが必要である。

次のような場合はRabbitMQへ移行すべきです:

  • 複数のサービスを運用している。
  • ポーリングによるデータベース負荷が高い。
  • Pub/Subパターンを使用したい。

決定は単一のジョブではなく、システム全体に基づいて行うべきです。

Source: https://dev.to/gabrielleroux/from-hangfire-to-rabbitmq-killing-database-polling-in-a-net-app-4og4

Optional learning community: https://t.me/GyaanSetuAi