Rails Active Job: perform_later not executing in background

aperture picture aperture · May 23, 2016 · Viewed 7.8k times · Source

I have a lengthy task that needs to run in the background in my Rails 4.2.6 app. Unfortunately, the job is not being sent to the background using Active Job. I've generated a job:

class PhotoProcessorJob < ActiveJob::Base
  queue_as :default
  def perform(*args)
    ::Photo.process_photos
  end
end

which calls a method on my Photo class (stored in config/initializers):

class Photo
  require 'zxing'
  require 'csv'

  @tablePath = Dir.glob("#{Rails.root.to_s}/tmp/photo_processing/*.csv")[0]
  @output = "#{Rails.root.to_s}/tmp/photo_data.csv"

  def self.getStudentInfo(id)
    CSV.foreach(@tablePath, headers: true) do |row|
      if row["Student ID"] == id
        return row
      else
        next
      end
    end
  end

  def self.writeInfoToFile(data, file)
    first_name = data["First Name"]
    last_name = data["Last Name"]
    student_id = data["Student ID"]
    grade = data["Grade"]
    email = data["Email"]
    photo = file.to_s
    CSV.open(@output, "a+") do |csv|
      csv << [first_name, last_name, student_id, grade, email, photo]
    end
  end

  def self.process_photos
    extensions = %w(.jpg .jpeg .png .gif .tif)
    studentInfo = nil
    newfile = false
    if File.exist?(@output)
      outfile = CSV.new(File.read(@output))
      if outfile.count == 0
        newfile = true
      end
    else
      newfile = true
    end
    if newfile
      CSV.open(@output, "wb") do |csv|
        csv << ["First Name", "Last Name", "Student ID", "Grade", "Email", "Photo"]
      end
    end
    Dir.glob("#{Rails.root.to_s}/tmp/photo_processing/*").each do |file|
      if file.match(/#{extensions.join("|")}/)
        id = ZXing.decode File.new(file)
        unless id.nil?
          studentInfo = getStudentInfo(id)
        else
          writeInfoToFile(studentInfo, file) unless studentInfo.nil?
        end
      end
    end
  end

end

and called from a controller:

class ProcessingController < ApplicationController
  def finish
    PhotoProcessorJob.perform_later
  end
end

I'm trying to use the Active Job Inline backend and so do not have any queue libraries installed. The problem is that the "finish" view is delayed while the process_photos method runs rather than it being sent to the background and the view being displayed immediately. This results in a 502 error within Nginx caused by upstream prematurely closed connection presumably because the process_photos task is taking too long to complete.

Have I done something wrong with my Active Job setup?

Answer

Chase picture Chase · May 23, 2016

To quote the docs:

Rails by default comes with an "immediate runner" queuing implementation. That means that each job that has been enqueued will run immediately.

What that means is that by default, active job will run in the main thread, not in "the background". This is a common gotcha. Basically active job is just a common API across multiple queueing backends.

TL;DR You have to set up a queueing backend like Sidekiq.

Otherwise, your setup looks great, textbook even.