maintenance: break maintenance tasks into individual jobs.
Break the hourly/daily/weekly/monthly maintenance tasks down into individual delayed jobs. This way if one task fails, it won't prevent other tasks from running. Also, jobs can be run in parallel, and can be individually retried if they fail.
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
# A job that runs daily to export all tables to BigQuery. Spawned by {DanbooruMaintenance}.
|
||||||
|
class BigqueryExportAllJob < ApplicationJob
|
||||||
|
def perform
|
||||||
|
BigqueryExportService.async_export_all!
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# A job that runs weekly to warn inactive approvers before they're demoted.
|
||||||
|
# Spawned by {DanbooruMaintenance}.
|
||||||
|
class DmailInactiveApproversJob < ApplicationJob
|
||||||
|
def perform
|
||||||
|
ApproverPruner.dmail_inactive_approvers!
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# A job that runs monthly to demote all inactive approvers. Spawned by
|
||||||
|
# {DanbooruMaintenance}.
|
||||||
|
class PruneApproversJob < ApplicationJob
|
||||||
|
def perform
|
||||||
|
ApproverPruner.prune!
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# A job that runs daily to remove expired bans. Spawned by
|
||||||
|
# {DanbooruMaintenance}.
|
||||||
|
class PruneBansJob < ApplicationJob
|
||||||
|
def perform
|
||||||
|
Ban.prune!
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# A job that runs daily to reject expired bulk update requests. Spawned by
|
||||||
|
# {DanbooruMaintenance}.
|
||||||
|
class PruneBulkUpdateRequestsJob < ApplicationJob
|
||||||
|
def perform
|
||||||
|
BulkUpdateRequestPruner.warn_old
|
||||||
|
BulkUpdateRequestPruner.reject_expired
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# 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
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# A job that runs daily to remove old post disapprovals. Spawned by
|
||||||
|
# {DanbooruMaintenance}.
|
||||||
|
class PrunePostDisapprovalsJob < ApplicationJob
|
||||||
|
def perform
|
||||||
|
PostDisapproval.prune!
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# A job that runs hourly to delete all expired pending, flagged, and appealed
|
||||||
|
# posts. Spawned by {DanbooruMaintenance}.
|
||||||
|
class PrunePostsJob < ApplicationJob
|
||||||
|
def perform
|
||||||
|
PostPruner.prune!
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# A job that runs hourly to delete all state rate limit objects from the
|
||||||
|
# database. Spawned by {DanbooruMaintenance}.
|
||||||
|
class PruneRateLimitsJob < ApplicationJob
|
||||||
|
def perform
|
||||||
|
RateLimit.prune!
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# A job that runs hourly to delete all completed, stale, or failed uploads.
|
||||||
|
# Spawned by {DanbooruMaintenance}.
|
||||||
|
class PruneUploadsJob < ApplicationJob
|
||||||
|
def perform
|
||||||
|
Upload.prune!
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# A job that runs hourly to fix all incorrect tag counts.
|
||||||
|
# Spawned by {DanbooruMaintenance}.
|
||||||
|
class RegeneratePostCountsJob < ApplicationJob
|
||||||
|
def perform
|
||||||
|
updated_tags = Tag.regenerate_post_counts!
|
||||||
|
updated_tags.each do |tag|
|
||||||
|
DanbooruLogger.info("Updated tag count", tag.attributes)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# A job that runs weekly to retire inactive aliases and implications. Spawned
|
||||||
|
# by {DanbooruMaintenance}.
|
||||||
|
class RetireTagRelationshipsJob < ApplicationJob
|
||||||
|
def perform
|
||||||
|
TagRelationshipRetirementService.find_and_retire!
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# A job that runs daily to vacuum the database. Spawned by {DanbooruMaintenance}.
|
||||||
|
class VacuumDatabaseJob < ApplicationJob
|
||||||
|
def perform
|
||||||
|
# We can't perform vacuum inside a transaction. This happens during tests.
|
||||||
|
return if ApplicationRecord.connection.transaction_open?
|
||||||
|
ApplicationRecord.connection.execute("vacuum analyze")
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -2,45 +2,34 @@ module DanbooruMaintenance
|
|||||||
module_function
|
module_function
|
||||||
|
|
||||||
def hourly
|
def hourly
|
||||||
safely { Upload.prune! }
|
queue PruneUploadsJob
|
||||||
safely { PostPruner.prune! }
|
queue PrunePostsJob
|
||||||
safely { RateLimit.prune! }
|
queue PruneRateLimitsJob
|
||||||
safely { regenerate_post_counts! }
|
queue RegeneratePostCountsJob
|
||||||
end
|
end
|
||||||
|
|
||||||
def daily
|
def daily
|
||||||
safely { Delayed::Job.where('created_at < ?', 45.days.ago).delete_all }
|
queue PruneDelayedJobsJob
|
||||||
safely { PostDisapproval.prune! }
|
queue PrunePostDisapprovalsJob
|
||||||
safely { BulkUpdateRequestPruner.warn_old }
|
queue PruneBulkUpdateRequestsJob
|
||||||
safely { BulkUpdateRequestPruner.reject_expired }
|
queue PruneBansJob
|
||||||
safely { Ban.prune! }
|
queue BigqueryExportAllJob
|
||||||
safely { BigqueryExportService.async_export_all! }
|
queue VacuumDatabaseJob
|
||||||
safely { ActiveRecord::Base.connection.execute("vacuum analyze") unless Rails.env.test? }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def weekly
|
def weekly
|
||||||
safely { TagRelationshipRetirementService.find_and_retire! }
|
queue RetireTagRelationshipsJob
|
||||||
safely { ApproverPruner.dmail_inactive_approvers! }
|
queue DmailInactiveApproversJob
|
||||||
end
|
end
|
||||||
|
|
||||||
def monthly
|
def monthly
|
||||||
safely { ApproverPruner.prune! }
|
queue PruneApproversJob
|
||||||
end
|
end
|
||||||
|
|
||||||
def regenerate_post_counts!
|
def queue(job)
|
||||||
updated_tags = Tag.regenerate_post_counts!
|
DanbooruLogger.info("Queueing #{job.name}")
|
||||||
updated_tags.each do |tag|
|
job.perform_later
|
||||||
DanbooruLogger.info("Updated tag count", tag.attributes)
|
rescue Exception # rubocop:disable Lint/RescueException
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def safely(&block)
|
|
||||||
ActiveRecord::Base.connection.execute("set statement_timeout = 0")
|
|
||||||
|
|
||||||
CurrentUser.scoped(User.system, "127.0.0.1") do
|
|
||||||
yield
|
|
||||||
end
|
|
||||||
rescue StandardError => exception
|
|
||||||
DanbooruLogger.log(exception)
|
DanbooruLogger.log(exception)
|
||||||
raise exception if Rails.env.test?
|
raise exception if Rails.env.test?
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class PruneBansJobTest < ActiveJob::TestCase
|
||||||
|
context "PruneBansJob" do
|
||||||
|
should "prune all expired bans" do
|
||||||
|
@expired_ban = travel_to(1.month.ago) { create(:ban, duration: 1.week) }
|
||||||
|
@unexpired_ban = create(:ban, duration: 1.week)
|
||||||
|
|
||||||
|
assert_equal(true, @expired_ban.user.is_banned?)
|
||||||
|
assert_equal(true, @unexpired_ban.user.is_banned?)
|
||||||
|
|
||||||
|
PruneBansJob.perform_now
|
||||||
|
|
||||||
|
assert_equal(false, @expired_ban.user.reload.is_banned?)
|
||||||
|
assert_equal(true, @unexpired_ban.user.reload.is_banned?)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class PrunePostsJobTest < ActiveJob::TestCase
|
||||||
|
context "PrunePostsJob" do
|
||||||
|
should "prune expired posts" do
|
||||||
|
@pending = create(:post, is_pending: true, created_at: 5.days.ago)
|
||||||
|
@flagged = create(:post, is_flagged: true, created_at: 5.days.ago)
|
||||||
|
@appealed = create(:post, is_deleted: true, created_at: 5.days.ago)
|
||||||
|
|
||||||
|
@flag = create(:post_flag, post: @flagged, created_at: 4.days.ago)
|
||||||
|
@appeal = create(:post_appeal, post: @appealed, created_at: 4.days.ago)
|
||||||
|
|
||||||
|
PrunePostsJob.perform_now
|
||||||
|
|
||||||
|
assert_equal(true, @pending.reload.is_deleted?)
|
||||||
|
assert_equal(true, @flagged.reload.is_deleted?)
|
||||||
|
assert_equal(true, @appealed.reload.is_deleted?)
|
||||||
|
assert_equal(true, @flag.reload.succeeded?)
|
||||||
|
assert_equal(true, @appeal.reload.rejected?)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class PruneRateLimitsJobTest < ActiveJob::TestCase
|
||||||
|
context "PruneRateLimitsJob" do
|
||||||
|
should "prune all stale rate limits" do
|
||||||
|
travel_to(2.hours.ago) { create(:rate_limit) }
|
||||||
|
|
||||||
|
assert_equal(1, RateLimit.count)
|
||||||
|
PruneRateLimitsJob.perform_now
|
||||||
|
assert_equal(0, RateLimit.count)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class PruneUploadsJobTest < ActiveJob::TestCase
|
||||||
|
context "PruneUploadsJob" do
|
||||||
|
should "prune all old uploads" do
|
||||||
|
@uploader = create(:user)
|
||||||
|
|
||||||
|
as(@uploader) do
|
||||||
|
@completed_upload = travel_to(2.hours.ago) { create(:upload, uploader: @uploader, status: "completed") }
|
||||||
|
@stale_upload = travel_to(2.days.ago) { create(:upload, uploader: @uploader, status: "preprocessed") }
|
||||||
|
@failed_upload = travel_to(4.days.ago) { create(:upload, uploader: @uploader, status: "error") }
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal(3, Upload.count)
|
||||||
|
PruneUploadsJob.perform_now
|
||||||
|
assert_equal(0, Upload.count)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class RegeneratePostCountsJobTest < ActiveJob::TestCase
|
||||||
|
context "RegeneratePostCountsJob" do
|
||||||
|
should "regenerate all incorrect tag post counts" do
|
||||||
|
tag1 = create(:tag, name: "touhou", post_count: -10)
|
||||||
|
tag2 = create(:tag, name: "bkub", post_count: 10)
|
||||||
|
tag3 = create(:tag, name: "chen", post_count: 10)
|
||||||
|
post = create(:post, tag_string: "touhou bkub")
|
||||||
|
|
||||||
|
RegeneratePostCountsJob.perform_now
|
||||||
|
|
||||||
|
assert_equal(1, Tag.find_by_name!("touhou").post_count)
|
||||||
|
assert_equal(1, Tag.find_by_name!("bkub").post_count)
|
||||||
|
assert_equal(0, Tag.find_by_name!("chen").post_count)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -3,38 +3,36 @@ require 'test_helper'
|
|||||||
class DanbooruMaintenanceTest < ActiveSupport::TestCase
|
class DanbooruMaintenanceTest < ActiveSupport::TestCase
|
||||||
context "hourly maintenance" do
|
context "hourly maintenance" do
|
||||||
should "work" do
|
should "work" do
|
||||||
assert_nothing_raised { DanbooruMaintenance.hourly }
|
assert_nothing_raised do
|
||||||
end
|
DanbooruMaintenance.hourly
|
||||||
|
perform_enqueued_jobs
|
||||||
should "prune expired posts" do
|
end
|
||||||
@pending = create(:post, is_pending: true, created_at: 5.days.ago)
|
|
||||||
@flagged = create(:post, is_flagged: true, created_at: 5.days.ago)
|
|
||||||
@appealed = create(:post, is_deleted: true, created_at: 5.days.ago)
|
|
||||||
|
|
||||||
@flag = create(:post_flag, post: @flagged, created_at: 4.days.ago)
|
|
||||||
@appeal = create(:post_appeal, post: @appealed, created_at: 4.days.ago)
|
|
||||||
|
|
||||||
DanbooruMaintenance.hourly
|
|
||||||
|
|
||||||
assert_equal(true, @pending.reload.is_deleted?)
|
|
||||||
assert_equal(true, @flagged.reload.is_deleted?)
|
|
||||||
assert_equal(true, @appealed.reload.is_deleted?)
|
|
||||||
assert_equal(true, @flag.reload.succeeded?)
|
|
||||||
assert_equal(true, @appeal.reload.rejected?)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "hourly maintenance" do
|
context "daily maintenance" do
|
||||||
context "when pruning bans" do
|
should "work" do
|
||||||
should "clear the is_banned flag for users who are no longer banned" do
|
assert_nothing_raised do
|
||||||
banner = FactoryBot.create(:admin_user)
|
DanbooruMaintenance.daily
|
||||||
user = FactoryBot.create(:user)
|
perform_enqueued_jobs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
as(banner) { create(:ban, user: user, banner: banner, duration: 1) }
|
context "weekly maintenance" do
|
||||||
|
should "work" do
|
||||||
|
assert_nothing_raised do
|
||||||
|
DanbooruMaintenance.weekly
|
||||||
|
perform_enqueued_jobs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
assert_equal(true, user.reload.is_banned)
|
context "monthly maintenance" do
|
||||||
travel_to(2.days.from_now) { DanbooruMaintenance.daily }
|
should "work" do
|
||||||
assert_equal(false, user.reload.is_banned)
|
assert_nothing_raised do
|
||||||
|
DanbooruMaintenance.monthly
|
||||||
|
perform_enqueued_jobs
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -194,19 +194,4 @@ class TagTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "A tag with an incorrect post count" do
|
|
||||||
should "be fixed" do
|
|
||||||
tag1 = FactoryBot.create(:tag, name: "touhou", post_count: -10)
|
|
||||||
tag2 = FactoryBot.create(:tag, name: "bkub", post_count: 10)
|
|
||||||
tag3 = FactoryBot.create(:tag, name: "chen", post_count: 10)
|
|
||||||
post = FactoryBot.create(:post, tag_string: "touhou bkub")
|
|
||||||
|
|
||||||
tags = Tag.regenerate_post_counts!
|
|
||||||
assert_equal(3, tags.size)
|
|
||||||
assert_equal(1, tag1.reload.post_count)
|
|
||||||
assert_equal(1, tag2.reload.post_count)
|
|
||||||
assert_equal(0, tag3.reload.post_count)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user