Table: RailsNotes
User: dreamable
Created at: 2021-02-18 06:29:16 UTC
Updated at: 2021-02-21 08:05:49 UTC
Reference:(Table ID 3, Record ID 25)

标题 :
Active Job
笔记 :

ActiveJob provides an uniform interface for declaring and run backedn jobs. Note that it is just a wrapper, it relies on background processing. If no backend is set, the job is immediately executed. The backend is set like:

# Set in config/application.rb
# Be sure to have the adapter's gem in your Gemfile and follow
# the adapter's specific installation and deployment instructions. 
config.active_job.queue_adapter = :sidekiq

Commonly used backends include delayed job, sidekiq, Resque. For comparison, please check Delayed Job vs Resque vs Sidekiq . delayed_job is easier to use, sidekiq is faster but replies on Redis.

Sidekiq

We use Sidekiq as the backend. It has good document. Here is my configuration:

# 1. Configuration
# install redis
sudo apt-get install redis-server # 3.0.6 
sudo service redis-server restart
# install sidekiq in Gemfile 
gem 'sidekiq'
bundle install # 5.2.5
# config active job in config/application.rb
config.active_job.queue_adapter = :sidekiq # if not set, all jobs will perform immediately.
# Remember to start the sidekiq process
# use "RAILS_ENV=production" or "-e production/development" to specify environment. 
bundle exec sidekiq # connect to localhost:6379/0 by default.
# 2. Jobs, example.
# create job app/jobs/notify_iftt.rb 
class NotifyIftttJob < ApplicationJob
  queue_as :default 
  include IftttHelper
  def perform(tids)
    # Do something later notify_ifttt(tids) if tids
  end 
end
# call it
tids = tids.map{|tid| tid.tid}
NotifyIftttJob.perform_later(tids) if tids # preform_now does not go to backend.

It works as:

  • Rails serialize jobs into Redis database
  • Sidekiq process fetch jobs from Redis
  • restore environment in Rails code, then process it.

It is very important to start the Sidekiq process. Otherwise, the jobs will stay in the Redis unprocessed.

# use RAILS_ENV or -e to specify environment 
RAILS_ENV=production bundle exec sidekiq -e production bundle exec sidekiq -e production
# use -C to specify configuration.
bundle exec sidekiq -e production -C config/sidekiq.xml
# config/sidekiq.yml 
:verbose: false
:concurrency: 25
:strict: false
:logfile: log/sidekiq.log :pidfile: tmp/pids/sidekiq.pid development:
:verbose: true
:concurrency: 15
:logfile: log/sidekiq_dev.log :pidfile: tmp/pids/sidekiq_dev.pid
production:

It is also important to process in correct Rails environment, otherwise, error ocurrs when processing it. For example, the job is stored by production Rails, but got by Sidekiq working on development Rails. So it is better to use different database for different environments or code bases. By default, Redis provides 16 databases (0-15). Race condition is problematic to have two sidekiq/rails shares the same database.

# config/initializers/sidekiq.rb
# Redis by default have 0-15 database. Must config both server and client. 
Sidekiq.configure_server do |config|
  if Rails.env.development?
    config.redis = { url: 'redis://localhost:6379/15'}
  else
    config.redis = { url: 'redis://localhost:6379/0'}
  end 
end
Sidekiq.configure_client do |config| 
  if Rails.env.development?
    config.redis = { url: 'redis://localhost:6379/15'} 
  else
    config.redis = { url: 'redis://localhost:6379/0'} 
  end
end

Sidekiq provide a web interface.

# Add in config/routes.rb
require 'sidekiq/web'
require 'admin_constraint' # require admin for access
mount Sidekiq::Web => '/sys/sidekiq', :constraints => AdminConstraint.new
 # lib/admin_constraint.rb
 class AdminConstraint
  def matches?(request)
    return false unless request.session[:user_id] 
    user = User.find request.session[:user_id] 
    user && user.ADMIN?
  end 
end
Tag: