use sidekiq

pull/349/head 1.6.2
Pete Matsyburka 1 year ago
parent 9539c476c0
commit 3034c00bb3

1
.gitignore vendored

@ -36,3 +36,4 @@ yarn-debug.log*
/attachments
/docuseal
/ee
dump.rdb

@ -76,5 +76,8 @@ Rails/SkipsModelValidations:
Rails/ApplicationController:
Enabled: false
Rails/Output:
Enabled: false
Capybara/ClickLinkOrButtonStyle:
Enabled: false

@ -41,7 +41,7 @@ ENV OPENSSL_CONF=/app/openssl_legacy.cnf
WORKDIR /app
RUN apk add --no-cache sqlite-dev libpq-dev mariadb-dev vips-dev vips-poppler poppler-utils vips-heif gcompat ttf-freefont && mkdir /fonts && rm /usr/share/fonts/freefont/FreeSans.otf
RUN apk add --no-cache sqlite-dev libpq-dev mariadb-dev vips-dev vips-poppler poppler-utils redis vips-heif gcompat ttf-freefont && mkdir /fonts && rm /usr/share/fonts/freefont/FreeSans.otf
RUN echo $'.include = /etc/ssl/openssl.cnf\n\
\n\

@ -27,7 +27,7 @@ gem 'pagy'
gem 'pg', require: false
gem 'premailer-rails'
gem 'pretender'
gem 'puma'
gem 'puma', require: false
gem 'rack'
gem 'rails'
gem 'rails_autolink'
@ -37,7 +37,7 @@ gem 'rqrcode'
gem 'ruby-vips'
gem 'rubyXL'
gem 'shakapacker'
gem 'sidekiq', require: ENV.key?('REDIS_URL')
gem 'sidekiq'
gem 'sqlite3', require: false, force_ruby_platform: true
gem 'strip_attributes'
gem 'turbo-rails'

@ -64,14 +64,14 @@ module Api
submissions = create_submissions(@template, params)
submissions.each do |submission|
SendSubmissionCreatedWebhookRequestJob.perform_later({ 'submission_id' => submission.id })
SendSubmissionCreatedWebhookRequestJob.perform_async({ 'submission_id' => submission.id })
end
Submissions.send_signature_requests(submissions)
submissions.each do |submission|
if submission.submitters.all?(&:completed_at?) && submission.submitters.last
ProcessSubmitterCompletionJob.perform_later({ 'submitter_id' => submission.submitters.last.id })
ProcessSubmitterCompletionJob.perform_async({ 'submitter_id' => submission.submitters.last.id })
end
end
@ -94,7 +94,7 @@ module Api
else
@submission.update!(archived_at: Time.current)
SendSubmissionArchivedWebhookRequestJob.perform_later('submission_id' => @submission.id)
SendSubmissionArchivedWebhookRequestJob.perform_async('submission_id' => @submission.id)
end
render json: @submission.as_json(only: %i[id archived_at])

@ -13,7 +13,7 @@ module Api
SubmissionEvents.create_with_tracking_data(submitter, 'view_form', request)
SendFormViewedWebhookRequestJob.perform_later({ 'submitter_id' => submitter.id })
SendFormViewedWebhookRequestJob.perform_async({ 'submitter_id' => submitter.id })
render json: {}
end

@ -66,7 +66,7 @@ module Api
end
if @submitter.completed_at?
ProcessSubmitterCompletionJob.perform_later({ 'submitter_id' => @submitter.id })
ProcessSubmitterCompletionJob.perform_async({ 'submitter_id' => @submitter.id })
elsif normalized_params[:send_email] || normalized_params[:send_sms]
Submitters.send_signature_requests([@submitter])
end

@ -20,7 +20,7 @@ module Api
Templates::CloneAttachments.call(template: cloned_template, original_template: @template)
SendTemplateCreatedWebhookRequestJob.perform_later('template_id' => cloned_template.id)
SendTemplateCreatedWebhookRequestJob.perform_async('template_id' => cloned_template.id)
render json: Templates::SerializeForApi.call(cloned_template)
end

@ -65,7 +65,7 @@ module Api
@template.update!(template_params)
SendTemplateUpdatedWebhookRequestJob.perform_later('template_id' => @template.id)
SendTemplateUpdatedWebhookRequestJob.perform_async('template_id' => @template.id)
render json: @template.as_json(only: %i[id updated_at])
end

@ -37,7 +37,7 @@ class StartFormController < ApplicationController
if @submitter.save
if is_new_record
SendSubmissionCreatedWebhookRequestJob.perform_later({ 'submission_id' => @submitter.submission.id })
SendSubmissionCreatedWebhookRequestJob.perform_async({ 'submission_id' => @submitter.submission.id })
end
redirect_to submit_form_path(@submitter.slug)

@ -45,7 +45,7 @@ class SubmissionsController < ApplicationController
end
submissions.each do |submission|
SendSubmissionCreatedWebhookRequestJob.perform_later({ 'submission_id' => submission.id })
SendSubmissionCreatedWebhookRequestJob.perform_async({ 'submission_id' => submission.id })
end
Submissions.send_signature_requests(submissions)
@ -56,7 +56,7 @@ class SubmissionsController < ApplicationController
def destroy
@submission.update!(archived_at: Time.current)
SendSubmissionArchivedWebhookRequestJob.perform_later('submission_id' => @submission.id)
SendSubmissionArchivedWebhookRequestJob.perform_async('submission_id' => @submission.id)
redirect_back(fallback_location: template_path(@submission.template), notice: 'Submission has been archived')
end

@ -13,7 +13,7 @@ class SubmittersSendEmailController < ApplicationController
alert: 'Email has been sent already.')
end
SendSubmitterInvitationEmailJob.perform_later('submitter_id' => @submitter.id)
SendSubmitterInvitationEmailJob.perform_async('submitter_id' => @submitter.id)
@submitter.sent_at ||= Time.current
@submitter.save!

@ -61,7 +61,7 @@ class TemplatesController < ApplicationController
if @template.save
Templates::CloneAttachments.call(template: @template, original_template: @base_template) if @base_template
SendTemplateUpdatedWebhookRequestJob.perform_later('template_id' => @template.id)
SendTemplateUpdatedWebhookRequestJob.perform_async('template_id' => @template.id)
maybe_redirect_to_template(@template)
else
@ -72,7 +72,7 @@ class TemplatesController < ApplicationController
def update
@template.update!(template_params)
SendTemplateUpdatedWebhookRequestJob.perform_later('template_id' => @template.id)
SendTemplateUpdatedWebhookRequestJob.perform_async('template_id' => @template.id)
head :ok
end

@ -18,7 +18,7 @@ class TemplatesUploadsController < ApplicationController
@template.update!(schema:)
SendTemplateCreatedWebhookRequestJob.perform_later('template_id' => @template.id)
SendTemplateCreatedWebhookRequestJob.perform_async('template_id' => @template.id)
redirect_to edit_template_path(@template)
rescue Templates::CreateAttachments::PdfEncrypted

@ -17,7 +17,7 @@ class WebhookSettingsController < ApplicationController
def update
submitter = current_account.submitters.where.not(completed_at: nil).order(:id).last
SendFormCompletedWebhookRequestJob.perform_later({ 'submitter_id' => submitter.id })
SendFormCompletedWebhookRequestJob.perform_async({ 'submitter_id' => submitter.id })
redirect_back(fallback_location: settings_webhooks_path, notice: 'Webhook request has been sent.')
end

@ -1,6 +1,8 @@
# frozen_string_literal: true
class ProcessSubmitterCompletionJob < ApplicationJob
class ProcessSubmitterCompletionJob
include Sidekiq::Job
def perform(params = {})
submitter = Submitter.find(params['submitter_id'])
@ -20,7 +22,7 @@ class ProcessSubmitterCompletionJob < ApplicationJob
return if Accounts.load_webhook_url(submitter.account).blank?
SendFormCompletedWebhookRequestJob.perform_later({ 'submitter_id' => submitter.id })
SendFormCompletedWebhookRequestJob.perform_async({ 'submitter_id' => submitter.id })
end
def enqueue_completed_emails(submitter)

@ -1,7 +1,9 @@
# frozen_string_literal: true
class SendFormCompletedWebhookRequestJob < ApplicationJob
queue_as :webhooks
class SendFormCompletedWebhookRequestJob
include Sidekiq::Job
sidekiq_options queue: :webhooks
USER_AGENT = 'DocuSeal.co Webhook'
@ -38,12 +40,11 @@ class SendFormCompletedWebhookRequestJob < ApplicationJob
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
(!Docuseal.multitenant? || submitter.account.account_configs.exists?(key: :plan))
SendFormCompletedWebhookRequestJob.set(wait: (2**attempt).minutes)
.perform_later({
'submitter_id' => submitter.id,
'attempt' => attempt + 1,
'last_status' => resp&.status.to_i
})
SendFormCompletedWebhookRequestJob.perform_in((2**attempt).minutes, {
'submitter_id' => submitter.id,
'attempt' => attempt + 1,
'last_status' => resp&.status.to_i
})
end
end
end

@ -1,7 +1,9 @@
# frozen_string_literal: true
class SendFormStartedWebhookRequestJob < ApplicationJob
queue_as :webhooks
class SendFormStartedWebhookRequestJob
include Sidekiq::Job
sidekiq_options queue: :webhooks
USER_AGENT = 'DocuSeal.co Webhook'
@ -36,12 +38,11 @@ class SendFormStartedWebhookRequestJob < ApplicationJob
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
(!Docuseal.multitenant? || submitter.account.account_configs.exists?(key: :plan))
SendFormStartedWebhookRequestJob.set(wait: (2**attempt).minutes)
.perform_later({
'submitter_id' => submitter.id,
'attempt' => attempt + 1,
'last_status' => resp&.status.to_i
})
SendFormStartedWebhookRequestJob.perform_in((2**attempt).minutes, {
'submitter_id' => submitter.id,
'attempt' => attempt + 1,
'last_status' => resp&.status.to_i
})
end
end
end

@ -1,7 +1,9 @@
# frozen_string_literal: true
class SendFormViewedWebhookRequestJob < ApplicationJob
queue_as :webhooks
class SendFormViewedWebhookRequestJob
include Sidekiq::Job
sidekiq_options queue: :webhooks
USER_AGENT = 'DocuSeal.co Webhook'
@ -36,12 +38,11 @@ class SendFormViewedWebhookRequestJob < ApplicationJob
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
(!Docuseal.multitenant? || submitter.account.account_configs.exists?(key: :plan))
SendFormViewedWebhookRequestJob.set(wait: (2**attempt).minutes)
.perform_later({
'submitter_id' => submitter.id,
'attempt' => attempt + 1,
'last_status' => resp&.status.to_i
})
SendFormViewedWebhookRequestJob.perform_in((2**attempt).minutes, {
'submitter_id' => submitter.id,
'attempt' => attempt + 1,
'last_status' => resp&.status.to_i
})
end
end
end

@ -1,7 +1,9 @@
# frozen_string_literal: true
class SendSubmissionArchivedWebhookRequestJob < ApplicationJob
queue_as :webhooks
class SendSubmissionArchivedWebhookRequestJob
include Sidekiq::Job
sidekiq_options queue: :webhooks
USER_AGENT = 'DocuSeal.co Webhook'
@ -34,12 +36,11 @@ class SendSubmissionArchivedWebhookRequestJob < ApplicationJob
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
(!Docuseal.multitenant? || submission.account.account_configs.exists?(key: :plan))
SendSubmissionArchivedWebhookRequestJob.set(wait: (2**attempt).minutes)
.perform_later({
'submission_id' => submission.id,
'attempt' => attempt + 1,
'last_status' => resp&.status.to_i
})
SendSubmissionArchivedWebhookRequestJob.perform_in((2**attempt).minutes, {
'submission_id' => submission.id,
'attempt' => attempt + 1,
'last_status' => resp&.status.to_i
})
end
end
end

@ -1,7 +1,9 @@
# frozen_string_literal: true
class SendSubmissionCreatedWebhookRequestJob < ApplicationJob
queue_as :webhooks
class SendSubmissionCreatedWebhookRequestJob
include Sidekiq::Job
sidekiq_options queue: :webhooks
USER_AGENT = 'DocuSeal.co Webhook'
@ -34,12 +36,11 @@ class SendSubmissionCreatedWebhookRequestJob < ApplicationJob
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
(!Docuseal.multitenant? || submission.account.account_configs.exists?(key: :plan))
SendSubmissionCreatedWebhookRequestJob.set(wait: (2**attempt).minutes)
.perform_later({
'submission_id' => submission.id,
'attempt' => attempt + 1,
'last_status' => resp&.status.to_i
})
SendSubmissionCreatedWebhookRequestJob.perform_in((2**attempt).minutes, {
'submission_id' => submission.id,
'attempt' => attempt + 1,
'last_status' => resp&.status.to_i
})
end
end
end

@ -1,6 +1,8 @@
# frozen_string_literal: true
class SendSubmitterInvitationEmailJob < ApplicationJob
class SendSubmitterInvitationEmailJob
include Sidekiq::Job
def perform(params = {})
submitter = Submitter.find(params['submitter_id'])

@ -1,7 +1,9 @@
# frozen_string_literal: true
class SendTemplateCreatedWebhookRequestJob < ApplicationJob
queue_as :webhooks
class SendTemplateCreatedWebhookRequestJob
include Sidekiq::Job
sidekiq_options queue: :webhooks
USER_AGENT = 'DocuSeal.co Webhook'
@ -34,12 +36,11 @@ class SendTemplateCreatedWebhookRequestJob < ApplicationJob
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
(!Docuseal.multitenant? || template.account.account_configs.exists?(key: :plan))
SendTemplateCreatedWebhookRequestJob.set(wait: (2**attempt).minutes)
.perform_later({
'template_id' => template.id,
'attempt' => attempt + 1,
'last_status' => resp&.status.to_i
})
SendTemplateCreatedWebhookRequestJob.perform_in((2**attempt).minutes, {
'template_id' => template.id,
'attempt' => attempt + 1,
'last_status' => resp&.status.to_i
})
end
end
end

@ -1,7 +1,9 @@
# frozen_string_literal: true
class SendTemplateUpdatedWebhookRequestJob < ApplicationJob
queue_as :webhooks
class SendTemplateUpdatedWebhookRequestJob
include Sidekiq::Job
sidekiq_options queue: :webhooks
USER_AGENT = 'DocuSeal.co Webhook'
@ -34,12 +36,11 @@ class SendTemplateUpdatedWebhookRequestJob < ApplicationJob
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
(!Docuseal.multitenant? || template.account.account_configs.exists?(key: :plan))
SendTemplateUpdatedWebhookRequestJob.set(wait: (2**attempt).minutes)
.perform_later({
'template_id' => template.id,
'attempt' => attempt + 1,
'last_status' => resp&.status.to_i
})
SendTemplateUpdatedWebhookRequestJob.perform_in((2**attempt).minutes, {
'template_id' => template.id,
'attempt' => attempt + 1,
'last_status' => resp&.status.to_i
})
end
end
end

@ -81,6 +81,12 @@ class User < ApplicationRecord
true
end
def sidekiq?
return true if Rails.env.development?
role == 'admin'
end
def self.sign_in_after_reset_password
if PasswordsController::Current.user.present?
!PasswordsController::Current.user.otp_required_for_login

@ -19,7 +19,7 @@ module DocuSeal
class Application < Rails::Application
config.load_defaults 7.1
config.autoload_lib(ignore: %w[assets tasks])
config.autoload_lib(ignore: %w[assets tasks puma])
config.active_storage.routes_prefix = ''

@ -2,41 +2,7 @@
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
if ENV['RAILS_ENV'] == 'production' && ENV['SECRET_KEY_BASE'].to_s.empty?
require 'dotenv'
require 'securerandom'
dotenv_path = "#{ENV.fetch('WORKDIR', '.')}/docuseal.env"
unless File.exist?(dotenv_path)
default_env = <<~TEXT
DATABASE_URL= # keep empty to use sqlite or specify postgresql database URL
SECRET_KEY_BASE=#{SecureRandom.hex(64)}
TEXT
File.write(dotenv_path, default_env)
end
database_url = ENV.fetch('DATABASE_URL', nil)
Dotenv.load(dotenv_path)
ENV['DATABASE_URL'] = ENV['DATABASE_URL'].to_s.empty? ? database_url : ENV.fetch('DATABASE_URL', nil)
end
if ENV['DATABASE_URL'].to_s.split('@').last.to_s.split('/').first.to_s.include?('_')
require 'addressable'
url = Addressable::URI.parse(ENV.fetch('DATABASE_URL', ''))
ENV['DATABASE_HOST'] = url.host
ENV['DATABASE_PORT'] = (url.port || 5432).to_s
ENV['DATABASE_USER'] = url.user
ENV['DATABASE_PASSWORD'] = url.password
ENV['DATABASE_NAME'] = url.path.to_s.delete_prefix('/')
ENV.delete('DATABASE_URL')
end
require_relative 'dotenv'
require 'bundler/setup' # Set up gems listed in the Gemfile.
require 'bootsnap/setup' # Speed up boot time by caching expensive operations.

@ -1,7 +1,7 @@
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch('RAILS_MAX_THREADS', 15) %>
pool: <%= ENV.fetch('RAILS_MAX_THREADS', 15).to_i + ENV.fetch('SIDEKIQ_THREADS', 5).to_i %>
development:
<<: *default
@ -22,12 +22,14 @@ production:
<% elsif ENV['DATABASE_URL'].to_s.empty? %>
adapter: sqlite3
database: <%= ENV['WORKDIR'] || '.' %>/db.sqlite3
pool: <%= ENV.fetch('RAILS_MAX_THREADS', 15).to_i + ENV.fetch('SIDEKIQ_THREADS', 5).to_i %>
timeout: 5000
<% elsif ENV['DATABASE_URL'].match?(/\Apostgres/) %>
<<: *default
url: <%= ENV['DATABASE_URL'] %>
<% elsif ENV['DATABASE_URL'].match?(/\Amysql/) %>
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch('RAILS_MAX_THREADS', 15) %>
pool: <%= ENV.fetch('RAILS_MAX_THREADS', 15).to_i + ENV.fetch('SIDEKIQ_THREADS', 5).to_i %>
url: <%= ENV['DATABASE_URL'] %>
<% end %>

@ -0,0 +1,46 @@
# frozen_string_literal: true
if ENV['RAILS_ENV'] == 'production' && ENV['SECRET_KEY_BASE'].to_s.empty?
require 'dotenv'
require 'securerandom'
dotenv_path = "#{ENV.fetch('WORKDIR', '.')}/docuseal.env"
unless File.exist?(dotenv_path)
default_env = <<~TEXT
DATABASE_URL= # keep empty to use sqlite or specify postgresql database URL
SECRET_KEY_BASE=#{SecureRandom.hex(64)}
TEXT
File.write(dotenv_path, default_env)
end
database_url = ENV.fetch('DATABASE_URL', nil)
Dotenv.load(dotenv_path)
ENV['DATABASE_URL'] = ENV['DATABASE_URL'].to_s.empty? ? database_url : ENV.fetch('DATABASE_URL', nil)
end
if ENV['DATABASE_URL'].to_s.split('@').last.to_s.split('/').first.to_s.include?('_')
require 'addressable'
url = Addressable::URI.parse(ENV.fetch('DATABASE_URL', ''))
ENV['DATABASE_HOST'] = url.host
ENV['DATABASE_PORT'] = (url.port || 5432).to_s
ENV['DATABASE_USER'] = url.user
ENV['DATABASE_PASSWORD'] = url.password
ENV['DATABASE_NAME'] = url.path.to_s.delete_prefix('/')
ENV.delete('DATABASE_URL')
end
if ENV['REDIS_URL'].to_s.empty?
require 'digest'
redis_password = Digest::SHA1.hexdigest("redis#{ENV.fetch('SECRET_KEY_BASE', '')}")
ENV['REDIS_URL'] = "redis://default:#{redis_password}@0.0.0.0:6379/0"
ENV['LOCAL_REDIS_URL'] = ENV.fetch('REDIS_URL', nil)
end

@ -47,7 +47,7 @@ Rails.application.configure do
config.cache_store = :null_store
end
config.active_job.queue_adapter = :sidekiq if defined?(Sidekiq)
config.active_job.queue_adapter = :sidekiq
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :disk

@ -27,7 +27,7 @@ Rails.application.configure do
config.action_controller.perform_caching = true
config.active_record.sqlite3_production_warning = false
config.active_job.queue_adapter = :sidekiq if defined?(Sidekiq)
config.active_job.queue_adapter = :sidekiq
# Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
# or in config/master.key. This key is used to decrypt credentials (and other encrypted files).

@ -44,7 +44,7 @@ Rails.application.configure do
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
config.active_job.queue_adapter = :test
config.active_job.queue_adapter = :sidekiq
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr

@ -1,16 +1,14 @@
# frozen_string_literal: true
if defined?(Sidekiq)
require 'sidekiq/web'
require 'sidekiq/web' if defined?(Puma)
if !ENV['SIDEKIQ_BASIC_AUTH_PASSWORD'].to_s.empty? && defined?(Sidekiq::Web)
Sidekiq::Web.use(Rack::Auth::Basic) do |_, password|
next true if Rails.env.development?
ActiveSupport::SecurityUtils.secure_compare(
Digest::SHA256.hexdigest(password),
Digest::SHA256.hexdigest(ENV.fetch('SIDEKIQ_BASIC_AUTH_PASSWORD'))
)
end
Sidekiq.strict_args!
end
Sidekiq.strict_args!

@ -5,7 +5,9 @@
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
require_relative 'dotenv'
max_threads_count = ENV.fetch('RAILS_MAX_THREADS', 15)
min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count }
threads min_threads_count, max_threads_count
@ -41,5 +43,10 @@ workers ENV.fetch('WEB_CONCURRENCY', 0)
#
# preload_app!
# Allow puma to be restarted by `bin/rails restart` command.
plugin :tmp_restart
if ENV['MULTITENANT'] != 'true' || ENV['DEMO'] == 'true'
require_relative '../lib/puma/plugin/redis_server'
require_relative '../lib/puma/plugin/sidekiq_embed'
plugin :sidekiq_embed
plugin :redis_server
end

@ -2,7 +2,12 @@
Rails.application.routes.draw do
mount LetterOpenerWeb::Engine, at: '/letter_opener' if Rails.env.development?
mount Sidekiq::Web => '/sidekiq' if defined?(Sidekiq)
if !Docuseal.multitenant? && defined?(Sidekiq::Web)
authenticated :user, ->(u) { u.sidekiq? } do
mount Sidekiq::Web => '/jobs'
end
end
root 'dashboard#index'

@ -0,0 +1,70 @@
# frozen_string_literal: true
require 'puma/plugin'
# rubocop:disable Metrics
Puma::Plugin.create do
def start(launcher)
return if ENV['LOCAL_REDIS_URL'].to_s.empty?
@puma_pid = $PROCESS_ID
launcher.events.on_booted do
@redis_server_pid = fork_redis
end
in_background { monitor_redis }
at_exit do
stop_redis_server if Process.pid == @puma_pid
end
launcher.events.on_stopped { stop_redis_server }
launcher.events.on_restart { stop_redis_server }
end
private
def monitor_redis
loop do
if redis_dead?
Process.kill(:INT, @puma_pid)
break
end
sleep 5
end
end
def redis_dead?
return false unless @redis_server_pid
Process.waitpid(@redis_server_pid, Process::WNOHANG)
false
rescue Errno::ECHILD, Errno::ESRCH
true
end
def fork_redis
fork do
Process.setsid
Dir.chdir(ENV.fetch('WORKDIR', nil)) unless ENV['WORKDIR'].to_s.empty?
exec('redis-server', '--requirepass', Digest::SHA1.hexdigest("redis#{ENV.fetch('SECRET_KEY_BASE', '')}"),
out: '/dev/null')
end
end
def stop_redis_server
if @redis_server_pid
Process.kill(:INT, @redis_server_pid)
Process.wait(@redis_server_pid)
end
rescue Errno::ECHILD, Errno::ESRCH
nil
end
end
# rubocop:enable Metrics

@ -0,0 +1,74 @@
# frozen_string_literal: true
require 'puma/plugin'
# rubocop:disable Metrics
Puma::Plugin.create do
def config(cfg)
return if cfg.instance_variable_get(:@options)[:workers] <= 0
cfg.on_worker_boot { start_sidekiq! }
cfg.on_worker_shutdown { @sidekiq&.stop }
cfg.on_refork { @sidekiq&.stop }
end
def start(launcher)
launcher.events.on_booted do
next if Puma.stats_hash[:workers].to_i != 0
start_sidekiq!
end
launcher.events.on_stopped { Thread.new { @sidekiq&.stop }.join }
launcher.events.on_restart { Thread.new { @sidekiq&.stop }.join }
end
def fire_event(config, event)
arr = config[:lifecycle_events][event]
arr.each(&:call)
arr.clear
end
def start_sidekiq!
Thread.new do
wait_for_redis!
configs = Sidekiq.configure_embed do |config|
config.logger.level = Logger::INFO
sidekiq_config = YAML.load_file('config/sidekiq.yml')
config.queues = sidekiq_config['queues']
config.concurrency = ENV.fetch('SIDEKIQ_THREADS', 5).to_i
config.merge!(sidekiq_config)
config[:max_retries] = 13
ActiveSupport.run_load_hooks(:sidekiq_config, config)
end.instance_variable_get(:@config)
@sidekiq = Sidekiq::Launcher.new(configs, embedded: true)
@sidekiq.run
fire_event(configs, :startup)
end
end
def wait_for_redis!
attempt = 0
loop do
attempt += 1
sleep (attempt - 1) / 10.0
RedisClient.new(url: ENV.fetch('REDIS_URL', nil)).call('GET', '1')
break
rescue RedisClient::CannotConnectError
raise('Unable to connect to redis') if attempt > 10
end
end
end
# rubocop:enable Metrics

@ -73,7 +73,7 @@ module Submitters
next if submitter.email.blank?
next if submitter.preferences['send_email'] == false
SendSubmitterInvitationEmailJob.perform_later('submitter_id' => submitter.id)
SendSubmitterInvitationEmailJob.perform_async('submitter_id' => submitter.id)
end
end
end

@ -14,14 +14,14 @@ module Submitters
unless submitter.submission_events.exists?(event_type: 'start_form')
SubmissionEvents.create_with_tracking_data(submitter, 'start_form', request)
SendFormStartedWebhookRequestJob.perform_later({ 'submitter_id' => submitter.id })
SendFormStartedWebhookRequestJob.perform_async({ 'submitter_id' => submitter.id })
end
update_submitter!(submitter, params, request)
submitter.submission.save!
ProcessSubmitterCompletionJob.perform_later({ 'submitter_id' => submitter.id }) if submitter.completed_at?
ProcessSubmitterCompletionJob.perform_async({ 'submitter_id' => submitter.id }) if submitter.completed_at?
submitter
end

@ -9,6 +9,9 @@ require 'rspec/rails'
require 'capybara/cuprite'
require 'capybara/rspec'
require 'webmock/rspec'
require 'sidekiq/testing'
Sidekiq::Testing.fake!
WebMock.disable_net_connect!(allow_localhost: true)
@ -56,4 +59,12 @@ RSpec.configure do |config|
driven_by :headless_cuprite
end
end
config.before do
Sidekiq::Worker.clear_all
end
config.before do |example|
Sidekiq::Testing.inline! if example.metadata[:sidekiq] == :inline
end
end

@ -81,7 +81,7 @@ RSpec.describe 'Submit Form' do
expect do
click_on 'submit'
end.to change(enqueued_jobs, :size).by(2)
end.to change(enqueued_jobs, :size).by(1)
end
end
end

Loading…
Cancel
Save