大量の未読ユーザー通知による大規模キューで発生するSidekiqの非常に遅い問題

@Falco さん、ありがとうございます。

同じ設定で、わずか 1 週間で 1 日あたりの完了ジョブ数が約 1100 万件から約 30 万件に激しく変動している理由がどうしてもわかりません。ジョブ/秒換算で約 35 倍もの速度差があります。

CPU 使用率については、再び通常の約 15〜20% まで低下しており、ジョブ処理速度も以前と同じ(遅い)ままです。

確認のためですが、私が言いたかったのは、低優先度キューを専用に処理するためにいくつかの Sidekiq ワーカーを「追加」するのではなく、「割り当てる」ことです。低優先度タスクははるかに高速に処理でき、同じボトルネックの影響を受けないように見えたためです。ジョブ/秒の数がこれほど劇的に変動する理由(つまり、デフォルトキューのバックログの背後に低優先度の「簡単」タスクが挟まっていること)をこれで説明できるのではないかと推測していました。

念のため確認ですが、ジョブ完了が遅い原因は PostgreSQL のパフォーマンスにあるとお考えでしょうか?それとも、昨日観察した CPU 使用率の高騰(現在は正常に戻っています)が原因だとお考えでしょうか?

これはすべてSSDですよね?

はい、その通りです @Stephen - NVMe SSD の RAID 1。

更新:低速優先キューとデフォルトキューを数回削除しましたが、デフォルトキューがすぐに再び増加するため、速度には影響しませんでした。その後、デフォルトキューを削除して読み取り専用モードを有効にしました。これにより、1 秒あたりのジョブ数が劇的に急増し、低速優先キューを驚異的な速度(約 100 倍の処理速度)で処理し始めました。

編集:低速優先キューが大量に存在するだけでも、処理速度は依然として遅いようです。Discourse を読み取り専用モードに設定し、低速優先キューとデフォルト優先キューの両方を空にすると、その後のジョブ処理が非常に高速に保たれ、スケジュールされたタスクとキューが空になるまで処理が続きます。読み取り専用モードを無効にすると、この状態は解除されます。:yuno:

次のステップは、Discourse アプリにアクセスして htop または top を実行し、CPU 使用率が高いプロセスを確認することで、問題の原因となっているプロセスを特定することです。

Postgresがボトルネックになっているようです。Prometheusを構成してパフォーマンスを監視し、十分なメモリにアクセスできているか確認してみてください。

@pfaffman のフィードバックをありがとうございます :slight_smile: app.yml 内の db_shared_buffersdb_work_mem が、PostgreSQL の RAM アクセスを制御する唯一の設定項目ですよね?

値を上げたり下げたりして少し試してみました。現在の app.yml の設定は以下の通りです:
db_shared_buffers: “32768MB”
db_work_mem: “128MB”

システム全体の RAM は 128GB です。

また、/var/discourse/shared/standalone/postgres_data/postgresql.conf 内の max_connections を変更し、Discourse を再ビルドしてみることも試みました。デフォルト値(100)より高い値、200 から 500 の範囲で試しましたが、現在は 300 に設定しています。ただ、そこで変更しても本当に最大接続数が反映されているのかは確信が持てません。

/var/discourse/templates/postgres.template.yml には以下のような設定があります:

db_synchronous_commit: “off”
db_shared_buffers: “256MB”
db_work_mem: “10MB”
db_default_text_search_config: “pg_catalog.english”
db_name: discourse
db_user: discourse
db_wal_level: minimal
db_max_wal_senders: 0
db_checkpoint_segments: 6
db_logging_collector: off
db_log_min_duration_statement: 100

@bartv さん、ありがとうございます。あなたの提案に従い、Discourse アプリ内で top コマンドを使って監視しました。postgres ユーザーによって実行されている postmaster プロセスが非常に多く見られ、CPU 使用率も変動しています。スクリーンショットは、類似の使用統計が長い期間続いている様子を示しています。

32 コアの約 95% を使用:

約 20% の使用で、postmaster の CPU 使用率が低い状態。

読み取り専用モードが有効な間、CPU 使用率は約 6%。

データベースの規模はどのくらいですか?ユーザー数は何人ですか?1日あたりの新規投稿数は何件ですか?

まず、Postgres コマンドラインから VACUUM ANALYZE; を実行してください。

実行には時間がかかる場合があります。処理中の負荷を軽減するため、Sidekiq を一時的に停止することをお勧めします。

それでも改善しない場合は、pg_stat_statements を有効にして、どのクエリが大量の CPU を消費しているかを確認してください。

@pfaffman

  • /var/discourse/shared/standalone/postgres_data フォルダのサイズは 170GB です
  • 直近 30 日間のアクティブユーザー数は 61,700 人です(総数は不明です)
  • 1 日あたりの新規投稿数は約 5 万〜8 万件です

ああ、それは単純なことではありませんね。

Postgres のチューニングについて調べてみるべきです。そのレベルのパフォーマンスは、ここで一般的に見られるセルフホスティングの範囲を超えています。

Postgres に RAM の約 3/4 を割り当てることをお勧めします。データ用と Web 用のコンテナを分けるのは確実に行うべきです。しかし、必要なパフォーマンスを得るためには、より複雑な Postgres の設定が必要になるかもしれません。

編集:ただし、より大規模なデータベースの経験はあまりありませんので、以下を参照してください!:wink:

私たちは、はるかに少ないRAMとCPU使用量で、はるかに大規模なデータベースを処理しています。

pg_stat_statementsの情報から、問題の原因がわかるはずです。

皆様、お手伝いいただき本当にありがとうございます。

VACUUM ANALYZE; を実行してみましたが、残念ながら効果はありませんでした。参考までに使用したコマンドを以下に示します。

cd /var/discourse/
./launcher enter app
sudo -u postgres psql
\c discourse
VACUUM ANALYZE;

次に pg_stat_statements を有効化しようとしました。手順は以下の通りです。

/var/discourse/shared/standalone/postgres_data/postgresql.conf の以下の行を追加・変更しました。

shared_preload_libraries = ‘pg_stat_statements’ # (change requires restart)
pg_stat_statements.max = 10000
pg_stat_statements.track = all
pg_stat_statements.track_utility = on
pg_stat_statements.save = off

その後、Discourse を再構築し、以下のコマンドを実行しました。

./launcher enter app
sudo -u postgres psql
\c discourse
CREATE EXTENSION pg_stat_statements;

クエリを実行しようとしましたが、以下のエラーが発生します。

ERROR: pg_stat_statements must be loaded via shared_preload_libraries

私の推測では、postgresql.conf ファイル(/var/discourse/shared/standalone/postgres_data/postgresql.conf)への編集が反映されていないのではないかと思われます(編集後に Discourse を再構築はしました)。これらの編集を app.yml ファイルを通じて行うことは可能でしょうか?あるいは、私が何か間違えている点はありますか?

Discourse を再構築すると、それらの変更は失われます。コンテナを再起動すれば、おそらく問題が解決するはずです(コンテナ内で sv restart postgres のようなコマンドを実行しても同様の効果が得られるかもしれません)。

ありがとうございます。コンテナを再起動してみました。

./launcher stop app
./launcher start app

しかし、クエリを実行しようとしても同じエラーが発生します。

ERROR: pg_stat_statements must be loaded via shared_preload_libraries

以前行った変更は、再構築を行っても以下のファイルで引き続き保持されています。

/var/discourse/shared/standalone/postgres_data/postgresql.conf

このファイルで編集を行うべきではないのではないかと疑っています :face_with_monocle:

containers フォルダで app.yml(および mail-receiver.yml)のみを使用し、data.yml を実装していないことが原因かもしれません。

実際に確認はしていませんが、コンテナ内の /etc/postgres である可能性が高いです。また、必要とされているライブラリをインストールする必要があるかもしれません。

ありがとうございます、最初は非常に役立ちました。:man_cartwheeling: 一時的に問題は解決したように見え、PostgreSQL の max_connections を増やすだけでジョブキューが非常に速く処理されていました。しかし、残念ながら約1日後に再び遅くなりました。

以下の手順は、PostgreSQL の max_connections を増やしたい他の人の参考になるかもしれません。

docker ps

コンテナ ID を取得します(例:aaabbbccc123)。以下のコマンドでこの ID に置き換えてください。

Docker コンテナ内の postgresql.conf ファイルをローカルファイルシステムにコピーします。

docker cp aaabbbccc123:/etc/postgresql/10/main/postgresql.conf /srv

設定ファイルを編集します。

nano /srv/postgresql.conf

編集したファイルを Docker コンテナに戻します。

docker cp /srv/postgresql.conf aaabbbccc123:/etc/postgresql/10/main/postgresql.conf

cd /var/discourse
./launcher stop app
./launcher start app

不要になったファイルを削除します(オプション)。

rm /srv/postgresql.conf

@supermathie I believe I’ve successfully enabled pg_stat_statements :grinning:

I tried using this query:

SELECT
(total_time / 1000 / 60) as total,
(total_time/calls) as avg,
query
FROM pg_stat_statements
ORDER BY 1 DESC
LIMIT 100;

From this guide: The most useful Postgres extension: pg_stat_statements

I can’t really read the result though, I think I’ve done something wrong.

total | avg | query
1671.1110420745 | 374.736186677194 | SELECT COUNT(*) FROM ( +
| | SELECT $1 FROM +
| | notifications n +
| | LEFT JOIN topics t ON t.id = n.topic_id +
| | WHERE t.deleted_at IS NULL AND +
| | n.notification_type <> $2 AND +
| | n.user_id = $3 AND +
| | n.id > $4 AND

やりたいことがわかったら、app.yml の replace ステージでこれらの変更を加えることができます。

また、./launcher enter appを実行してファイルを直接編集することもできます。ただし、再構築すると、新しいコンテナにはその変更は反映されない点にご注意ください。