膨大なストレージトランザクション

こんにちは、

ストレージに対して、頻繁に大量のトランザクション負荷が発生しています。この事象のスケジュールや時間的なパターンは見つかりませんでしたが、少なくとも1日に1回は発生します。また、その持続時間は10分から数時間まで変動します。

これらの負荷が発生している間、インストール全体が少し奇妙な挙動を示します。例えば、トピックの読み込みが認識されず、「新規」や「未読」に表示され続けてしまいます。

Discourseが大量のファイルを移動しているように見えます。特に READ 操作が増加しています。外部からのトラフィックも増加しているか確認しましたが、増加していません。Discourse とストレージ間のトラフィックのみが影響を受けています。

この挙動は、Discourse 2.4.0.beta9 から 2.4.0.beta10 へのアップグレード後に初めて認識されましたが、それ以前に発生していた可能性もあります。現在は 2.5.0.beta4 を実行しています。

Discourse インストールは、Azure 環境で動作しており、SMBv3 を介してプレミウムストレージが接続されています。通常は非常に良好に動作しています。

何が起きているのか説明していただけないでしょうか?当初は sidekiq ジョブ MigrateUploadScheme が原因ではないかと疑いましたが、このジョブがこれらのトランザクションの原因である場合、もっと頻繁に高負荷が発生するはずです。また、他に原因となり得るジョブは見つかりませんでした。

「バースト IOPS」により、約80万トランザクション/30分というピークが見られます。これらのクレジットが使い果たされると、約25万トランザクション/30分にスロットリングされます。このピークについては、Azure ストレージ階層の制限付き/クレジット付きボーナスに過ぎないため、気にしないでください。

通常は、30分あたり5千〜4万トランザクションです。

この時点で、どこに原因があるのか見当がつかず、アイデアやヒントをいただければ幸いです。

よろしくお願いいたします。
サッシャ

自動バックアップは有効になっていますか?「自動バックアップの有効化」と「バックアップ頻度」のサイト設定を確認してください。

こんにちは。
いいえ、バックアップは完全に無効化されています。PSQL インスタンス自体のバックアップ保持機能と、(自動化されていない)ストレージスナップショットを利用しています。

Postgres で統計機能を有効にして、長時間実行されるクエリや繰り返し実行されるクエリを追跡することは可能ですか?

pg_stat_statements を有効にし、生成される統計データを詳しく調べる必要があります。

統計機能は、ボトルネックを解消するために数週間前に有効化しました。それが私の以前の投稿 Azure PSQL によるデータベースパフォーマンスの推奨事項 のきっかけです。

過去 1 週間の実行時間が最も長いクエリトップ 10 は以下の通りです:

完全なクエリが必要な場合はお知らせください。また、なぜこれがストレージ使用量に影響するのかについても興味深いです。

おそらく最初の完全なクエリです。これは1分間の実行時間で14回実行されており、かなり大規模です。

こんにちは。最初のクエリは DirectoryItem.refresh_period 経由でトリガー/実行されると思います(推測です)。

では、実際のクエリは以下の通りです:

サマリー
WITH x AS (SELECT
			u.id user_id,
			SUM(CASE WHEN p.id IS NOT NULL AND t.id IS NOT NULL AND ua.action_type = 2 THEN 1 ELSE 0 END) likes_received,
			SUM(CASE WHEN p.id IS NOT NULL AND t.id IS NOT NULL AND ua.action_type = 1 THEN 1 ELSE 0 END) likes_given,
			COALESCE((SELECT COUNT(topic_id) FROM topic_views AS v WHERE v.user_id = u.id AND v.viewed_at > '2019-10-28 23:52:24.911261'), 0) topics_entered,
			COALESCE((SELECT COUNT(id) FROM user_visits AS uv WHERE uv.user_id = u.id AND uv.visited_at > '2019-10-28 23:52:24.911261'), 0) days_visited,
			COALESCE((SELECT SUM(posts_read) FROM user_visits AS uv2 WHERE uv2.user_id = u.id AND uv2.visited_at > '2019-10-28 23:52:24.911261'), 0) posts_read,
			SUM(CASE WHEN t2.id IS NOT NULL AND ua.action_type = 4 THEN 1 ELSE 0 END) topic_count,
			SUM(CASE WHEN p.id IS NOT NULL AND t.id IS NOT NULL AND ua.action_type = 5 THEN 1 ELSE 0 END) post_count
			FROM users AS u
			LEFT OUTER JOIN user_actions AS ua ON ua.user_id = u.id AND COALESCE(ua.created_at, '2019-10-28 23:52:24.911261') > '2019-10-28 23:52:24.911261'
			LEFT OUTER JOIN posts AS p ON ua.target_post_id = p.id AND p.deleted_at IS NULL AND p.post_type = 1 AND NOT p.hidden
			LEFT OUTER JOIN topics AS t ON p.topic_id = t.id AND t.archetype = 'regular' AND t.deleted_at IS NULL AND t.visible
			LEFT OUTER JOIN topics AS t2 ON t2.id = ua.target_topic_id AND t2.archetype = 'regular' AND t2.deleted_at IS NULL AND t2.visible
			LEFT OUTER JOIN categories AS c ON t.category_id = c.id
			WHERE u.active
			AND u.silenced_till IS NULL
			AND u.id > 0
			GROUP BY u.id)
	UPDATE directory_items di SET
		 likes_received = x.likes_received,
		 likes_given = x.likes_given,
		 topics_entered = x.topics_entered,
		 days_visited = x.days_visited,
		 posts_read = x.posts_read,
		 topic_count = x.topic_count,
		 post_count = x.post_count
	FROM x
	WHERE
	x.user_id = di.user_id AND
	di.period_type = 5 AND (
	di.likes_received <> x.likes_received OR
	di.likes_given <> x.likes_given OR
	di.topics_entered <> x.topics_entered OR
	di.days_visited <> x.days_visited OR
	di.posts_read <> x.posts_read OR
	di.topic_count <> x.topic_count OR
	di.post_count <> x.post_count )

より良い評価ができるよう、いくつかのコンテキストを提供してもよろしいでしょうか:
当システムには約 43 万人のユーザー、削除されたものを除く 160 万件のトピック(840 万件の投稿)、241 個のカテゴリ、そして 1200 万件の user_actions があります。

しかし、なぜ低速なクエリがストレージ(/uploads)上でこれほど多くの READ 操作を引き起こすのか、まだ理解できていません。何か見落としているのでしょうか?

これは正しくないように思えます。混乱しています。Azure を使用している場合、ファイルをどのように保存しているのでしょうか?これは単一コンテナのセットアップですか?アップロードの設定はどのようになっていますか?

ディレクトリの更新は非常に遅いです。更新コストを負担できない場合は、ディレクトリを無効にできます https://meta.discourse.org/u 。ユーザー検索をフルページ検索に追加する具体的な計画がいくつかありますので、ディレクトリなしで運用することも可能です。

混乱させてしまい申し訳ありません。Discourse の設定方法について、できるだけ詳しく説明させていただきます。

まず、これは単一のコンテナ構成ではありません。Redis、PostgreSQL、ストレージには、Azure の独自サービスを使用するように分割しています。

Discourse + Nginx が動作している VM は 3 台あります。これら 3 台の VM には、SMBv3 を介して個別の Azure ファイル共有 がマウントされており、このマウントポイントはボリュームとして Discourse コンテナに接続されています。
/public/uploads/tmp/javascript-cache/tmp/stylesheet-cache はここに保存されます。

さらに、Azure Cache for RedisAzure Database for PostgreSQL も活用しています。

VM ディスク、ストレージ、データベースは互いに分離されています。そのため、DB の負荷はストレージや VM のパフォーマンスに影響を与えません(与えるべきではありません)。また、これらのサービスの利点(前述の PostgreSQL インスタンスでのデータベース統計やパフォーマンス推奨事項など)を利用できます。

この構成により、各サービスやコンポーネントを個別に監視することも可能です。実際に、uploads が配置されている Azure ファイル共有では、非常に多くのトランザクションが発生していることが確認されています(最初の投稿でご覧いただいた通りです)。これらのトランザクションのほとんどは READ 操作です。

このストレージ(ファイル共有)は Discourse 自体のみで使用されているため、1〜2 日に一度、数分から数時間にわたって発生するこれらのイベントの原因となるプロセスやジョブが何かを特定しようと試みました。

これらの膨大なトランザクション数を除けば、この構成は非常にうまく機能しています。ただし、いくつかのケースではパフォーマンスに影響を与える遅いクエリが一部存在します(例:ユーザーアクティビティサマリーページの読み込みに最大 15 秒かかる場合があります)。

DB パフォーマンスが静的ファイルのトランザクション数にどのように影響するのか、私が疑問に思った理由がお分かりいただけたでしょうか。

これまでのご尽力に感謝し、よろしくお願いいたします。

Sascha

追伸
当社の環境ではカスタム Docker イメージを使用していますが、カスタムソリューションに対するサポートを提供できない、あるいは提供しないことは完全に理解しています。
私たちが知りたいのは、設定全体の処理速度を部分的に低下させているこれらのストレージトランザクション数を引き起こしているプロセス、ジョブ、設定が何か、そしてそれを回避するために何ができるかということです。

パフォーマンスの観点からは、アップロードストレージをS3またはS3互換のストレージエンジンとCDNに切り替えるのが最善策だと考えます。SMB共有でのアップロードはテストしたことがありませんが、アップロードサイズを毎日チェックしていると思われます。ローカルでは非常に高速ですが、SMBでは非常に遅くなるでしょう。

ご clarification とアドバイスありがとうございます。確かに、SMB は多数のファイルにアクセスする場合、非常に遅くなることがあります。ただし、頻繁にアクセスされるファイルは nginx によってキャッシュされるため、通常は問題になりません(Discourse の nginx サンプル設定に変更を加える際も、頻繁に適用しています)。しかし、これらの一時的なアクセス(peeks)においては、パフォーマンスが低下します。

私たちは長らく他のストレージソリューションを検討してきました。外部の S3(互換)ストレージを使用すると、セキュリティ概念の一部が破綻する可能性があります。データベース、VM、ストレージなど、関与するすべてのインスタンスやサービスはプライベートネットワークに紐付けられており、公衆インターネットからはアクセスできません。すべての公開トラフィックは Azure Application Gateway によって管理されています。

残念ながら、Azure Blob Storage は S3 互換ではありませんが、それを利用するために時間を投資する必要があるかもしれません。
現在考えられる解決策は、Discourse Blob Storage プラグインを使用するか、コンテナ内で直接 blobfuse を利用することです。

とにかく、お時間とご支援をいただきありがとうございます。アップロードサイズのチェックが毎日行われる理由と、それを無効にする方法はあるのでしょうか?

よろしくお願いいたします。

これはおそらく、こちらに起因していると思われます:

ホットリンクされた画像の取得を無効にするか、プラグイン内でこの機能を無効化するモンキーパッチを作成することもできると思います:

あるいは、コンテナ設定を修正して、コンテナ内の du コマンドを何もしない(no-op)エイリアスにすることもできます。

ありがとうございます、これは非常に役立ちます。SMB シェアを使用する場合、du コマンドは非常に高コストになる可能性があります。少なくとも、このファイルシェアには約 80 万個のファイル(38GB)がホストされています。

法的・著作権上の問題の可能性により、すでに pulling_hotlinked_images は無効化済みです。

du をエイリアスするのは少し侵入的すぎると思います。プラグインでパッチを当てるのは良いアイデアですが、イメージをビルドする際に git-patch を適用するのはどうでしょうか。例えば以下のようなものです:

def self.used(path)
    output = Discourse::Utils.execute_command('df', '-Pk', path)
    size_line = output.split("\n")[1]
    size_line.split(/\s+/)[2].to_i * 1024
end

du はより信頼性が高く正確かもしれませんが、df でも私たちの要件は満たせるはずです。また、他の機能に影響を与えることもないでしょう。