В консоли администратора появилось предупреждение, касающееся наших очередей задач Sidekiq. У нас почти 200 000 задач в очереди, и мы не знаем, как решить эту проблему. Мы используем версию 3.2.0.
Все задачи находятся в очереди ultra_low и, судя по всему, относятся к задаче Jobs::ProcessPost. Они все выглядят примерно так:
Два дня назад было около 195 тыс., а сейчас выросло до 211 тыс. Очень нужна ваша помощь: на что обратить внимание и как решить эту проблему.
Мы заметили 5 неудачных заданий, которые выглядят так (IP-адреса скрыты):
Jobs::HandledExceptionWrapper: Wrapped Errno::ENETUNREACH: Failed to open TCP connection to x.x.x.x:443 (Network is unreachable - connect(2) for "x.x.x.x" port 443)
Этот адрес принадлежит серверу в нашей инфраструктуре, но он не используется форумом (это сервер репозитория исходного кода). Я нигде в конфигурации не вижу упоминания этого хоста, поэтому не понимаю, почему система считает, что нужно подключаться к нему. Но, возможно, это связано с растущим количеством заданий в очереди ultra_low?
За последние ~23 часа в очередь добавлено ещё 7000 задач.
Это также сдерживает внедрение нескольких изменений: автоматическое закрытие тем и добавление плагина с решениями (просто не хочется вносить изменения, которые могут или не могут быть затронуты этим, или когда это может усугубиться из-за внесения столь значительных изменений в настройку системы).
Добавленные 7 тысяч задач не соответствуют количеству новых сообщений за последний день, поэтому я не понимаю, что вообще является причиной этого.
Я не могу помочь, кроме как предложить, что больше ответов и лайков к сообщениям в этой теме могут привлечь к ней внимание, которого она, кажется, заслуживает!
Редакция: Я также упомянул эту проблему в теме о порядке сортировки по умолчанию «по популярности», что, на мой взгляд, очень эффективно и крайне нецелесообразно скрывает эту тему.
Я предполагаю, что это нужно сделать через консоль — мне придется попросить кого-то с соответствующим доступом сделать это. Можете подсказать, откуда это можно получить, чтобы я мог передать эту информацию человеку с доступом?
Ах, спасибо за подсказку. Вот что показывается (IP-адрес замазан):
Сообщение (10 копий в отчёте)
Исключение в задаче: Не удалось установить TCP-соединение с x.x.x.x:443 (Сеть недоступна — connect(2) для "x.x.x.x" порт 443)
Стек вызовов
/usr/lib64/ruby/gems/3.2.0/gems/net-http-0.4.1/lib/net/http.rb:1603:in `initialize'
/usr/lib64/ruby/gems/3.2.0/gems/net-http-0.4.1/lib/net/http.rb:1603:in `open'
/usr/lib64/ruby/gems/3.2.0/gems/net-http-0.4.1/lib/net/http.rb:1603:in `block in connect'
/usr/lib64/ruby/gems/3.2.0/gems/timeout-0.4.1/lib/timeout.rb:186:in `block in timeout'
/usr/lib64/ruby/gems/3.2.0/gems/timeout-0.4.1/lib/timeout.rb:193:in `timeout'
/usr/lib64/ruby/gems/3.2.0/gems/net-http-0.4.1/lib/net/http.rb:1601:in `connect'
/srv/www/vhosts/discourse/lib/final_destination/http.rb:27:in `block in connect'
/srv/www/vhosts/discourse/lib/final_destination/http.rb:17:in `each'
/srv/www/vhosts/discourse/lib/final_destination/http.rb:17:in `each_with_index'
/srv/www/vhosts/discourse/lib/final_destination/http.rb:17:in `connect'
Я заметил, что это отличается от вывода во вкладке «backtrace», но тот вывод не похож на стек вызовов, тогда как то, что здесь скопировано (с помощью кнопки копирования), — это именно он. Дайте знать, если для этого случая потребуется дополнительная информация.
Также я попробую получить больше информации о 220+ тысячах задач в очереди таким же образом — я не знал о наличии конечной точки /logs.
Вот что успел скопировать кнопка «Копировать» — здесь полный вывод из вкладки трассировки:
net-http-0.4.1/lib/net/http.rb:1603:in `initialize'
net-http-0.4.1/lib/net/http.rb:1603:in `open'
net-http-0.4.1/lib/net/http.rb:1603:in `block in connect'
timeout-0.4.1/lib/timeout.rb:186:in `block in timeout'
timeout-0.4.1/lib/timeout.rb:193:in `timeout'
net-http-0.4.1/lib/net/http.rb:1601:in `connect'
/srv/www/vhosts/discourse/lib/final_destination/http.rb:27:in `block in connect'
/srv/www/vhosts/discourse/lib/final_destination/http.rb:17:in `each'
/srv/www/vhosts/discourse/lib/final_destination/http.rb:17:in `each_with_index'
/srv/www/vhosts/discourse/lib/final_destination/http.rb:17:in `connect'
net-http-0.4.1/lib/net/http.rb:1580:in `do_start'
net-http-0.4.1/lib/net/http.rb:1569:in `start'
net-http-0.4.1/lib/net/http.rb:1029:in `start'
/srv/www/vhosts/discourse/lib/final_destination.rb:551:in `safe_session'
/srv/www/vhosts/discourse/lib/final_destination.rb:486:in `safe_get'
/srv/www/vhosts/discourse/lib/final_destination.rb:170:in `get'
/srv/www/vhosts/discourse/lib/retrieve_title.rb:90:in `fetch_title'
/srv/www/vhosts/discourse/lib/retrieve_title.rb:12:in `crawl'
/srv/www/vhosts/discourse/lib/inline_oneboxer.rb:76:in `lookup'
/srv/www/vhosts/discourse/lib/cooked_processor_mixin.rb:310:in `process_inline_onebox'
/srv/www/vhosts/discourse/lib/cooked_processor_mixin.rb:39:in `block in post_process_oneboxes'
/srv/www/vhosts/discourse/lib/oneboxer.rb:214:in `block in apply'
/srv/www/vhosts/discourse/lib/oneboxer.rb:162:in `block in each_onebox_link'
nokogiri-1.16.0/lib/nokogiri/xml/node_set.rb:235:in `block in each'
nokogiri-1.16.0/lib/nokogiri/xml/node_set.rb:234:in `upto'
nokogiri-1.16.0/lib/nokogiri/xml/node_set.rb:234:in `each'
/srv/www/vhosts/discourse/lib/oneboxer.rb:162:in `each_onebox_link'
/srv/www/vhosts/discourse/lib/oneboxer.rb:213:in `apply'
/srv/www/vhosts/discourse/lib/cooked_processor_mixin.rb:9:in `post_process_oneboxes'
/srv/www/vhosts/discourse/lib/cooked_post_processor.rb:42:in `block in post_process'
/srv/www/vhosts/discourse/lib/distributed_mutex.rb:53:in `block in synchronize'
/srv/www/vhosts/discourse/lib/distributed_mutex.rb:49:in `synchronize'
/srv/www/vhosts/discourse/lib/distributed_mutex.rb:49:in `synchronize'
/srv/www/vhosts/discourse/lib/distributed_mutex.rb:34:in `synchronize'
/srv/www/vhosts/discourse/lib/cooked_post_processor.rb:38:in `post_process'
/srv/www/vhosts/discourse/app/jobs/regular/process_post.rb:28:in `block in execute'
/srv/www/vhosts/discourse/lib/distributed_mutex.rb:53:in `block in synchronize'
/srv/www/vhosts/discourse/lib/distributed_mutex.rb:49:in `synchronize'
/srv/www/vhosts/discourse/lib/distributed_mutex.rb:49:in `synchronize'
/srv/www/vhosts/discourse/lib/distributed_mutex.rb:34:in `synchronize'
/srv/www/vhosts/discourse/app/jobs/regular/process_post.rb:8:in `execute'
/srv/www/vhosts/discourse/app/jobs/base.rb:297:in `block (2 levels) in perform'
rails_multisite-5.0.0/lib/rails_multisite/connection_management.rb:82:in `with_connection'
/srv/www/vhosts/discourse/app/jobs/base.rb:284:in `block in perform'
/srv/www/vhosts/discourse/app/jobs/base.rb:280:in `each'
/srv/www/vhosts/discourse/app/jobs/base.rb:280:in `perform'
sidekiq-6.5.12/lib/sidekiq/processor.rb:202:in `execute_job'
sidekiq-6.5.12/lib/sidekiq/processor.rb:170:in `block (2 levels) in process'
sidekiq-6.5.12/lib/sidekiq/middleware/chain.rb:177:in `block in invoke'
/srv/www/vhosts/discourse/lib/sidekiq/pausable.rb:132:in `call'
sidekiq-6.5.12/lib/sidekiq/middleware/chain.rb:179:in `block in invoke'
sidekiq-6.5.12/lib/sidekiq/middleware/chain.rb:182:in `invoke'
sidekiq-6.5.12/lib/sidekiq/processor.rb:169:in `block in process'
sidekiq-6.5.12/lib/sidekiq/processor.rb:136:in `block (6 levels) in dispatch'
sidekiq-6.5.12/lib/sidekiq/job_retry.rb:113:in `local'
sidekiq-6.5.12/lib/sidekiq/processor.rb:135:in `block (5 levels) in dispatch'
sidekiq-6.5.12/lib/sidekiq/rails.rb:14:in `block in call'
activesupport-7.0.8/lib/active_support/execution_wrapper.rb:92:in `wrap'
activesupport-7.0.8/lib/active_support/reloader.rb:72:in `block in wrap'
activesupport-7.0.8/lib/active_support/execution_wrapper.rb:92:in `wrap'
activesupport-7.0.8/lib/active_support/reloader.rb:71:in `wrap'
sidekiq-6.5.12/lib/sidekiq/rails.rb:13:in `call'
sidekiq-6.5.12/lib/sidekiq/processor.rb:131:in `block (4 levels) in dispatch'
sidekiq-6.5.12/lib/sidekiq/processor.rb:263:in `stats'
sidekiq-6.5.12/lib/sidekiq/processor.rb:126:in `block (3 levels) in dispatch'
sidekiq-6.5.12/lib/sidekiq/job_logger.rb:13:in `call'
sidekiq-6.5.12/lib/sidekiq/processor.rb:125:in `block (2 levels) in dispatch'
sidekiq-6.5.12/lib/sidekiq/job_retry.rb:80:in `global'
sidekiq-6.5.12/lib/sidekiq/processor.rb:124:in `block in dispatch'
sidekiq-6.5.12/lib/sidekiq/job_logger.rb:39:in `prepare'
sidekiq-6.5.12/lib/sidekiq/processor.rb:123:in `dispatch'
sidekiq-6.5.12/lib/sidekiq/processor.rb:168:in `process'
sidekiq-6.5.12/lib/sidekiq/processor.rb:78:in `process_one'
sidekiq-6.5.12/lib/sidekiq/processor.rb:68:in `run'
sidekiq-6.5.12/lib/sidekiq/component.rb:8:in `watchdog'
sidekiq-6.5.12/lib/sidekiq/component.rb:17:in `block in safe_thread'
Извините, что не скопировал весь вывод. Мне показалось странным, что кнопка «Копировать» не захватила больше, но я ошибочно предположил, что она скопировала всё необходимое.
Это означает, что есть одна или несколько встроенных ссылок с URL-адресом, который указывает на IP-адрес вашего сервера. Если этот сервер недоступен, мы записываем ошибку в лог, но процесс обработки Markdown должен продолжаться без выполнения «улучшения ссылок», которое обычно запускает встроенный onebox.
Ещё один момент: как именно вы установили Discourse? Похоже, что установка не соответствовала нашему официальному руководству по установке.
Я на самом деле не выполнял установку — полагаю, наша команда инфраструктуры развернула её через конвейер CI/CD, но я не знаю деталей.
Похоже, что сообщения об ошибках не являются проблемой — большая очередь задач в очереди ultra_low выглядит как более серьёзная проблема. В логах ничего не найдено (что, впрочем, логично, поскольку задача ещё не была выполнена).
Я не вижу опции, чтобы принудительно запускать их в веб-интерфейсе — у меня есть возможность «показать все» аргументы или удалять задачи по отдельности.
Не переживайте — да, они отображаются на странице «В очереди» (по адресу /sidekiq/queues/ultra_low).
Посмотрев на саму страницу очередей, я вижу, что задержка очереди ultra_low составляет около года. Похоже, это происходило уже давно, и мы лишь недавно получили уведомление об этом в панели управления.
Погрузившись чуть глубже в то, что я вижу в задачах Jobs::ProcessPost, обнаружил, что значения post_id идут в обратном порядке. Я извлек ID постов из первой и последней задач в очереди: даты соответствуют посту 2012 года (до миграции на Discourse, но с меткой времени ‘updated_at’ из 2022 года, что, возможно, совпадает с датой нашей миграции — это нужно будет проверить) и самому последнему посту от января 2023 года.
Я также иногда вижу задачи для генерации миниатюр тем, но они встречаются довольно редко. При более чем 8 800 страницах очереди я не могу проверить всё, но похоже, что в очередь добавляются в основном задачи типа Jobs::ProcessPost, которые обрабатывают ответы и исходные посты тем, двигаясь назад во времени (самый ранний пост был в середине обсуждения, а самый поздний — корневым постом темы).
Я сделал резервную копию из производственной системы и загрузил её в тестовую среду, настроенную локально. Очередь, похоже, вообще не резервируется — более того, я не вижу, чтобы Job::ProcessPost появлялся в этой очереди, пока наблюдаю за ней (я настрою запись экрана очереди и оставлю систему работать самостоятельно, чтобы проверить, не упускаю ли я момент её появления).
Это приводит меня к двум вопросам:
Что вызывает запуск задания ProcessPost?
Что может помешать обработке очереди ultra_low?
Я не эксперт в технологиях, используемых в Discourse (redis, sidekiq или rails), но мне комфортно учиться и пробовать различные варианты в изолированной тестовой среде, чтобы понять, на что именно нужно обратить внимание в производственной среде.
Хорошая новость в том, что эта проблема, похоже, пока не вызывает никаких затруднений у наших пользователей. Третий вопрос: поможет ли просто очистка этой очереди, или же запрет на выполнение этих заданий может как-то навредить системе.
Дополнительно: я только что увидел, как в тестовой среде прошла партия заданий, и они там обрабатываются. Значит, проблема, скорее всего, в том, что на производственном сервере очередь по какой-то причине не обрабатывается.
Похоже, мы пришли к решению. Используемая нами установка основана на пакете, созданном для openSUSE (речь идёт об экземпляре, о котором мы говорили, — это экземпляр для форумов openSUSE), поэтому по сути используется процесс установки «сборка из исходного кода».
У нас есть среды разработки и производства, и очередь ultra_low была ошибочно исключена из конфигурации sidekiq для производственной среды. Это исправлено, и похоже, что очередь опустошается, а задержка снизилась с одного года до четырёх недель.
Я считаю, что теперь вопрос можно считать решённым. Благодарю всех, кто предоставил свои замечания — это помогло мне понять суть проблемы.