Add sidekiq as a job backend

This commit is contained in:
Alex Sherman 2021-03-03 16:37:33 +05:00
parent 313731232e
commit 929ada8fd0
22 changed files with 109 additions and 154 deletions

View file

@ -64,9 +64,8 @@ gem 'omniauth-tara', github: 'internetee/omniauth-tara'
gem 'epp', github: 'internetee/epp', branch: :master gem 'epp', github: 'internetee/epp', branch: :master
gem 'epp-xml', '1.1.0', github: 'internetee/epp-xml' gem 'epp-xml', '1.1.0', github: 'internetee/epp-xml'
gem 'que' gem 'sidekiq'
gem 'daemons-rails', '1.2.1' gem 'daemons-rails', '1.2.1'
gem 'que-web'
gem 'pdfkit' gem 'pdfkit'
gem 'jquery-ui-rails', '5.0.5' gem 'jquery-ui-rails', '5.0.5'
gem 'airbrake' gem 'airbrake'

View file

@ -301,8 +301,6 @@ GEM
railties (>= 3.0) railties (>= 3.0)
msgpack (1.4.2) msgpack (1.4.2)
multi_json (1.15.0) multi_json (1.15.0)
mustermann (1.1.1)
ruby2_keywords (~> 0.0.1)
netrc (0.11.0) netrc (0.11.0)
nio4r (2.5.7) nio4r (2.5.7)
nokogiri (1.10.10) nokogiri (1.10.10)
@ -336,11 +334,6 @@ GEM
public_suffix (4.0.6) public_suffix (4.0.6)
puma (5.2.2) puma (5.2.2)
nio4r (~> 2.0) nio4r (~> 2.0)
que (0.14.3)
que-web (0.7.2)
erubis
que (~> 0.8)
sinatra
rack (2.2.3) rack (2.2.3)
rack-oauth2 (1.16.0) rack-oauth2 (1.16.0)
activesupport activesupport
@ -348,8 +341,6 @@ GEM
httpclient httpclient
json-jwt (>= 1.11.0) json-jwt (>= 1.11.0)
rack (>= 2.1.0) rack (>= 2.1.0)
rack-protection (2.1.0)
rack
rack-test (1.1.0) rack-test (1.1.0)
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
rails (6.0.3.6) rails (6.0.3.6)
@ -385,6 +376,7 @@ GEM
i18n i18n
rbtree3 (0.6.0) rbtree3 (0.6.0)
regexp_parser (2.1.1) regexp_parser (2.1.1)
redis (4.2.5)
request_store (1.5.0) request_store (1.5.0)
rack (>= 1.4) rack (>= 1.4)
responders (3.0.1) responders (3.0.1)
@ -421,6 +413,10 @@ GEM
selenium-webdriver (3.142.7) selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0) childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2) rubyzip (>= 1.2.2)
sidekiq (6.1.3)
connection_pool (>= 2.2.2)
rack (~> 2.0)
redis (>= 4.2.0)
simplecov (0.17.1) simplecov (0.17.1)
docile (~> 1.1) docile (~> 1.1)
json (>= 1.8, < 3) json (>= 1.8, < 3)
@ -428,11 +424,6 @@ GEM
simplecov-html (0.10.2) simplecov-html (0.10.2)
simpleidn (0.1.1) simpleidn (0.1.1)
unf (~> 0.1.4) unf (~> 0.1.4)
sinatra (2.1.0)
mustermann (~> 1.0)
rack (~> 2.2)
rack-protection (= 2.1.0)
tilt (~> 2.0)
sixarm_ruby_unaccent (1.2.0) sixarm_ruby_unaccent (1.2.0)
socksify (1.7.1) socksify (1.7.1)
sprockets (4.0.2) sprockets (4.0.2)
@ -545,8 +536,6 @@ DEPENDENCIES
pg (= 1.2.2) pg (= 1.2.2)
pry (= 0.14.0) pry (= 0.14.0)
puma puma
que
que-web
rails (~> 6.0) rails (~> 6.0)
ransack (~> 2.3) ransack (~> 2.3)
rest-client rest-client
@ -554,6 +543,7 @@ DEPENDENCIES
sass-rails sass-rails
select2-rails (= 4.0.13) select2-rails (= 4.0.13)
selectize-rails (= 0.12.1) selectize-rails (= 0.12.1)
sidekiq
simplecov (= 0.17.1) simplecov (= 0.17.1)
simpleidn (= 0.1.1) simpleidn (= 0.1.1)
truemail (~> 2.2) truemail (~> 2.2)

View file

@ -14,7 +14,7 @@ module Domains
return unless saved return unless saved
recipients.each do |recipient| recipients.each do |recipient|
DomainExpireEmailJob.enqueue(domain.id, recipient, run_at: send_time) DomainExpireEmailJob.set(wait_until: send_time).perform_later(domain.id, recipient)
end end
end end

View file

@ -1,4 +1,4 @@
class DomainExpireEmailJob < Que::Job class DomainExpireEmailJob < ApplicationJob
def perform(domain_id, email) def perform(domain_id, email)
domain = Domain.find(domain_id) domain = Domain.find(domain_id)

View file

@ -12,7 +12,8 @@ class RegistrantChangeExpiredEmailJob < ApplicationJob
private private
def log(domain) def log(domain)
message = "Send RegistrantChangeMailer#expired email for domain #{domain.name} (##{domain.id}) to #{domain.new_registrant_email}" message = 'Send RegistrantChangeMailer#expired email for domain '\
"#{domain.name} (##{domain.id}) to #{domain.new_registrant_email}"
logger.info(message) logger.info(message)
end end

View file

@ -19,8 +19,12 @@ module Domain::Deletable
end end
def do_not_delete_later def do_not_delete_later
# Que job can be manually deleted in admin area UI return if Rails.env.test?
QueJob.find_by("args->>0 = '#{id}'", job_class: DomainDeleteJob.name)&.destroy
jobs = Sidekiq::ScheduledSet.new.select do |job|
job.args.first['job_class'] == 'DomainDeleteJob' && job.args.first['arguments'] == [id]
end
jobs.each(&:delete)
end end
def deletion_time_span def deletion_time_span

View file

@ -1,4 +0,0 @@
# To be able to remove existing jobs
class QueJob < ApplicationRecord
self.primary_key = 'job_id'
end

View file

@ -17,7 +17,7 @@ end
module DomainNameRegistry module DomainNameRegistry
class Application < Rails::Application class Application < Rails::Application
config.load_defaults 6.0 config.load_defaults 6.0
config.autoloader = :zeitwerk # Do not use zeitwerk for now config.autoloader = :zeitwerk
# Settings in config/environments/* take precedence over those specified here. # Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers # Application configuration should go into files in config/initializers
@ -44,7 +44,7 @@ module DomainNameRegistry
config.active_record.schema_format = :sql config.active_record.schema_format = :sql
config.active_job.queue_adapter = :que config.active_job.queue_adapter = :sidekiq
config.generators do |g| config.generators do |g|
g.stylesheets false g.stylesheets false
@ -79,6 +79,14 @@ module DomainNameRegistry
config.action_view.default_form_builder = 'DefaultFormBuilder' config.action_view.default_form_builder = 'DefaultFormBuilder'
config.secret_key_base = Figaro.env.secret_key_base config.secret_key_base = Figaro.env.secret_key_base
# nil will use the "default" queue
# some of these options will not work with your Rails version
# add/remove as necessary
config.action_mailer.deliver_later_queue_name = nil # defaults to "mailers"
config.active_storage.queues.analysis = nil # defaults to "active_storage_analysis"
config.active_storage.queues.purge = nil # defaults to "active_storage_purge"
config.active_storage.queues.mirror = nil # defaults to "active_storage_mirror"
# Using `Rails.application.config.active_record.belongs_to_required_by_default` in # Using `Rails.application.config.active_record.belongs_to_required_by_default` in
# `new_framework_defaults.rb` has no effect in Rails 5.0.x. # `new_framework_defaults.rb` has no effect in Rails 5.0.x.
# https://github.com/rails/rails/issues/23589 # https://github.com/rails/rails/issues/23589

View file

@ -62,9 +62,10 @@ Rails.application.configure do
# Use an evented file watcher to asynchronously detect changes in source code, # Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem. # routes, locales, etc. This feature depends on the listen gem.
config.file_watcher = ActiveSupport::EventedFileUpdateChecker config.file_watcher = ActiveSupport::EventedFileUpdateChecker
end
# In this mode, any jobs you queue will be run in the same thread, synchronously # In this mode, any jobs you queue will be run in the same thread, synchronously
# (that is, MyJob.enqueue runs the job and won't return until it's completed). # (that is, MyJob.enqueue runs the job and won't return until it's completed).
# This makes your application's behavior easier to test # This makes your application's behavior easier to test
Que.mode = :sync config.active_job.queue_adapter = :test
end

View file

@ -87,8 +87,3 @@ Rails.application.configure do
# Do not dump schema after migrations. # Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false config.active_record.dump_schema_after_migration = false
end end
# In off mode, queueing a job will simply insert it into the database -
# the current process will make no effort to run it.
# You should use this if you want to use a dedicated process to work tasks
Que.mode = :off

View file

@ -42,5 +42,3 @@ Rails.application.configure do
# If set to :null_store, Setting.x returns nil after first spec runs (database is emptied) # If set to :null_store, Setting.x returns nil after first spec runs (database is emptied)
config.cache_store = :memory_store config.cache_store = :memory_store
end end
Que.mode = :sync

View file

@ -1,7 +1,7 @@
Que::Adapters::Base::CAST_PROCS[1184] = lambda do |value| # Que::Adapters::Base::CAST_PROCS[1184] = lambda do |value|
case value # case value
when Time then value # when Time then value
when String then Time.parse(value) # when String then Time.parse(value)
else raise "Unexpected time class: #{value.class} (#{value.inspect})" # else raise "Unexpected time class: #{value.class} (#{value.inspect})"
end # end
end # end

View file

@ -0,0 +1,3 @@
require 'sidekiq/web' # Require at the top of the initializer
Sidekiq::Web.set :session_secret, Rails.application.secret_key_base

View file

@ -1,4 +1,5 @@
require_dependency 'epp_constraint' require_dependency 'epp_constraint'
require 'sidekiq/web'
Rails.application.routes.draw do Rails.application.routes.draw do
# https://github.com/internetee/epp_proxy#translation-of-epp-calls # https://github.com/internetee/epp_proxy#translation-of-epp-calls
@ -323,7 +324,8 @@ Rails.application.routes.draw do
resources :bounced_mail_addresses, only: %i[index show destroy] resources :bounced_mail_addresses, only: %i[index show destroy]
authenticate :admin_user do authenticate :admin_user do
mount Que::Web, at: 'que' # mount Que::Web, at: 'que'
mount Sidekiq::Web, at: 'sidekiq'
end end
end end

1
config/sidekiq.yml Normal file
View file

@ -0,0 +1 @@
:concurrency: 1

View file

@ -1,43 +1,43 @@
#!/usr/bin/env ruby # #!/usr/bin/env ruby
#
ENV["RAILS_ENV"] ||= "production" # ENV["RAILS_ENV"] ||= "production"
#
root = File.expand_path(File.dirname(__FILE__)) # root = File.expand_path(File.dirname(__FILE__))
root = File.dirname(root) until File.exist?(File.join(root, 'config')) # root = File.dirname(root) until File.exist?(File.join(root, 'config'))
Dir.chdir(root) # Dir.chdir(root)
#
require File.join(root, "config", "environment") # require File.join(root, "config", "environment")
#
# from que gem rake task # # from que gem rake task
if defined?(::Rails) && Rails.respond_to?(:application) # if defined?(::Rails) && Rails.respond_to?(:application)
# ActiveSupport's dependency autoloading isn't threadsafe, and Que uses # # ActiveSupport's dependency autoloading isn't threadsafe, and Que uses
# multiple threads, which means that eager loading is necessary. Rails # # multiple threads, which means that eager loading is necessary. Rails
# explicitly prevents eager loading when the environment task is invoked, # # explicitly prevents eager loading when the environment task is invoked,
# so we need to manually eager load the app here. # # so we need to manually eager load the app here.
Rails.application.eager_load! # Rails.application.eager_load!
end # end
#
Que.logger.level = Logger.const_get((ENV['QUE_LOG_LEVEL'] || 'INFO').upcase) # Que.logger.level = Logger.const_get((ENV['QUE_LOG_LEVEL'] || 'INFO').upcase)
Que.worker_count = 1 # Que.worker_count = 1
Que.wake_interval = (ENV['QUE_WAKE_INTERVAL'] || 1).to_f # Que.wake_interval = (ENV['QUE_WAKE_INTERVAL'] || 1).to_f
Que.mode = :async # Que.mode = :async
#
# When changing how signals are caught, be sure to test the behavior with # # When changing how signals are caught, be sure to test the behavior with
# the rake task in tasks/safe_shutdown.rb. # # the rake task in tasks/safe_shutdown.rb.
#
stop = false # stop = false
%w( INT ).each do |signal| # %w( INT ).each do |signal|
trap(signal) { stop = true } # trap(signal) { stop = true }
end # end
#
at_exit do # at_exit do
$stdout.puts "Finishing Que's current jobs before exiting..." # $stdout.puts "Finishing Que's current jobs before exiting..."
Que.worker_count = 0 # Que.worker_count = 0
Que.mode = :off # Que.mode = :off
$stdout.puts "Que's jobs finished, exiting..." # $stdout.puts "Que's jobs finished, exiting..."
end # end
#
loop do # loop do
sleep 1 # sleep 1
break if stop # break if stop
end # end

View file

@ -1,6 +1,6 @@
#!/usr/bin/env ruby # #!/usr/bin/env ruby
require 'rubygems' # require 'rubygems'
require 'daemons/rails/config' # require 'daemons/rails/config'
#
config = Daemons::Rails::Config.for_controller(File.expand_path(__FILE__)) # config = Daemons::Rails::Config.for_controller(File.expand_path(__FILE__))
Daemons::Rails.run config[:script], config.to_hash # Daemons::Rails.run config[:script], config.to_hash

View file

@ -10,13 +10,7 @@ class StartTest < ActiveSupport::TestCase
end end
def test_sets_expired def test_sets_expired
job_count = lambda do Sidekiq::Testing.fake! do
QueJob.where("args->>0 = '#{@domain.id}'", job_class: DomainExpireEmailJob.name).count
end
one_job_per_contact_email = @domain.expired_domain_contact_emails.count
assert_difference job_count, one_job_per_contact_email do
perform_enqueued_jobs do perform_enqueued_jobs do
DomainCron.start_expire_period DomainCron.start_expire_period
end end

View file

@ -1,4 +1,6 @@
require 'test_helper' require 'test_helper'
require 'sidekiq/testing'
Sidekiq::Testing.fake!
class DomainReleasableDiscardableTest < ActiveSupport::TestCase class DomainReleasableDiscardableTest < ActiveSupport::TestCase
include ActiveJob::TestHelper include ActiveJob::TestHelper
@ -44,11 +46,7 @@ class DomainReleasableDiscardableTest < ActiveSupport::TestCase
Domain.release_domains Domain.release_domains
job_count = lambda do assert_no_enqueued_jobs do
QueJob.where("args->>0 = '#{@domain.id}'", job_class: DomainDeleteJob.name).count
end
assert_no_difference job_count, 'A domain should not be discarded again' do
Domain.release_domains Domain.release_domains
end end
end end
@ -104,7 +102,8 @@ class DomainReleasableDiscardableTest < ActiveSupport::TestCase
def test_keeping_a_domain_cancels_domain_deletion def test_keeping_a_domain_cancels_domain_deletion
@domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE]) @domain.update!(statuses: [DomainStatus::DELETE_CANDIDATE])
assert_no_enqueued_jobs only: DomainDeleteJob do
@domain.keep @domain.keep
assert_nil QueJob.find_by("args->>0 = '#{@domain.id}'", job_class: DomainDeleteJob.name) end
end end
end end

View file

@ -1,4 +1,7 @@
require 'application_system_test_case' require 'application_system_test_case'
require 'sidekiq/testing'
Sidekiq::Testing.fake!
class AdminDomainsTestTest < ApplicationSystemTestCase class AdminDomainsTestTest < ApplicationSystemTestCase
setup do setup do

View file

@ -1,42 +0,0 @@
require 'application_system_test_case'
class DomainDeleteConfirmsTest < ApplicationSystemTestCase
include ActionMailer::TestHelper
setup do
@user = users(:registrant)
sign_in @user
@domain = domains(:shop)
@domain.registrant_verification_asked!('<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<epp></epp>', @user.id)
@domain.pending_delete!
end
def test_enqueues_approve_job_after_verification
visit registrant_domain_delete_confirm_url(@domain.id, token: @domain.registrant_verification_token)
click_on 'Confirm domain delete'
assert_text 'Domain registrant change has successfully received.'
assert_enqueued_jobs 1, only: DomainDeleteConfirmJob
end
def test_enqueues_reject_job_after_verification
visit registrant_domain_delete_confirm_url(@domain.id, token: @domain.registrant_verification_token)
click_on 'Reject domain delete'
assert_text 'Domain registrant change has been rejected successfully.'
assert_enqueued_jobs 1, only: DomainDeleteConfirmJob
end
def test_saves_whodunnit_info_after_verifivation
visit registrant_domain_delete_confirm_url(@domain.id, token: @domain.registrant_verification_token)
token = @domain.registrant_verification_token
click_on 'Confirm domain delete'
assert_text 'Domain registrant change has successfully received.'
refute RegistrantVerification.find_by(verification_token:token).updator_str.empty?
end
end

View file

@ -18,6 +18,9 @@ require 'capybara/rails'
require 'capybara/minitest' require 'capybara/minitest'
require 'webmock/minitest' require 'webmock/minitest'
require 'support/assertions/epp_assertions' require 'support/assertions/epp_assertions'
require 'sidekiq/testing'
Sidekiq::Testing.fake!
# `bin/rails test` is not the same as `bin/rake test`. # `bin/rails test` is not the same as `bin/rake test`.