mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 18:03:43 +08:00
PERF: Disable Sidekiq only during database restore (#10857)
It pauses Sidekiq, clears Redis (namespaced to the current site), clears Sidekiq jobs for the current site, restores the database and unpauses Sidekiq. Previously it stayed paused until the end of the restore. Redis is cleared because we don't want any old data lying around (e.g. old Sidekiq jobs). Most data in Redis is prefixed with the name of the multisite, but Sidekiq jobs in a multisite are all stored in the same keys. So, deleting those jobs requires a little bit more logic.
This commit is contained in:
parent
43e52a7dc1
commit
d5ef6188ed
|
@ -43,13 +43,16 @@ module BackupRestore
|
||||||
validate_backup_metadata
|
validate_backup_metadata
|
||||||
|
|
||||||
@system.enable_readonly_mode
|
@system.enable_readonly_mode
|
||||||
@system.pause_sidekiq
|
@system.pause_sidekiq("restore")
|
||||||
@system.wait_for_sidekiq
|
@system.wait_for_sidekiq
|
||||||
|
@system.flush_redis
|
||||||
|
@system.clear_sidekiq_queues
|
||||||
|
|
||||||
@database_restorer.restore(db_dump_path)
|
@database_restorer.restore(db_dump_path)
|
||||||
|
|
||||||
reload_site_settings
|
reload_site_settings
|
||||||
|
|
||||||
|
@system.unpause_sidekiq
|
||||||
@system.disable_readonly_mode
|
@system.disable_readonly_mode
|
||||||
|
|
||||||
clear_category_cache
|
clear_category_cache
|
||||||
|
|
|
@ -54,12 +54,16 @@ module BackupRestore
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def pause_sidekiq
|
def pause_sidekiq(reason)
|
||||||
|
return if Sidekiq.paused?
|
||||||
|
|
||||||
log "Pausing sidekiq..."
|
log "Pausing sidekiq..."
|
||||||
Sidekiq.pause!
|
Sidekiq.pause!(reason)
|
||||||
end
|
end
|
||||||
|
|
||||||
def unpause_sidekiq
|
def unpause_sidekiq
|
||||||
|
return unless Sidekiq.paused?
|
||||||
|
|
||||||
log "Unpausing sidekiq..."
|
log "Unpausing sidekiq..."
|
||||||
Sidekiq.unpause!
|
Sidekiq.unpause!
|
||||||
rescue => ex
|
rescue => ex
|
||||||
|
@ -88,6 +92,23 @@ module BackupRestore
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def flush_redis
|
||||||
|
redis = Discourse.redis
|
||||||
|
redis.scan_each(match: "*") do |key|
|
||||||
|
redis.del(key) unless key == SidekiqPauser::PAUSED_KEY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear_sidekiq_queues
|
||||||
|
Sidekiq::Queue.all.each do |queue|
|
||||||
|
queue.each { |job| delete_job_if_it_belongs_to_current_site(job) }
|
||||||
|
end
|
||||||
|
|
||||||
|
Sidekiq::RetrySet.new.each { |job| delete_job_if_it_belongs_to_current_site(job) }
|
||||||
|
Sidekiq::ScheduledSet.new.each { |job| delete_job_if_it_belongs_to_current_site(job) }
|
||||||
|
Sidekiq::DeadSet.new.each { |job| delete_job_if_it_belongs_to_current_site(job) }
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def sidekiq_has_running_jobs?
|
def sidekiq_has_running_jobs?
|
||||||
|
@ -100,5 +121,9 @@ module BackupRestore
|
||||||
|
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delete_job_if_it_belongs_to_current_site(job)
|
||||||
|
job.delete if job.args.first&.fetch("current_site_id", nil) == @current_db
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,7 @@ describe BackupRestore::SystemInterface do
|
||||||
|
|
||||||
context "readonly mode" do
|
context "readonly mode" do
|
||||||
after do
|
after do
|
||||||
Discourse::READONLY_KEYS.each { |key| $redis.del(key) }
|
Discourse::READONLY_KEYS.each { |key| Discourse.redis.del(key) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#enable_readonly_mode" do
|
describe "#enable_readonly_mode" do
|
||||||
|
@ -81,16 +81,23 @@ describe BackupRestore::SystemInterface do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#pause_sidekiq" do
|
describe "#pause_sidekiq" do
|
||||||
|
after { Sidekiq.unpause! }
|
||||||
|
|
||||||
it "calls pause!" do
|
it "calls pause!" do
|
||||||
Sidekiq.expects(:pause!).once
|
expect(Sidekiq.paused?).to eq(false)
|
||||||
subject.pause_sidekiq
|
subject.pause_sidekiq("my reason")
|
||||||
|
expect(Sidekiq.paused?).to eq(true)
|
||||||
|
expect(Discourse.redis.get(SidekiqPauser::PAUSED_KEY)).to eq("my reason")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#unpause_sidekiq" do
|
describe "#unpause_sidekiq" do
|
||||||
it "calls unpause!" do
|
it "calls unpause!" do
|
||||||
Sidekiq.expects(:unpause!).once
|
Sidekiq.pause!
|
||||||
|
expect(Sidekiq.paused?).to eq(true)
|
||||||
|
|
||||||
subject.unpause_sidekiq
|
subject.unpause_sidekiq
|
||||||
|
expect(Sidekiq.paused?).to eq(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -101,12 +108,16 @@ describe BackupRestore::SystemInterface do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with Sidekiq workers" do
|
context "with Sidekiq workers" do
|
||||||
before { $redis.flushall }
|
before { flush_sidekiq_redis_namespace }
|
||||||
after { $redis.flushall }
|
after { flush_sidekiq_redis_namespace }
|
||||||
|
|
||||||
|
def flush_sidekiq_redis_namespace
|
||||||
|
Sidekiq.redis do |redis|
|
||||||
|
redis.scan_each { |key| Discourse.redis.del(key) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def create_workers(site_id: nil, all_sites: false)
|
def create_workers(site_id: nil, all_sites: false)
|
||||||
$redis.flushall
|
|
||||||
|
|
||||||
payload = Sidekiq::Testing.fake! do
|
payload = Sidekiq::Testing.fake! do
|
||||||
data = { post_id: 1 }
|
data = { post_id: 1 }
|
||||||
|
|
||||||
|
@ -157,5 +168,46 @@ describe BackupRestore::SystemInterface do
|
||||||
subject.wait_for_sidekiq
|
subject.wait_for_sidekiq
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "flush_redis" do
|
||||||
|
context "Sidekiq" do
|
||||||
|
after { Sidekiq.unpause! }
|
||||||
|
|
||||||
|
it "doesn't unpause Sidekiq" do
|
||||||
|
Sidekiq.pause!
|
||||||
|
subject.flush_redis
|
||||||
|
|
||||||
|
expect(Sidekiq.paused?).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "removes only keys from the current site in a multisite", type: :multisite do
|
||||||
|
test_multisite_connection("default") do
|
||||||
|
Discourse.redis.set("foo", "default-foo")
|
||||||
|
Discourse.redis.set("bar", "default-bar")
|
||||||
|
|
||||||
|
expect(Discourse.redis.get("foo")).to eq("default-foo")
|
||||||
|
expect(Discourse.redis.get("bar")).to eq("default-bar")
|
||||||
|
end
|
||||||
|
|
||||||
|
test_multisite_connection("second") do
|
||||||
|
Discourse.redis.set("foo", "second-foo")
|
||||||
|
Discourse.redis.set("bar", "second-bar")
|
||||||
|
|
||||||
|
expect(Discourse.redis.get("foo")).to eq("second-foo")
|
||||||
|
expect(Discourse.redis.get("bar")).to eq("second-bar")
|
||||||
|
|
||||||
|
subject.flush_redis
|
||||||
|
|
||||||
|
expect(Discourse.redis.get("foo")).to be_nil
|
||||||
|
expect(Discourse.redis.get("bar")).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
test_multisite_connection("default") do
|
||||||
|
expect(Discourse.redis.get("foo")).to eq("default-foo")
|
||||||
|
expect(Discourse.redis.get("bar")).to eq("default-bar")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user