Огромное количество транзакций хранения

Здравствуйте,

Мы периодически сталкиваемся с огромной нагрузкой на хранилище при выполнении транзакций. Мы не смогли выявить никакого расписания или временного паттерна для этого события, но оно происходит как минимум раз в день. Длительность также варьируется от 10 минут до нескольких часов.
Во время таких нагрузок вся наша установка ведёт себя немного странно: например, чтение тем не распознаётся, поэтому они продолжают отображаться в разделах «Новые» и/или «Непрочитанные».

Кажется, что Discourse перемещает огромные объёмы файлов. В частности, увеличивается количество операций READ. Мы уже проверили, не растёт ли также внешний трафик, но он остаётся неизменным. Затронут только трафик между Discourse и хранилищем.
Мы впервые заметили это поведение после обновления с версии Discourse 2.4.0.beta9 до 2.4.0.beta10, но не уверены, не происходило ли это и раньше. Сейчас мы работаем на версии 2.5.0.beta4.

Наша установка Discourse работает в среде Azure с Premium Storage, подключённым через SMBv3, который обычно функционирует вполне стабильно.

Может ли кто-то объяснить, что происходит? Сначала мы подозревали задание sidekiq MigrateUploadScheme, но если это задание отвечает за такие транзакции, мы должны были бы видеть такую высокую нагрузку гораздо чаще, чем это происходит. Кроме того, мы не нашли других заданий, которые могли бы быть причиной.


Из-за «всплесков IOPS» вы можете видеть этот пик примерно на уровне 800 тыс. транзакций за 30 минут. После исчерпания кредитов нагрузка снижается до ~250 тыс. транзакций за 30 минут. Поэтому, пожалуйста, не обращайте внимания на этот пик — это просто ограниченный/кредитованный бонус уровня хранилища Azure.
Обычно у нас наблюдается от 5 до 40 тыс. транзакций за 30 минут.

На данный момент мы не понимаем, куда смотреть, и будем признательны за любую идею или подсказку.

С уважением,
Саша

Включено ли автоматическое резервное копирование? Проверьте настройки сайта «Включено автоматическое резервное копирование» и «Частота резервного копирования»

Здравствуйте.
Нет, резервное копирование полностью отключено. Мы используем функцию хранения резервных копий самого экземпляра PSQL и (неавтоматические) снимки хранилища.

Можно ли включить статистику в Postgres для поиска долго выполняющихся или повторяющихся запросов?

Вам нужно включить расширение pg_stat_statements и изучить сгенерированную им статистику.

Мы включили сбор статистики некоторое время назад, чтобы устранить узкие места. Именно это стало поводом для моего предыдущего поста Рекомендации по производительности базы данных (от Azure PSQL).

Вот топ-10 самых длительных запросов за прошедшую неделю:

Если вам нужны полные тексты запросов, пожалуйста, дайте знать. Также было бы интересно узнать, почему это влияет на использование хранилища.

Наверное, это точно первый полный запрос, он большой: длительность 1:00 и 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 )

Могу ли я предоставить дополнительный контекст, чтобы вы могли лучше оценить ситуацию:
У нас около 430 тыс. пользователей, 1,6 млн тем (без удалённых) с 8,4 млн сообщений (без удалённых) в 241 категории и 12 млн записей user_actions.

Но я всё ещё не понимаю, почему медленные запросы должны вызывать такое большое количество операций ЧТЕНИЯ на диске (/uploads). Возможно, я что-то упускаю?

Это звучит неправильно. Я в замешательстве: если вы используете Azure, как вы храните файлы? Это настройка с одним контейнером? Как настроена загрузка файлов?

Обновление каталога происходит очень медленно. Если вы не можете позволить себе расходы на его обновление, вы можете отключить каталог https://meta.discourse.org/u. У нас есть очень конкретные планы по добавлению поиска пользователей в полный поиск по страницам, так что вы сможете обойтись без каталога.

Извините за путаницу. Я постараюсь объяснить, как мы настроили Discourse.

Во-первых, это не установка в одном контейнере. Мы разделили компоненты, используя собственные службы Azure для Redis, PostgreSQL и хранилища.

Работают 3 виртуальные машины (ВМ) с Discourse и Nginx. Отдельная Azure File Share монтируется через SMBv3 на этих 3 ВМ, и эта точка монтирования подключается к контейнерам Discourse в качестве тома.
Именно здесь будут храниться /public/uploads, /tmp/javascript-cache и /tmp/stylesheet-cache.

Кроме того, мы используем Azure Cache for Redis и Azure Database for PostgreSQL.

Диски ВМ, хранилище и база данных разделены друг от друга. Поэтому нагрузка на БД не должна (в идеале) влиять на производительность хранилища или ВМ, и мы можем воспользоваться преимуществами этих служб (например, статистикой базы данных на экземпляре PostgreSQL, о котором вы упоминали выше, и рекомендациями по производительности).

Такая настройка также позволяет нам мониторить каждую службу/часть отдельно, и мы заметили, что наше Azure File Share, где находятся uploads, получает очень большое количество транзакций (как вы можете видеть в моём первом сообщении). Эти транзакции в основном являются операциями ЧТЕНИЕ.
Поскольку это хранилище (File Share) используется только самим Discourse, мы пытались выяснить, какой процесс/задача отвечает за эти события, которые происходят 1–2 раза в день в течение нескольких минут или даже нескольких часов.

Помимо этих огромных количеств транзакций, эта настройка работает довольно хорошо, за исключением некоторых медленных запросов, которые влияют на производительность лишь в нескольких случаях (например, страницы с краткой сводкой активности небольшого количества пользователей могут загружаться до 15 секунд).

Надеюсь, я объяснил, почему меня удивило, как производительность БД может влиять на количество транзакций статических файлов.

С уважением и спасибо за ваши усилия до сих пор,
Саша

P.S.
Мы используем кастомный Docker-образ в нашей настройке, и я полностью понимаю, что вы не можете/не будете оказывать поддержку для кастомных решений.
Единственное, что мы хотели бы знать, — какой процесс/задача/настройка может вызывать эти количества транзакций хранилища, которые частично замедляют всю настройку, и что мы можем сделать, чтобы избежать этого.

С точки зрения производительности, я думаю, лучше всего переключить хранилище для загрузки на S3 или совместимый с S3 движок хранения + CDN. Использование SMB-шара для загрузок мы никогда не тестировали. Мой предположение, что мы проверяем размер загрузок ежедневно: на локальной системе это происходит мгновенно, а на SMB — очень медленно.

Спасибо за уточнения и ваши советы. Действительно, SMB может работать очень медленно при доступе к большому количеству файлов. В большинстве случаев это не имеет значения, поскольку часто используемые файлы кэшируются nginx (мы регулярно применяем изменения, внесённые в пример конфигурации nginx для Discourse).
Однако при выполнении таких проверок производительность падает.

Мы уже некоторое время ищем другие решения для хранения данных. Использование внешнего S3-совместимого хранилища могло бы может нарушить часть нашей концепции безопасности. Каждое участвующее в системе звено (база данных, виртуальная машина, хранилище и т. д.) привязано к частной сети и недоступно из общедоступного интернета. Весь публичный трафик управляется через Azure Application Gateway.
К сожалению, Azure Blob Storage не совместим с S3, но, возможно, нам стоит потратить время на его использование.
Возможные текущие решения — это плагин Discourse Blob Storage или прямое использование blobfuse внутри контейнера.

В любом случае, спасибо за ваше время и помощь. Есть ли причина, по которой проверка размера загрузки выполняется ежедневно, и можно ли её отключить?

С уважением,

Скорее всего, это происходит отсюда:

Думаю, вы можете отключить загрузку горячих ссылок на изображения или создать патч-заглушку (monkey patch) в плагине, который отключит это:

Или просто задайте алиас du в вашем контейнере как операцию без действия, изменив конфигурацию контейнера.

Спасибо большое, это очень поможет. Использование du через SMB-раздел будет довольно затратным, учитывая, что мы храним около 800 тысяч файлов (38 ГБ) на этом файловом ресурсе.

Мы уже отключили 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 подойдет для наших нужд и не нарушит другую функциональность.