Планирование фоновых заданий без дубликатов - PullRequest
0 голосов
/ 03 декабря 2018

У нас есть приложение rails, которое синхронизируется с другим приложением.Это происходит на заднем плане.По сути, каждый раз, когда эта работа просто синхронизирует все данные, поэтому в настоящий момент она действительно медленная, и мы ищем ускорение процесса с помощью параллелизма.

В основном это выглядит примерно так:

accounts.each { |a| sync_account(a) }

И мы хотим, чтобы это выглядело так:

accounts.each { |a| SyncAccountJob.perform_later(a) }

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

Например, если мы планируем задания каждый час, иногда, когда какая-то учетная запись еще не синхронизирована, будет запланировано новое задание (извините замой английский).

Что бы вы сделали?

Мы думаем, что мы должны просто сохранить идентификатор созданной работы в таблице счетов и просто проверить, не существует ли работа, прежде чем планировать ее снова.

Другой вопрос - какую систему мы используем: delayed_job (уже используется почтовыми программами) или sidekiq?

Другая проблема: задания "зомби".Например, допустим, я запланировал какое-то задание (delayed_job), и работник начал его обрабатывать.Теперь он заблокирован.И затем сервер падает, так что работа все еще заблокирована, но ничего не обрабатывает.Неужели delayed_job / sidekiq решает эту проблему самостоятельно или мне стоит написать что-нибудь почище?

Буду признателен за любые комментарии или истории на эту тему.

Ответы [ 3 ]

0 голосов
/ 03 декабря 2018

Перво-наперво, вы ускоряете процесс, используя асинхронизм , а не параллелизм , небольшая разница.:)

Во-вторых, кажется, что есть три основные проблемы, которые вы хотите решить:

  1. Поставить в очередь задание для каждой учетной записи.
  2. Убедитесь, что только однауникальная работа всегда стоит в очереди.
  3. Старайтесь избегать долгоживущих работ.

Исторически я использовал Resque для такого рода вещей, но я уверен, что есть много альтернатив.

Вы бы сделали что-то вроде:

accounts.each { |a| Resque.enqueue(SyncAccount, a) }

Чтобы убедиться, что они будут работать в какой-то момент в будущем, вы можете посмотреть, используя cron или планировщик resque .

Чтобы обеспечить уникальность заданий, вы можете использовать какой-то слой кэширования, например Redis , для которого вы сохраняете выходные данные хеш-функции , который принимает некоторые аргументы, связанные с учетной записью, которую вы используете для создания заданий, которые вы запрашиваете до постановки в очередь, и пишете в redis после завершения задания.

Во избежание зомбиДжобс, большинство я бы рекомендовал обернуть вашу логику работы в разумных пределахe блоки времени ожидания , и да, используйте какое-то средство для очистки, чтобы убирать мертвые задания из очереди.

0 голосов
/ 06 декабря 2018

Посмотрим.

  • Задержка задания или Sidekiq: Это зависит от характера вашего приложения.Поскольку у вас уже есть бэкэнд-система для организации очередей заданий, вы можете очень хорошо ее использовать.В каждой системе есть различия (положительные и отрицательные), поэтому, в конечном итоге, это зависит от вашего выбора.В качестве примера, если ваше приложение сильно загружает базу данных, тогда лучше избегать delayed_jobs.

  • Один диджей для каждого аккаунта: я бы сделал это.

i) Добавьте столбец в таблицу ваших счетов.Скажите «sync_status».Перед тем как поставить в очередь задание на синхронизацию, установите статус как «in_progress».

ii) После этого напишите настраиваемое задание для синхронизации.Это не должно быть сложно, так как у вас уже есть код бизнес-логики.После завершения синхронизации вы можете изменить статус на «выполнено» или вернуться к «готово».

iii) Таким образом, вы можете поставить в очередь задание, только если 'sync_status' для этой учетной записи выполнено / готово.

Пример:

Delayed::Job.enqueue(CustomSyncJob.new()) if account.ready_to_sync? 

В пределах custom_sync.rb, в конце:

account.status = 'ready'
account.save
  • Обработка сигналов: Ваше приложение никогда не должно аварийно завершать работу, и ваш код должен это гарантировать.Но чтобы убить DJ изящно, вы можете добавить следующую настройку:

    Delayed :: Worker.raise_signal_exceptions =: term

Это вызовет исключение SignalException.Ди-джей изящно справится с этим, очистив столбец locked_by.

Надеюсь, это поможет.Приветствия.

0 голосов
/ 03 декабря 2018

если мы планируем работу каждый час

В этом случае вы можете использовать sidekiq-cron .Это гарантирует, что ни одно и то же задание не будет запущено одновременно.Конечно, подход с сохранением ID тоже подойдет.

Что касается заданий по зомби - ИМХО, это не должно быть большой проблемой.Ваш сервер не падает регулярно, не так ли?В случае каких-либо проблем вы всегда можете очистить вещи в веб-интерфейсе или консоли.

...