Можно ли прервать уже запущенное отложенное задание с помощью Ruby Threading? - PullRequest
5 голосов
/ 06 апреля 2011

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

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

ОБНОВЛЕНИЕ Мой начальник (кстати, отличный программист)предложил использовать Ruby Threading для этой нашей функции.Это возможно?Например, создание новых потоков для каждой задачи и уничтожение этого потока во время его работы?

что-то вроде:

t1 = Thread.new(task.run)
self.delay.t1.join (?) -- still reading on threads so correct me if im wrong

, а затем, чтобы остановить его, я просто снова использую t1.stop (?) Don 'пока не знаю

Возможно ли это?Спасибо!

Ответы [ 4 ]

3 голосов
/ 07 апреля 2011

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

  1. Во-первых, у нас есть модель Job, у которой есть def execute! (который запускает то, что он должен делать).
  2. Далее у нас есть delayed_job работник в фоновом режиме, слушающий новые вакансии. Теперь, когда вы создаете задание, вы можете запланировать его немедленное выполнение или запуск каждый определенный день (для этого мы используем rufus)
  3. Когда задание создается, оно проверяет, должно ли оно выполняться немедленно. Если это так, он добавляется в очередь отложенных заданий. Функция execute создает поток, поэтому каждое задание имеет свой собственный поток.
  4. Пользователь в пользовательском интерфейсе может видеть, выполняется ли задание (если есть начальный_от и нет законченный_ат). Если он работает, есть кнопка, чтобы отменить его. Отмена его просто устанавливает для отмененного задания задание Time.now.
  5. Во время выполнения задания оно также проверяет себя на наличие отмененного_at или Time.now>> 1015 *. Если так, убейте нить.

Вуаля! Мы проверили это на одной работе, и, кажется, это работает. Теперь единственная проблема - масштабирование ...

Если вы видите какие-либо проблемы с этим, пожалуйста, сделайте это в комментариях или дайте больше предложений, если когда-либо :) Я надеюсь, что это кому-то тоже поможет!

2 голосов
/ 06 апреля 2011

Delayed::Job - это модель < ActiveRecord::Base, поэтому вы можете запросить ее, как обычно, как Delayed::Job.all(:conditions => {:last_error => nil}).

Объекты Delayed :: Job имеют поле полезной нагрузки, которое содержит сериализованную версию метода или задания, которое вы пытаетесь запустить. Доступ к этому объекту осуществляется с помощью их метода #payload_object, который загружает рассматриваемый объект.

Вы можете объединить эти две возможности, чтобы сделать работоспособных работников, например, если у вас есть модель User, а у пользователя есть скрепка :avatar, то вы можете создать метод для удаления необработанных работ, например так:

class User < ActiveRecord::Base
   has_attached_file :avatar, PaperclipOptions.new(:avatar)
   before_create :'process_avatar_later'

   def process_avatar_later
      filename = Rails.root.join('tmp/avatars_for_processing/',self.id)
      open(filename, 'w') do |file| file <<self.avatar.to_file end
      Delayed::Job.enqueue(WorkAvatar.new(self.id, filename))
      self.avatar = nil
   end

   def cancel_future_avatar_processing
      WorkAvatar.future_jobs_for_user(self.id).each(&:destroy)
      #ummm... tell them to reupload their avatar, I guess?
   end

   class WorkAvatar < Struct.new(:user_id, :path)
     def user
        @user ||= User.find(self.user_id)
     end
     def self.all_jobs
       Delayed::Job.scoped(:conditions => 'payload like "%WorkAvatar%"')
     end
     def self.future_jobs_for_user(user_id)
       all_jobs.scoped(:conditions => {:locked_at => nil}).select do |job|
          job.payload_object.user_id == user_id
       end
     end  
     def perform
        @user.avatar = File.open(path, 'rb')
        @user.save()
     end
   end          
end

Возможно, кто-то сделал плагин для создания запрашиваемых объектов, подобных этому. Возможно, поиск на GitHub будет плодотворным.

Обратите внимание, что вам придется работать с любыми инструментами мониторинга процессов, вам может потребоваться отменить все выполняющиеся рабочие процессы рабочих процессов, которые выполняются, если вы хотите отменить работу, для которой установлены locked_at и locked_by. 1017 *

1 голос
/ 06 апреля 2011

Вы можете заключить задачу в оператор Timeout.

require 'timeout'

class TaskWithTimeout < Struct.new(:parameter)
  def perform
    Timeout.timeout(10) do
      # ...
    end
  rescue Timeout::Error => e
    # the task took longer than 10 seconds
  end
end
0 голосов
/ 06 апреля 2011

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

У пользователя никогда не будет возможности использовать кнопку «Отмена», поскольку это будет связано с поиском метода прямой связи с работающим запущенным процессом, выполняющим задание. Можно было бы добавить обработчик сигнала к рабочему, чтобы вы могли сделать что-то вроде kill -USR1 pid, чтобы он прервал работу, в которой он работает в данный момент, и пошел дальше. Достигнет ли это вашей цели?

...