[Rails] Backburner

Tags
Rails
Engineering
Created
Oct 26, 2023 03:28 AM
Edited
Oct 25, 2023
Description
A beanstalkd-powered job queue that can handle a very high volume of jobs, but the scalability to handle large throughput could be questioned.

What is it?

A beanstalkd-powered job queue that can handle a very high volume of jobs.
💡
Beanstalk is a simple, fast work queue.
  • Backburner supports multiple queues, job priorities, delays, and timeouts.
  • Backburner has robust support for retrying failed jobs, handling error cases, custom logging, and extensible plugin hooks.

Enqueueing job

Define a job object

class NewsletterJob
  include Backburner::Queue
  queue "newsletter-sender"  # defaults to 'backburner-jobs' tube
  queue_priority 1000 # most urgent priority is 0
  queue_respond_timeout 300 # number of seconds before job times out, 0 to avoid timeout

  def self.perform(email, body)
    NewsletterMailer.deliver_text_to_email(email, body)
  end

  # optional, defaults to 'backburner-jobs' tube
  def self.queue
    "newsletter-sender"
  end

  # optional, defaults to default_priority
  def self.queue_priority
    1000 # most urgent priority is 0
  end

  # optional, defaults to respond_timeout in config
  def self.queue_respond_timeout
    300 # number of seconds before job times out, 0 to avoid timeout. NB: A timeout of 1 second will likely lead to race conditions between Backburner and beanstalkd and should be avoided
  end

  # optional, defaults to retry_delay_proc in config
  def self.queue_retry_delay_proc
    lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 5) }
  end

  # optional, defaults to retry_delay in config
  def self.queue_retry_delay
    5
  end

  # optional, defaults to max_job_retries in config
  def self.queue_max_job_retries
    5
  end
end

Enqueue a job object

Backburner.enqueue NewsletterJob, 'foo@admin.com', 'lorem ipsum...'
💡
In Ruby, when you define or call (execute, use) a method, you can omit the parentheses.

Async Job

A more flexible way to define and fire jobs.
class User
  include Backburner::Performable
  queue "user-jobs"  # defaults to 'user'
  queue_priority 500 # most urgent priority is 0
  queue_respond_timeout 300 # number of seconds before job times out, 0 to avoid timeout

  def activate(device_id)
    @device = Device.find(device_id)
    # ...
  end

  def self.reset_password(user_id)
    # ...
  end
end

# Async works for instance methods on a persisted object with an `id`
@user = User.first
@user.async(:ttr => 100, :queue => "activate").activate(@device.id)
# ..and for class methods
User.async(:pri => 100, :delay => 10.seconds).reset_password(@user.id)

Source