jobs: switch from DelayedJob to GoodJob.
Switch the ActiveJob backend from DelayedJob to GoodJob. Differences: * The job worker is run with `bin/good_job start` instead of `bin/delayed_job`. * Jobs have an 8 hour timeout instead of a 4 hour timeout. * Jobs don't automatically retry on failure. * Finishing jobs are preserved and pruned after 7 days.
This commit is contained in:
2
Procfile
2
Procfile
@@ -8,7 +8,7 @@
|
|||||||
# https://github.com/ddollar/foreman
|
# https://github.com/ddollar/foreman
|
||||||
|
|
||||||
web: bin/rails server
|
web: bin/rails server
|
||||||
worker: bin/rails jobs:work
|
worker: bin/good_job start
|
||||||
clock: bin/rails danbooru:cron
|
clock: bin/rails danbooru:cron
|
||||||
webpack-dev-server: bin/webpack-dev-server
|
webpack-dev-server: bin/webpack-dev-server
|
||||||
# db: docker run --rm -it --name danbooru-postgres --shm-size=8g -p 5432:5432 -e POSTGRES_USER=danbooru - e POSTRES_HOST_AUTH_METHOD=trust -v danbooru-postgres:/var/lib/postgresql/data ghcr.io/danbooru/postgres:14.0
|
# db: docker run --rm -it --name danbooru-postgres --shm-size=8g -p 5432:5432 -e POSTGRES_USER=danbooru - e POSTRES_HOST_AUTH_METHOD=trust -v danbooru-postgres:/var/lib/postgresql/data ghcr.io/danbooru/postgres:14.0
|
||||||
|
|||||||
@@ -9,30 +9,26 @@ later.
|
|||||||
Jobs use the Rails Active Job framework. Active Job is a common framework that
|
Jobs use the Rails Active Job framework. Active Job is a common framework that
|
||||||
allows jobs to be run on different job runner backends.
|
allows jobs to be run on different job runner backends.
|
||||||
|
|
||||||
In the production environment, jobs are run using the Delayed Job backend. Jobs
|
In the production environment, jobs are run using the Good Job backend. Jobs
|
||||||
are stored in the database in the `delayed_job` table. Worker processes spawned
|
are stored in the database in the `good_jobs` table. Worker processes spawned
|
||||||
by `bin/delayed_job` poll the table for new jobs to work.
|
by `bin/good_job` poll the table for new jobs to work.
|
||||||
|
|
||||||
In the development environment, jobs are run with an in-process thread pool.
|
In the development environment, jobs are run with an in-process thread pool.
|
||||||
This will run jobs in the background, but will drop jobs when the server is
|
This will run jobs in the background, but will drop jobs when the server is
|
||||||
restarted.
|
restarted.
|
||||||
|
|
||||||
There are two job queues, the `default` queue and the `bulk_update`. The
|
There is a very minimal admin dashboard for jobs at https://danbooru.donmai.us/jobs.
|
||||||
`bulk_update` queue handles bulk update requests. It has only one worker so that
|
|
||||||
bulk update requests are effectively processed sequentially. The `default` queue
|
|
||||||
handles everything else.
|
|
||||||
|
|
||||||
There is a very minimal admin dashboard for jobs at https://danbooru.donmai.us/delayed_jobs.
|
|
||||||
|
|
||||||
Danbooru also has periodic maintenance tasks that run in the background as cron
|
Danbooru also has periodic maintenance tasks that run in the background as cron
|
||||||
jobs. These are different from the jobs in this directory. See [app/logical/danbooru_maintenance.rb](../logical/danbooru_maintenance.rb).
|
jobs. These are different from the jobs in this directory. See
|
||||||
|
[app/logical/danbooru_maintenance.rb](../logical/danbooru_maintenance.rb).
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
Start a pool of job workers:
|
Start a pool of job workers:
|
||||||
|
|
||||||
```
|
```
|
||||||
RAILS_ENV=production bin/delayed_job --pool=default:8 --pool=bulk_update start
|
RAILS_ENV=production bin/good_job start --max-threads=4
|
||||||
```
|
```
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
@@ -47,12 +43,12 @@ DeleteFavoritesJob.perform_later(user)
|
|||||||
# See also
|
# See also
|
||||||
|
|
||||||
* [app/logical/danbooru_maintenance.rb](../logical/danbooru_maintenance.rb)
|
* [app/logical/danbooru_maintenance.rb](../logical/danbooru_maintenance.rb)
|
||||||
* [app/controllers/delayed_jobs_controller.rb](../controllers/delayed_jobs_controller.rb)
|
* [app/controllers/jobs_controller.rb](../controllers/jobs_controller.rb)
|
||||||
* [config/initializers/delayed_jobs.rb](../../config/initializers/delayed_jobs.rb)
|
* [config/initializers/good_job.rb](../../config/initializers/good_job.rb)
|
||||||
* [test/jobs](../../test/jobs)
|
* [test/jobs](../../test/jobs)
|
||||||
|
|
||||||
# External links
|
# External links
|
||||||
|
|
||||||
* https://guides.rubyonrails.org/active_job_basics.html
|
* https://guides.rubyonrails.org/active_job_basics.html
|
||||||
* https://github.com/collectiveidea/delayed_job
|
* https://github.com/bensheldon/good_job
|
||||||
* https://danbooru.donmai.us/delayed_jobs
|
* https://danbooru.donmai.us/jobs
|
||||||
|
|||||||
@@ -3,15 +3,19 @@
|
|||||||
# The base class for all background jobs on Danbooru.
|
# The base class for all background jobs on Danbooru.
|
||||||
#
|
#
|
||||||
# @see https://guides.rubyonrails.org/active_job_basics.html
|
# @see https://guides.rubyonrails.org/active_job_basics.html
|
||||||
# @see https://github.com/collectiveidea/delayed_job
|
# @see https://github.com/bensheldon/good_job
|
||||||
class ApplicationJob < ActiveJob::Base
|
class ApplicationJob < ActiveJob::Base
|
||||||
|
class JobTimeoutError < StandardError; end
|
||||||
|
|
||||||
queue_as :default
|
queue_as :default
|
||||||
queue_with_priority 0
|
queue_with_priority 0
|
||||||
|
|
||||||
around_perform do |_job, block|
|
around_perform do |_job, block|
|
||||||
CurrentUser.scoped(User.system, "127.0.0.1") do
|
CurrentUser.scoped(User.system, "127.0.0.1") do
|
||||||
ApplicationRecord.without_timeout do
|
ApplicationRecord.without_timeout do
|
||||||
block.call
|
Timeout.timeout(8.hours, JobTimeoutError) do
|
||||||
|
block.call
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,13 +5,6 @@
|
|||||||
#
|
#
|
||||||
# @see BigqueryExportService
|
# @see BigqueryExportService
|
||||||
class BigqueryExportJob < ApplicationJob
|
class BigqueryExportJob < ApplicationJob
|
||||||
retry_on Exception, attempts: 0
|
|
||||||
|
|
||||||
# XXX delayed_job specific
|
|
||||||
def max_attempts
|
|
||||||
1
|
|
||||||
end
|
|
||||||
|
|
||||||
def perform(model:, **options)
|
def perform(model:, **options)
|
||||||
BigqueryExportService.new(model, **options).export!
|
BigqueryExportService.new(model, **options).export!
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,13 +3,6 @@
|
|||||||
# A job that sends notifications about new forum posts to Discord. Spawned by
|
# A job that sends notifications about new forum posts to Discord. Spawned by
|
||||||
# the {ForumPost} class when a new forum post is created.
|
# the {ForumPost} class when a new forum post is created.
|
||||||
class DiscordNotificationJob < ApplicationJob
|
class DiscordNotificationJob < ApplicationJob
|
||||||
retry_on Exception, attempts: 0
|
|
||||||
|
|
||||||
# XXX delayed_job specific
|
|
||||||
def max_attempts
|
|
||||||
1
|
|
||||||
end
|
|
||||||
|
|
||||||
def perform(forum_post:)
|
def perform(forum_post:)
|
||||||
forum_post.send_discord_notification
|
forum_post.send_discord_notification
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,13 +5,6 @@
|
|||||||
# @see {BulkUpdateRequestProcessor}
|
# @see {BulkUpdateRequestProcessor}
|
||||||
# @see {BulkUpdateRequest}
|
# @see {BulkUpdateRequest}
|
||||||
class ProcessBulkUpdateRequestJob < ApplicationJob
|
class ProcessBulkUpdateRequestJob < ApplicationJob
|
||||||
retry_on Exception, attempts: 0
|
|
||||||
|
|
||||||
# XXX delayed_job specific
|
|
||||||
def max_attempts
|
|
||||||
1
|
|
||||||
end
|
|
||||||
|
|
||||||
def perform(bulk_update_request)
|
def perform(bulk_update_request)
|
||||||
bulk_update_request.processor.process!
|
bulk_update_request.processor.process!
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# A job that runs daily to delete all stale delayed jobs. Spawned by
|
|
||||||
# {DanbooruMaintenance}.
|
|
||||||
class PruneDelayedJobsJob < ApplicationJob
|
|
||||||
def perform
|
|
||||||
Delayed::Job.where("created_at < ?", 45.days.ago).delete_all
|
|
||||||
end
|
|
||||||
end
|
|
||||||
8
app/jobs/prune_jobs_job.rb
Normal file
8
app/jobs/prune_jobs_job.rb
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# A job that runs daily to delete all stale jobs. Spawned by {DanbooruMaintenance}.
|
||||||
|
class PruneJobsJob < ApplicationJob
|
||||||
|
def perform
|
||||||
|
GoodJob::ActiveJobJob.where("created_at < ?", 7.days.ago).destroy_all
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -12,7 +12,7 @@ module DanbooruMaintenance
|
|||||||
end
|
end
|
||||||
|
|
||||||
def daily
|
def daily
|
||||||
queue PruneDelayedJobsJob
|
queue PruneJobsJob
|
||||||
queue PrunePostDisapprovalsJob
|
queue PrunePostDisapprovalsJob
|
||||||
queue PruneBulkUpdateRequestsJob
|
queue PruneBulkUpdateRequestsJob
|
||||||
queue PruneBansJob
|
queue PruneBansJob
|
||||||
|
|||||||
29
bin/good_job
Executable file
29
bin/good_job
Executable file
@@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
#
|
||||||
|
# This file was generated by Bundler.
|
||||||
|
#
|
||||||
|
# The application 'good_job' is installed as part of a gem, and
|
||||||
|
# this file is here to facilitate running it.
|
||||||
|
#
|
||||||
|
|
||||||
|
require "pathname"
|
||||||
|
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||||
|
Pathname.new(__FILE__).realpath)
|
||||||
|
|
||||||
|
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||||
|
|
||||||
|
if File.file?(bundle_binstub)
|
||||||
|
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||||
|
load(bundle_binstub)
|
||||||
|
else
|
||||||
|
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||||
|
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
require "rubygems"
|
||||||
|
require "bundler/setup"
|
||||||
|
|
||||||
|
load Gem.bin_path("good_job", "good_job")
|
||||||
@@ -45,7 +45,7 @@ Rails.application.configure do
|
|||||||
# config.cache_store = :mem_cache_store
|
# config.cache_store = :mem_cache_store
|
||||||
|
|
||||||
# Use a real queuing backend for Active Job (and separate queues per environment).
|
# Use a real queuing backend for Active Job (and separate queues per environment).
|
||||||
config.active_job.queue_adapter = :delayed_job
|
config.active_job.queue_adapter = :good_job
|
||||||
# config.active_job.queue_name_prefix = "danbooru_production"
|
# config.active_job.queue_name_prefix = "danbooru_production"
|
||||||
|
|
||||||
config.action_mailer.perform_caching = false
|
config.action_mailer.perform_caching = false
|
||||||
|
|||||||
5
config/initializers/good_job.rb
Normal file
5
config/initializers/good_job.rb
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
GoodJob.retry_on_unhandled_error = false
|
||||||
|
GoodJob.preserve_job_records = true
|
||||||
|
GoodJob.on_thread_error = ->(exception) do
|
||||||
|
DanbooruLogger.log(exception)
|
||||||
|
end
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
require 'set'
|
require 'set'
|
||||||
|
|
||||||
CurrentUser.ip_addr = "127.0.0.1"
|
CurrentUser.ip_addr = "127.0.0.1"
|
||||||
Delayed::Worker.delay_jobs = false
|
|
||||||
used_names = Set.new([""])
|
used_names = Set.new([""])
|
||||||
|
|
||||||
def rand_string(n, unique = false)
|
def rand_string(n, unique = false)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ services:
|
|||||||
- "danbooru-images:/danbooru/public/data"
|
- "danbooru-images:/danbooru/public/data"
|
||||||
command: ["bash", "-c", "bin/wait-for-http http://danbooru:3000 5s && bin/rails danbooru:cron"]
|
command: ["bash", "-c", "bin/wait-for-http http://danbooru:3000 5s && bin/rails danbooru:cron"]
|
||||||
|
|
||||||
delayed_jobs:
|
jobs:
|
||||||
# We need root to write temp upload files in the images directory (/danbooru/public/data)
|
# We need root to write temp upload files in the images directory (/danbooru/public/data)
|
||||||
user: root
|
user: root
|
||||||
image: ghcr.io/danbooru/danbooru:production
|
image: ghcr.io/danbooru/danbooru:production
|
||||||
@@ -71,7 +71,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
# We need access to images so we can add/remove images to IQDB.
|
# We need access to images so we can add/remove images to IQDB.
|
||||||
- "danbooru-images:/danbooru/public/data"
|
- "danbooru-images:/danbooru/public/data"
|
||||||
command: ["bash", "-c", "bin/wait-for-http http://danbooru:3000 5s && bin/delayed_job run"]
|
command: ["bash", "-c", "bin/wait-for-http http://danbooru:3000 5s && bin/good_job start"]
|
||||||
|
|
||||||
# https://github.com/danbooru/iqdb
|
# https://github.com/danbooru/iqdb
|
||||||
# https://hub.docker.com/repository/docker/evazion/iqdb
|
# https://hub.docker.com/repository/docker/evazion/iqdb
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ namespace :danbooru do
|
|||||||
|
|
||||||
# Usage: bin/rails danbooru:reindex_iqdb
|
# Usage: bin/rails danbooru:reindex_iqdb
|
||||||
#
|
#
|
||||||
# Schedules all posts to be reindexed in IQDB. Requires the delayed jobs
|
# Schedules all posts to be reindexed in IQDB. Requires the jobs
|
||||||
# worker (bin/delayed_job) to be running.
|
# worker (bin/good_job) to be running.
|
||||||
desc "Reindex all posts in IQDB"
|
desc "Reindex all posts in IQDB"
|
||||||
task reindex_iqdb: :environment do
|
task reindex_iqdb: :environment do
|
||||||
Post.find_each do |post|
|
Post.find_each do |post|
|
||||||
|
|||||||
Reference in New Issue
Block a user