Rails 5, начальная нить из инициализатора сошла с ума
У меня есть следующий код в config/application.rb
:
config.after_initialize do
MyConcern.init_processing
end
Тогда следующие родственные методы:
def init_processing
@queue_processor = Thread.new do
process_op_queue
end
@queue_processor.name = "Some Queue"
# @queue_processor_notifier = Queue.new
end
def process_op_queue
while true
req = MyRequest.where(done: Helper::FALSE_NIL_OR_MISSING, started_at: nil).order_by(id: :asc).find_one_and_update({'$currentDate' => {started_at: true}}, return_document: :after)
req ? process_queue_item(req) : sleep(30)
end
end
По сути, запуск потока при запуске сервера для выполнения некоторых фоновых задач, принимаемых приложением от пользователей по HTTP-вызовам.
В режиме разработки все работает нормально. Запуск в производственной среде с тем же сервером Puma, @queue_processor
показывает мертвых (с рельсов консоли):
MyConcern.instance_variable_get("@queue_processor").join
=> #<Thread:0x00000002b8fa68@MyConcern Queue@/var/lib/openshift/5842d71a5110e25cdf00000c/app-root/runtime/repo/app/controllers/concerns/my_concern.rb:23 dead>
MyConcern.instance_variable_get("@queue_processor").value
=> false
Во-первых, для меня (мягко сказано) неочевидно, как поток может умереть без исключения и вернуть false с циклом, который я описал выше в process_op_queue
, Другие очень интересные факты:
- на самом деле есть поток, выполняющий работу, очевидную для меня, когда он выполняет запросы к приложению и просматривает изменения в базе данных.
- в консоли рельсов вижу только одну ветку:
,
2.3.1 :009 > Thread.list
=> [#<Thread:0x000000012f63a8 run>]
2.3.1 :010 >
Часть приложения проверяет поток процессора очереди и сообщает об ошибках с ним, так что обнаружение мертвых действительно нарушает эту логику. Любой советует, как отладить и решить проблему будет принята с благодарностью. Спасибо.
1 ответ
Читая о пуме и работниках, мне неясно, что за магия делает пума, когда она разветвляет рабочих и т. Д. Итак, в итоге мы решили создать для нее грабли:
rails g task mynamespace process_queue
Затем положить в lib/tasks/mynamespace.rake
код, необходимый для запуска процессора:
namespace :mynamespace do
desc "Process reuqest queue"
task process_queue: :environment do
MyConcern.process_op_queue
end
end
Затем запустите это как отдельный процесс вместо отдельного потока приложения. Это позволяет запускать столько процессоров и столько контейнеров, сколько мне нравится. Хорошая вещь с задачей rake состоит в том, что она имеет все поведение как в самом приложении. Это означает MyModel.something
будет автоматически работать как внутри приложения.
nohup bin/rails mynamespace:process_queue > my.log