Попытка обойти 10-минутную задержку

Я планирую использовать плагин wp-discourse на сайте прогноза погоды на побережье Мексиканского залива weather forecasting site, который обычно обслуживает около 10–20 тысяч просмотров страниц в день, но во время сильных погодных явлений иногда достигает 1,5–2 миллионов просмотров в день для примерно 500–700 тысяч посетителей. Сайт и стратегия его хостинга прошли проверку в ходе нескольких сильных погодных явлений, и всё работает отлично даже под нагрузкой, в основном благодаря тщательному проектированию и большой помощи от Cloudflare.

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

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

После долгих попыток использовать встроенные настройки, чтобы посты появлялись мгновенно без вмешательства кэширования nginx fastcgi, WordPress или браузера, которое мешало новым комментариям отображаться после обновления страницы, я добавил следующие два mu-плагина, чтобы смягчить эту проблему и обеспечить отображение недавно опубликованных комментариев на стороне WordPress при обновлении страницы:

wp-discourse-transient-killer.php
wp-discourse-cache-header-fix.php

Это решило мою проблему: новые посты в потоках Discourse, созданных через WordPress, теперь мгновенно появляются под постами WordPress при обновлении страницы.

Но я, честно говоря, на пределе своих компетенций здесь — что я ломаю/портю/подрываю, делая это?

Меня не особо беспокоит создание дополнительной нагрузки на моего хостинг-провайдера из-за того, что конечная точка комментариев будет подвергаться интенсивным запросам от посетителей, проверяющих страницу ежедневного прогноза погоды (с встроенными комментариями Discourse) — это проблема, которую я могу решить, вложив деньги. Моя главная цель — избежать 20 000+ писем от пользователей с вопросом, почему их комментарии не появляются мгновенно на главной странице после публикации.

Является ли это правильным подходом? Разумно ли то, что я делаю? Не создаёт ли это дополнительных проблем с безопасностью или производительностью, которые я не учёл? В общем, портю ли я что-то, делая это?

Спасибо :slight_smile:

Хм, я не понимаю, почему это работает, потому что

А ваш плагин

add_action( 'wpdc_after_webhook_post_update', function( $topic_ids ) {
    foreach ( (array)$topic_ids as $topic_id ) {
        delete_transient( 'wpdc_comment_html_' . $topic_id );
    }
}, 11 );

Разве вы не путаете переданные в действие идентификаторы записей WordPress ($post_id) с идентификатором темы Discourse (topic_id), который используется как ключ для транзита?

Я бы ожидал, что ваш плагин будет выглядеть так

add_action( 'wpdc_after_webhook_post_update', function( $post_ids) {
    foreach ( (array)$post_ids as $post_id ) {
        $topic_id = get_post_meta( $post_id, 'discourse_topic_id', true );
        delete_transient( 'wpdc_comment_html_' . $topic_id );
    }
}, 11 );

С другой стороны, вы утверждаете, что это работает? :thinking:

Привет @Lee_Ars, не могли бы вы сначала подтвердить, что у вас настроен вебхук для комментариев?

Это работает следующим образом:

  1. В Discourse появляется новая публикация.
  2. На WordPress отправляется полезная нагрузка вебхука.
  3. Плагин WP Discourse обновляет количество комментариев в публикации и устанавливает пользовательское поле публикации wpdc_sync_post_comments.
  4. Если wpdc_sync_post_comments установлено, комментарии Discourse будут синхронизироваться при загрузке публикации в WordPress, независимо от периода синхронизации (то есть 10-минутной задержки).

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

Привет, Энгус! Да, подтверждаю: опция вебхука «Синхронизация данных комментариев» включена на стороне WP, и я создал вебхук на стороне Discourse — пинги проходят успешно. Логи плагина WP показывают сообщения comment.INFO: sync_comments.success в нужное время:

[2025-07-07 14:16:38] connection.INFO: check_connection_status.successful_connection  
[2025-07-07 14:16:38] connection.INFO: check_connection_status.valid_scopes  
[2025-07-07 20:11:31] comment.INFO: sync_comments.success {"post_id":786} 
[2025-07-07 20:25:03] comment.INFO: sync_comments.success {"post_id":786} 
[2025-07-07 20:32:14] comment.INFO: sync_comments.success {"post_id":786} 
[2025-07-07 20:44:15] comment.INFO: sync_comments.success {"post_id":786} 
[2025-07-07 21:00:39] comment.INFO: sync_comments.success {"post_id":786} 
[2025-07-07 21:01:42] comment.INFO: sync_comments.success {"post_id":786} 
[2025-07-07 21:15:40] comment.INFO: sync_comments.success {"post_id":786} 

Проблема в том, что даже при наличии этих сообщений об успехе, существующие посетители (или, по крайней мере, я — многократно тестируя на Firefox/Safari/Chrome на Mac, Firefox/Chrome/Edge на ПК с Windows 10 и Safari на iOS) продолжают получать закэшированный эндпоинт /wp-json/wp-discourse/v1/discourse-comments, где заголовок cache-control установлен в ненулевое значение. Если я нажимаю Ctrl+Shift+F5 в Chrome (или аналог в других браузерах), чтобы принудительно обойти локальный кэш при обновлении, всё работает отлично, и новые посты появляются.

При наличии mu-плагинов этот эндпоинт показывает cache-control со значением no-store no-cache и т.п., и проблема не проявляется — достаточно просто открыть пост на WP или обновить его обычной кнопкой обновления, и новые встроенные комментарии отображаются.

Я включил подробное логирование вебхуков и создал тестовый пост; при создании нового поста всё выглядит нормально:

webhook_topic.INFO: update_topic_content.update_post_metadata_success {"post_ids":"786"}

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

Ой, извините, вы наверняка правы — я действительно где-то накосячил. Вчерашний день был долгим, и, как я уже говорил, я, пожалуй, достиг предела своих возможностей в этом вопросе. Хотя определённо что-то происходило, теперь я сомневаюсь, не является ли то поведение, которое я называю «исправленным», просто новой формой ошибки. (Я обновил плагин «Убийца транзиентов», чтобы использовать правильный аргумент, спасибо!)

Спасибо, осталось уточнить ещё один момент. Включена ли эта настройка WP Discourse (в разделе «Комментирование»)?

Я пробовал как с включённой, так и с выключенной настройкой Cache Comment HTML, но это, похоже, никак не влияет на проблему. Сейчас у меня она выключена включена (извините, я ошибся: пока я продолжаю искать причину, она включена), но я могу изменить её на любое значение, которое поможет в диагностике.

Если настройка отключена, вы не заметите путаницы с topic_id / post_id, так как в этом случае плагин ничего не делает. Отсутствие кэширования — неважно, если вы удалите неправильный кэш.

Если настройка включена, вы должны заметить, что плагин работает неправильно.

То есть, если вы хотите устранить неполадки, вам следует включить эту настройку.

Хорошо, в качестве первой меры для вашего случая я бы предложил:

  1. оставить отключённой опцию «Cache Comment HTML»; и
  2. удалить плагин https://www.bigdinosaur.org/r/wp-discourse-transient-killer.txt, так как, как отмечает Ричард, в настоящее время он ничего не делает.

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

  1. Какие решения кэширования вы (и/или ваш хостинг-провайдер) используете для WordPress?
  2. Если файл https://www.bigdinosaur.org/r/wp-discourse-cache-header-fix.txt решает проблему, каким именно образом его специфическое поведение инвалидирует те кэши, которые применяются в пункте 1?

Смотря на wp-discourse-cache-header-fix, я вижу, что одно из исправлений касается load-comments.js. У вас включена эта настройка?

Это самохостинговый WP на nginx+php-fpm 8.3 с кэшированием fast-cgi в nginx для динамического контента и кэшированием объектов Redis (с активным drop-in для кэша объектов). Других уровней нет (нет CDN, нет Cloudflare, нет Varnish на сервере или другого локального кэша помимо fast-cgi кэша nginx). Очистка кэша fast-cgi nginx (агрессивно, через rm -rf /etc/nginx/cache/*) не влияет на проблемное поведение — устаревшие результаты всё равно отдаются даже после полной очистки каталога кэша и перезапуска как nginx, так и php-fpm.

Да, у меня действительно включена загрузка комментариев через Ajax, но, опять же, отключение этой опции (плюс очистка кэша nginx и перезапуск nginx и php-fpm на всякий случай) не повлияло на проблемное поведение. Браузеры всё равно получали устаревшие комментарии.

Опция переключена, плагин transient-killer удалён. Проблемное поведение не изменилось.

Применяемый эффект заключается в том, что возвращается заголовок cache-control со значением no-cache вместо заголовка с указанным временем кэширования. Без этого заголовка мой браузер явно стремится отдать устаревшую кэшированную версию с диска для конечной точки wp-json/wp-discourse/v1/discourse-comments; как отмечалось, мне приходится использовать Shift+Ctrl+F5 (или аналогичную комбинацию), чтобы принудительно выполнить обновление без кэша.

Проблемное поведение, похоже, находится на стороне браузера, а не в постоянном кэше сервера. Просто каждый браузер на каждой ОС, к которой у меня есть доступ, ведёт себя именно так.

Хорошо, просто чтобы я был на 100% уверен: когда у вас:

  • проверенный рабочий вебхук комментариев
  • отключено кэширование HTML комментариев
  • отключена AJAX-загрузка
  • нет CDN
  • нет CloudFront
  • нет плагина кэширования WordPress
  • нет соответствующих предупреждений или ошибок в логах PHP

вы уверены, что это не работает?

Если при такой настройке это не работает, без более детального изучения я немного теряюсь и склоняюсь к варианту:

  • включить AJAX-загрузку
  • применить ваши исправления из wp-discourse-cache-header-fix.php

что, как я подозреваю, у вас работало. Если этот вариант сработает, то вам стоит остановиться на нём.

Вот быстрая галерея на Imgur со скриншотами моих текущих настроек плагинов для справки.

Подтверждаю: нет CDN, нет Cloudfront или Cloudflare, нет плагинов кэширования, кроме Nginx helper (чтобы WP мог при необходимости инвалидировать кэш nginx fast-cgi).

Также подтверждаю, что в логах ошибок php-fpm или nginx нет ничего относящегося к делу.

Боже, дружище, я бы тоже так хотел. Я уже около 30 часов бьюсь головой об стену с этим, делая перерывы только на сон. Может, я уже немного запутался, хех.

Да, я тебя понимаю. Возьми паузу на день-другой. Завтра я попробую воссоздать проблему, скопировав твою настройку.

Если я не смогу решить проблему методом проб и ошибок, и если вы согласны, я с радостью предоставлю временный локальный доступ (к блогу WordPress, к Discourse и/или к соответствующим хостам) завтра или послезавтра для отладки. Конечно, я не пытаюсь получить бесплатную работу или что-то в этом роде. Я с удовольствием оплачу ваши услуги, если потребуется реальное время на работу.

Для справки: оба хоста — один с WordPress, другой с Discourse — это экземпляры EC2 типа t3a.large, работающие на образе AWS Ubuntu 24.04 AMI. Установленное ПО минимально: на веб-хосте — nginx, php8, fpm и redis; на хосте Discourse — только git, docker и сам Discourse. Это мой личный блог, и я тестирую интеграцию комментариев здесь, прежде чем внедрить её в продакшн на значительно более крупном сайте WordPress, упомянутом в исходном сообщении. (Я также поддерживаю этот сайт, и у него аналогичный технологический стек, хотя он находится за Cloudflare. Разберусь с этим, когда дойдём до этого момента!)

Если вам понадобятся или вы захотите получить дополнительные сведения о технологическом стеке, я с радостью предоставлю их.

Хорошо, вот видео, где я пытаюсь (и не могу) воспроизвести вашу проблему:

Следующее, что я хочу, чтобы вы попробовали — этот фильтр.

Спасибо @angus — то, что воспроизвести не удалось, на самом деле меня успокаивает, ведь это значит, что я что-то делаю не так, а не то что проблема действительно существует :smiley:

Сегодня, как только у меня появится время на отладку, я добавлю этот фильтр и отпишусь :+1:

Отвечаю с задержкой в месяц с небольшим, чтобы закрыть тему — у меня всё ещё наблюдаются некоторые странности при развёртывании на полную продакшн-среду (продакшн-сайт, пример поста на продакшн-сайте с встраиванием Discourse, реальный форум Discourse), но всё это управляемо. Я склонен списывать любые оставшиеся проблемы с задержками на сложную многослойную структуру, состоящую из WordPress + Discourse + Cloudflare, а также на мою агрессивную стратегию кэширования, позволяющую поддерживать работу всех этих компонентов во время всплесков трафика, вызванных реальными штормами.

Спасибо, что нашли время ответить, @angus <3

Приношу извинения, что снова поднимаю эту тему, но хотел уточнить и сообщить, что я нашёл причину проблемы! И, как обычно, это была моя собственная вина.

Кратко: я храню общие настройки блока location для PHP в сниппете в /etc/nginx/snippets/, чтобы подключать их в несколько файлов vhost без дублирования. Прошло очень много времени (вероятно, годы), с тех пор как я последний раз проверял этот сниппет, и, как оказалось, там был лишняя строка add_header Cache-Control "public, max-age=7200";, которая применялась ко всему, что выходило из этого блока location.

Поэтому я удалил её, очистил все уровни кэша, и, о чудо, проблема исчезла.

Ещё раз спасибо, что нашли время помочь мне, @angus, даже несмотря на то, что в итоге это снова оказалась ещё одна проблема Discourse, которую я создал сам :people_hugging: