switchman-inst-jobs 3.0.4 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/db/migrate/20101216224513_create_delayed_jobs.rb +1 -1
- data/db/migrate/20110208031356_add_delayed_jobs_tag.rb +1 -1
- data/db/migrate/20110426161613_add_delayed_jobs_max_attempts.rb +1 -1
- data/db/migrate/20110516225834_add_delayed_jobs_strand.rb +1 -1
- data/db/migrate/20110531144916_cleanup_delayed_jobs_indexes.rb +1 -1
- data/db/migrate/20110610213249_optimize_delayed_jobs.rb +1 -1
- data/db/migrate/20110831210257_add_delayed_jobs_next_in_strand.rb +1 -1
- data/db/migrate/20120510004759_delayed_jobs_delete_trigger_lock_for_update.rb +1 -1
- data/db/migrate/20120531150712_drop_psql_jobs_pop_fn.rb +1 -1
- data/db/migrate/20120607164022_delayed_jobs_use_advisory_locks.rb +1 -1
- data/db/migrate/20120607181141_index_jobs_on_locked_by.rb +2 -2
- data/db/migrate/20120608191051_add_jobs_run_at_index.rb +2 -2
- data/db/migrate/20120927184213_change_delayed_jobs_handler_to_text.rb +1 -1
- data/db/migrate/20140505215131_add_failed_jobs_original_job_id.rb +1 -1
- data/db/migrate/20140505215510_copy_failed_jobs_original_id.rb +2 -2
- data/db/migrate/20140505223637_drop_failed_jobs_original_id.rb +1 -1
- data/db/migrate/20140512213941_add_source_to_jobs.rb +1 -1
- data/db/migrate/20150807133223_add_max_concurrent_to_jobs.rb +1 -1
- data/db/migrate/20151123210429_add_expires_at_to_jobs.rb +1 -1
- data/db/migrate/20151210162949_improve_max_concurrent.rb +1 -1
- data/db/migrate/20161206323555_add_back_default_string_limits_jobs.rb +1 -1
- data/db/migrate/20170308045400_add_shard_id_to_delayed_jobs.rb +1 -11
- data/db/migrate/20170308045401_add_delayed_jobs_shard_id_to_switchman_shards.rb +5 -0
- data/db/migrate/20181217155351_speed_up_max_concurrent_triggers.rb +1 -1
- data/db/migrate/20190726154743_make_critical_columns_not_null.rb +1 -1
- data/db/migrate/20200330230722_add_id_to_get_delayed_jobs_index.rb +2 -2
- data/db/migrate/20200824222232_speed_up_max_concurrent_delete_trigger.rb +1 -1
- data/db/migrate/20200825011002_add_strand_order_override.rb +2 -2
- data/lib/switchman_inst_jobs.rb +5 -0
- data/lib/switchman_inst_jobs/active_record/connection_adapters/connection_pool.rb +15 -0
- data/lib/switchman_inst_jobs/delayed/backend/base.rb +13 -11
- data/lib/switchman_inst_jobs/delayed/pool.rb +1 -1
- data/lib/switchman_inst_jobs/delayed/settings.rb +9 -0
- data/lib/switchman_inst_jobs/delayed/worker.rb +2 -2
- data/lib/switchman_inst_jobs/delayed/worker/health_check.rb +13 -14
- data/lib/switchman_inst_jobs/engine.rb +6 -4
- data/lib/switchman_inst_jobs/jobs_migrator.rb +64 -47
- data/lib/switchman_inst_jobs/switchman/shard.rb +17 -23
- data/lib/switchman_inst_jobs/version.rb +1 -1
- metadata +38 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1e76d31061b35e05fb85de802c868f5244c386f39490b236b7e2fa48160c1b4d
|
4
|
+
data.tar.gz: 81d2c12f67012e962b7d45157f5df7f045a3f4f0c45df2c9294661125524f234
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb73f1c14a052801826a5f95cdfda52ecd73703cdc1b82b320a4b4ba69ba5ef2b4f77f01d41b1bec667df5b16a02529d8297f7de5726a392e65e086e0ad81570
|
7
|
+
data.tar.gz: 6b806f4e8e1e74e589090f75e54de9f12fe3e6ac5f97fc08296adc1c998055a082f7f815d0f7a6362d4745770e7791db100f640e6dadba5c8196ed0e940109b4
|
@@ -1,8 +1,8 @@
|
|
1
1
|
class IndexJobsOnLockedBy < ActiveRecord::Migration[4.2]
|
2
|
-
disable_ddl_transaction!
|
2
|
+
disable_ddl_transaction!
|
3
3
|
|
4
4
|
def connection
|
5
|
-
Delayed::Backend::ActiveRecord::
|
5
|
+
Delayed::Backend::ActiveRecord::AbstractJob.connection
|
6
6
|
end
|
7
7
|
|
8
8
|
def up
|
@@ -1,8 +1,8 @@
|
|
1
1
|
class AddJobsRunAtIndex < ActiveRecord::Migration[4.2]
|
2
|
-
disable_ddl_transaction!
|
2
|
+
disable_ddl_transaction!
|
3
3
|
|
4
4
|
def connection
|
5
|
-
Delayed::Backend::ActiveRecord::
|
5
|
+
Delayed::Backend::ActiveRecord::AbstractJob.connection
|
6
6
|
end
|
7
7
|
|
8
8
|
def up
|
@@ -1,11 +1,11 @@
|
|
1
1
|
class CopyFailedJobsOriginalId < ActiveRecord::Migration[4.2]
|
2
2
|
def connection
|
3
|
-
Delayed::Backend::ActiveRecord::
|
3
|
+
Delayed::Backend::ActiveRecord::AbstractJob.connection
|
4
4
|
end
|
5
5
|
|
6
6
|
def up
|
7
7
|
# this is a smaller, less frequently accessed table, so we just update all at once
|
8
|
-
Delayed::Backend::ActiveRecord::Job::Failed.where(
|
8
|
+
Delayed::Backend::ActiveRecord::Job::Failed.where(original_job_id: nil).update_all('original_job_id = original_id')
|
9
9
|
end
|
10
10
|
|
11
11
|
def down; end
|
@@ -2,7 +2,7 @@ class AddShardIdToDelayedJobs < ActiveRecord::Migration[4.2]
|
|
2
2
|
disable_ddl_transaction!
|
3
3
|
|
4
4
|
def connection
|
5
|
-
Delayed::Backend::ActiveRecord::
|
5
|
+
Delayed::Backend::ActiveRecord::AbstractJob.connection
|
6
6
|
end
|
7
7
|
|
8
8
|
def up
|
@@ -11,19 +11,9 @@ class AddShardIdToDelayedJobs < ActiveRecord::Migration[4.2]
|
|
11
11
|
|
12
12
|
add_column :failed_jobs, :shard_id, :integer, limit: 8
|
13
13
|
add_index :failed_jobs, :shard_id, algorithm: :concurrently
|
14
|
-
|
15
|
-
add_column :switchman_shards, :delayed_jobs_shard_id, :integer, limit: 8
|
16
|
-
add_foreign_key(
|
17
|
-
:switchman_shards,
|
18
|
-
:switchman_shards,
|
19
|
-
column: :delayed_jobs_shard_id
|
20
|
-
)
|
21
14
|
end
|
22
15
|
|
23
16
|
def down
|
24
|
-
remove_foreign_key :switchman_shards, column: :delayed_jobs_shard_id
|
25
|
-
remove_column :switchman_shards, :delayed_jobs_shard_id
|
26
|
-
|
27
17
|
remove_index :failed_jobs, :shard_id
|
28
18
|
remove_column :failed_jobs, :shard_id
|
29
19
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
class AddIdToGetDelayedJobsIndex < ActiveRecord::Migration[4.2]
|
2
|
-
disable_ddl_transaction!
|
2
|
+
disable_ddl_transaction!
|
3
3
|
|
4
4
|
def connection
|
5
|
-
Delayed::
|
5
|
+
Delayed::Backend::ActiveRecord::AbstractJob.connection
|
6
6
|
end
|
7
7
|
|
8
8
|
def up
|
@@ -1,8 +1,8 @@
|
|
1
1
|
class AddStrandOrderOverride < ActiveRecord::Migration[4.2]
|
2
|
-
disable_ddl_transaction!
|
2
|
+
disable_ddl_transaction!
|
3
3
|
|
4
4
|
def connection
|
5
|
-
Delayed::
|
5
|
+
Delayed::Backend::ActiveRecord::AbstractJob.connection
|
6
6
|
end
|
7
7
|
|
8
8
|
def up
|
data/lib/switchman_inst_jobs.rb
CHANGED
@@ -5,6 +5,9 @@ module SwitchmanInstJobs
|
|
5
5
|
cattr_accessor :delayed_jobs_shard_fallback
|
6
6
|
|
7
7
|
def self.initialize_active_record
|
8
|
+
::ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(
|
9
|
+
ActiveRecord::ConnectionAdapters::ConnectionPool
|
10
|
+
)
|
8
11
|
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(
|
9
12
|
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
10
13
|
)
|
@@ -36,8 +39,10 @@ module SwitchmanInstJobs
|
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
42
|
+
require 'switchman_inst_jobs/active_record/connection_adapters/connection_pool'
|
39
43
|
require 'switchman_inst_jobs/active_record/connection_adapters/postgresql_adapter'
|
40
44
|
require 'switchman_inst_jobs/active_record/migration'
|
45
|
+
require 'switchman_inst_jobs/delayed/settings'
|
41
46
|
require 'switchman_inst_jobs/delayed/backend/base'
|
42
47
|
require 'switchman_inst_jobs/delayed/message_sending'
|
43
48
|
require 'switchman_inst_jobs/delayed/pool'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module SwitchmanInstJobs
|
2
|
+
module ActiveRecord
|
3
|
+
module ConnectionAdapters
|
4
|
+
module ConnectionPool
|
5
|
+
def shard
|
6
|
+
if connection_klass == ::Delayed::Backend::ActiveRecord::AbstractJob
|
7
|
+
return shard_stack.last || ::Switchman::Shard.current(::ActiveRecord::Base).delayed_jobs_shard
|
8
|
+
end
|
9
|
+
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -18,40 +18,38 @@ module SwitchmanInstJobs
|
|
18
18
|
enqueue_options = options.merge(
|
19
19
|
current_shard: current_shard
|
20
20
|
)
|
21
|
-
enqueue_job = -> { ::GuardRail.activate(:
|
21
|
+
enqueue_job = -> { ::GuardRail.activate(:primary) { super(object, **enqueue_options) } }
|
22
22
|
|
23
23
|
# Another dj shard must be currently manually activated, so just use that
|
24
24
|
# In general this will only happen in unusual circumstances like tests
|
25
25
|
# also if migrations are running, always use the current shard's job shard
|
26
26
|
if ::ActiveRecord::Migration.open_migrations.zero? &&
|
27
|
-
current_shard.delayed_jobs_shard !=
|
27
|
+
current_shard.delayed_jobs_shard !=
|
28
|
+
::Switchman::Shard.current(::Delayed::Backend::ActiveRecord::AbstractJob)
|
28
29
|
enqueue_job.call
|
29
30
|
else
|
30
|
-
::Switchman::Shard.
|
31
|
-
current_shard = ::Switchman::Shard.lookup(current_shard.id)
|
32
|
-
end
|
31
|
+
current_shard = ::Switchman::Shard.lookup(current_shard.id)
|
33
32
|
current_job_shard = current_shard.delayed_jobs_shard
|
34
33
|
|
35
34
|
if (options[:singleton] || options[:strand]) && current_shard.block_stranded
|
36
35
|
enqueue_options[:next_in_strand] = false
|
37
36
|
end
|
38
37
|
|
39
|
-
current_job_shard.activate(
|
38
|
+
current_job_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
40
39
|
enqueue_job.call
|
41
40
|
end
|
42
41
|
end
|
43
42
|
end
|
44
43
|
|
45
44
|
def configured_shard_ids
|
46
|
-
|
47
|
-
map { |w| w['shard'] }.compact.uniq
|
45
|
+
::SwitchmanInstJobs::Delayed::Settings.configured_shard_ids
|
48
46
|
end
|
49
47
|
|
50
48
|
def processes_locked_locally
|
51
49
|
shard_ids = configured_shard_ids
|
52
50
|
if shard_ids.any?
|
53
51
|
shards = shard_ids.map { |shard_id| ::Delayed::Worker.shard(shard_id) }
|
54
|
-
::Switchman::Shard.with_each_shard(shards, [
|
52
|
+
::Switchman::Shard.with_each_shard(shards, [::Delayed::Backend::ActiveRecord::AbstractJob]) do
|
55
53
|
super
|
56
54
|
end
|
57
55
|
else
|
@@ -62,7 +60,9 @@ module SwitchmanInstJobs
|
|
62
60
|
|
63
61
|
def self.prepended(base)
|
64
62
|
base.singleton_class.prepend(ClassMethods)
|
65
|
-
|
63
|
+
return unless base.name == 'Delayed::Backend::ActiveRecord::Job'
|
64
|
+
|
65
|
+
::Delayed::Backend::ActiveRecord::AbstractJob.sharded_model
|
66
66
|
end
|
67
67
|
|
68
68
|
def current_shard
|
@@ -82,6 +82,8 @@ module SwitchmanInstJobs
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def invoke_job
|
85
|
+
raise ShardNotFoundError, shard_id unless current_shard
|
86
|
+
|
85
87
|
current_shard.activate { super }
|
86
88
|
end
|
87
89
|
|
@@ -89,7 +91,7 @@ module SwitchmanInstJobs
|
|
89
91
|
raise ShardNotFoundError, shard_id unless current_shard
|
90
92
|
|
91
93
|
current_shard.activate { super }
|
92
|
-
rescue
|
94
|
+
rescue PG::ConnectionBad, PG::UndefinedTable
|
93
95
|
# likely a missing shard with a stale cache
|
94
96
|
current_shard.send(:clear_cache)
|
95
97
|
::Switchman::Shard.clear_cache
|
@@ -20,7 +20,7 @@ module SwitchmanInstJobs
|
|
20
20
|
shard_ids = @config[:workers].pluck(:shard).uniq
|
21
21
|
shards = shard_ids.map { |shard_id| ::Delayed::Worker.shard(shard_id) }
|
22
22
|
end
|
23
|
-
::Switchman::Shard.with_each_shard(shards, [
|
23
|
+
::Switchman::Shard.with_each_shard(shards, [::Delayed::Backend::ActiveRecord::AbstractJob]) do
|
24
24
|
super
|
25
25
|
end
|
26
26
|
end
|
@@ -16,14 +16,14 @@ module SwitchmanInstJobs
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def start
|
19
|
-
shard.activate(
|
19
|
+
shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) { super }
|
20
20
|
end
|
21
21
|
|
22
22
|
# Worker#run is usually only called from Worker#start, but if the worker
|
23
23
|
# is called directly from the console, we want to make sure it still gets
|
24
24
|
# the right shard activated.
|
25
25
|
def run
|
26
|
-
shard.activate(
|
26
|
+
shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) { super }
|
27
27
|
end
|
28
28
|
|
29
29
|
def shard
|
@@ -18,22 +18,21 @@ module SwitchmanInstJobs
|
|
18
18
|
::Delayed::Settings.worker_health_check_config['service_name'] = original_service_name
|
19
19
|
end
|
20
20
|
|
21
|
-
def reschedule_abandoned_jobs
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
def reschedule_abandoned_jobs
|
22
|
+
shard_ids = ::SwitchmanInstJobs::Delayed::Settings.configured_shard_ids
|
23
|
+
shards = shard_ids.map { |shard_id| ::Delayed::Worker.shard(shard_id) }
|
24
|
+
::Switchman::Shard.with_each_shard(shards,
|
25
|
+
[::ActiveRecord::Base, ::Delayed::Backend::ActiveRecord::AbstractJob]) do
|
26
|
+
munge_service_name(::Switchman::Shard.current) do
|
27
|
+
# because this rescheduling process is running on every host, we need
|
28
|
+
# to make sure that it's functioning for each shard the current
|
29
|
+
# host is programmed to interact with, but ONLY for those shards.
|
30
|
+
# reading the config lets us iterate over any shards this host should
|
31
|
+
# work with and lets us pick the correct service name to identify which
|
32
|
+
# hosts are currently alive and valid via the health checks
|
33
|
+
super()
|
27
34
|
end
|
28
35
|
end
|
29
|
-
|
30
|
-
::Switchman::Shard.with_each_shard(shards, [:delayed_jobs], exception: :ignore) do
|
31
|
-
shard = ::Switchman::Shard.current(:delayed_jobs)
|
32
|
-
singleton = <<~SINGLETON
|
33
|
-
periodic: Delayed::Worker::HealthCheck.reschedule_abandoned_jobs:#{shard.id}
|
34
|
-
SINGLETON
|
35
|
-
delay(singleton: singleton).reschedule_abandoned_jobs(call_super: shard)
|
36
|
-
end
|
37
36
|
end
|
38
37
|
end
|
39
38
|
end
|
@@ -11,7 +11,9 @@ module SwitchmanInstJobs
|
|
11
11
|
|
12
12
|
::Delayed::Worker.lifecycle.around(:work_queue_pop) do |worker, config, &block|
|
13
13
|
if config[:shard]
|
14
|
-
::Switchman::Shard.lookup(config[:shard]).activate(
|
14
|
+
::Switchman::Shard.lookup(config[:shard]).activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
15
|
+
block.call(worker, config)
|
16
|
+
end
|
15
17
|
else
|
16
18
|
block.call(worker, config)
|
17
19
|
end
|
@@ -23,8 +25,8 @@ module SwitchmanInstJobs
|
|
23
25
|
::Switchman::Shard.clear_cache
|
24
26
|
::Switchman::Shard.default.activate do
|
25
27
|
current_job_shard = ::Switchman::Shard.lookup(job.shard_id).delayed_jobs_shard
|
26
|
-
if current_job_shard != ::Switchman::Shard.current(
|
27
|
-
current_job_shard.activate(
|
28
|
+
if current_job_shard != ::Switchman::Shard.current(::Delayed::Backend::ActiveRecord::AbstractJob)
|
29
|
+
current_job_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
28
30
|
j = ::Delayed::Job.where(strand: job.strand).next_in_strand_order.first
|
29
31
|
j.update_column(:next_in_strand, true) if j && !j.next_in_strand
|
30
32
|
end
|
@@ -43,7 +45,7 @@ module SwitchmanInstJobs
|
|
43
45
|
end
|
44
46
|
|
45
47
|
config.after_initialize do
|
46
|
-
::Switchman::Shard.default.delayed_jobs_shard.activate!(
|
48
|
+
::Switchman::Shard.default.delayed_jobs_shard.activate!(::Delayed::Backend::ActiveRecord::AbstractJob)
|
47
49
|
end
|
48
50
|
end
|
49
51
|
end
|
@@ -17,10 +17,10 @@ module SwitchmanInstJobs
|
|
17
17
|
return yield if shards.empty?
|
18
18
|
|
19
19
|
shard = shards.pop
|
20
|
-
current_shard = ::Switchman::Shard.current(
|
21
|
-
shard.activate(
|
20
|
+
current_shard = ::Switchman::Shard.current(::Delayed::Backend::ActiveRecord::AbstractJob)
|
21
|
+
shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
22
22
|
::Delayed::Job.transaction do
|
23
|
-
current_shard.activate(
|
23
|
+
current_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
24
24
|
transaction_on(shards, &block)
|
25
25
|
end
|
26
26
|
end
|
@@ -39,31 +39,49 @@ module SwitchmanInstJobs
|
|
39
39
|
|
40
40
|
# Do the updates in batches and then just clear redis instead of clearing them one at a time
|
41
41
|
target_shards.each do |target_shard, shards|
|
42
|
-
|
42
|
+
updates = { delayed_jobs_shard_id: target_shard, block_stranded: true }
|
43
|
+
updates[:updated_at] = Time.zone.now if ::Switchman::Shard.column_names.include?('updated_at')
|
44
|
+
::Switchman::Shard.where(id: shards).update_all(updates)
|
43
45
|
end
|
44
46
|
clear_shard_cache
|
45
47
|
|
46
|
-
# Wait a little over the 60 second in-process shard cache clearing
|
47
|
-
# threshold to ensure that all new stranded jobs are now being
|
48
|
-
# enqueued with next_in_strand: false
|
49
|
-
Rails.logger.debug('Waiting for caches to clear')
|
50
|
-
sleep(65) unless @skip_cache_wait
|
51
|
-
|
52
48
|
::Switchman::Shard.clear_cache
|
53
49
|
# rubocop:disable Style/CombinableLoops
|
54
50
|
# We first migrate strands so that we can stop blocking strands before we migrate unstranded jobs
|
55
51
|
source_shards.each do |s|
|
56
|
-
::Switchman::Shard.lookup(s).activate(
|
52
|
+
::Switchman::Shard.lookup(s).activate(::Delayed::Backend::ActiveRecord::AbstractJob) { migrate_strands }
|
57
53
|
end
|
58
54
|
|
59
55
|
source_shards.each do |s|
|
60
|
-
::Switchman::Shard.lookup(s).activate(
|
56
|
+
::Switchman::Shard.lookup(s).activate(::Delayed::Backend::ActiveRecord::AbstractJob) { migrate_everything }
|
61
57
|
end
|
58
|
+
ensure_unblock_stranded_for(shard_map.map(&:first))
|
62
59
|
# rubocop:enable Style/CombinableLoops
|
63
60
|
end
|
64
61
|
|
65
|
-
|
62
|
+
# if :migrate_strands ran on any shards that fell into scenario 1, then
|
63
|
+
# block_stranded never got flipped, so do that now.
|
64
|
+
def ensure_unblock_stranded_for(shards)
|
65
|
+
shards = ::Switchman::Shard.where(id: shards, block_stranded: true).to_a
|
66
|
+
return unless shards.any?
|
67
|
+
|
68
|
+
::Switchman::Shard.where(id: shards).update_all(block_stranded: false)
|
69
|
+
clear_shard_cache
|
70
|
+
|
71
|
+
# shards is an array of shard objects that is now stale cause block_stranded has been updated.
|
72
|
+
shards.map(&:delayed_jobs_shard).uniq.each do |dj_shard|
|
73
|
+
unblock_strands(dj_shard)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def clear_shard_cache(debug_message = nil)
|
66
78
|
::Switchman.cache.clear
|
79
|
+
Rails.logger.debug("Waiting for caches to clear #{debug_message}")
|
80
|
+
# Wait a little over the 60 second in-process shard cache clearing
|
81
|
+
# threshold to ensure that all new stranded jobs are now being
|
82
|
+
# enqueued with next_in_strand: false
|
83
|
+
# @skip_cache_wait is for spec usage only
|
84
|
+
sleep(65) unless @skip_cache_wait
|
67
85
|
end
|
68
86
|
|
69
87
|
# This method expects that all relevant shards already have block_stranded: true
|
@@ -85,7 +103,7 @@ module SwitchmanInstJobs
|
|
85
103
|
# 4) no running job, jobs moved: set next_in_strand=true on the first of
|
86
104
|
# those (= do nothing since it should already be true)
|
87
105
|
|
88
|
-
source_shard = ::Switchman::Shard.current(
|
106
|
+
source_shard = ::Switchman::Shard.current(::Delayed::Backend::ActiveRecord::AbstractJob)
|
89
107
|
strand_scope = ::Delayed::Job.shard(source_shard).where.not(strand: nil)
|
90
108
|
shard_map = build_shard_map(strand_scope, source_shard)
|
91
109
|
shard_map.each do |(target_shard, source_shard_ids)|
|
@@ -94,7 +112,7 @@ module SwitchmanInstJobs
|
|
94
112
|
# 1) is taken care of because it should not show up here in strands
|
95
113
|
strands = shard_scope.distinct.order(:strand).pluck(:strand)
|
96
114
|
|
97
|
-
target_shard.activate(
|
115
|
+
target_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
98
116
|
strands.each do |strand|
|
99
117
|
transaction_on([source_shard, target_shard]) do
|
100
118
|
this_strand_scope = shard_scope.where(strand: strand)
|
@@ -125,7 +143,7 @@ module SwitchmanInstJobs
|
|
125
143
|
update_all(next_in_strand: false)
|
126
144
|
end
|
127
145
|
|
128
|
-
# 4) is taken care of here, by
|
146
|
+
# 4) is taken care of here, by leaving next_in_strand alone and
|
129
147
|
# it should execute on the new shard
|
130
148
|
batch_move_jobs(
|
131
149
|
target_shard: target_shard,
|
@@ -141,42 +159,39 @@ module SwitchmanInstJobs
|
|
141
159
|
updated = ::Switchman::Shard.where(id: source_shard_ids, block_stranded: true).
|
142
160
|
update_all(block_stranded: false)
|
143
161
|
# If this is being manually re-run for some reason to clean something up, don't wait for nothing to happen
|
144
|
-
unless updated.zero?
|
145
|
-
|
146
|
-
# Wait a little over the 60 second in-process shard cache clearing
|
147
|
-
# threshold to ensure that all new stranded jobs are now being
|
148
|
-
# enqueued with next_in_strand: false
|
149
|
-
Rails.logger.debug("Waiting for caches to clear (#{source_shard.id} -> #{target_shard.id})")
|
150
|
-
# for spec usage only
|
151
|
-
sleep(65) unless @skip_cache_wait
|
152
|
-
end
|
162
|
+
clear_shard_cache("(#{source_shard.id} -> #{target_shard.id})") unless updated.zero?
|
163
|
+
|
153
164
|
::Switchman::Shard.clear_cache
|
154
165
|
# At this time, let's unblock all the strands on the target shard that aren't being held by a blocker
|
155
166
|
# but actually could have run and we just didn't know it because we didn't know if they had jobs
|
156
167
|
# on the source shard
|
157
|
-
target_shard
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
168
|
+
unblock_strands(target_shard)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def unblock_strands(target_shard)
|
174
|
+
target_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
175
|
+
loop do
|
176
|
+
# We only want to unlock stranded jobs where they don't belong to a blocked shard (if they *do* belong)
|
177
|
+
# to a blocked shard, they must be part of a concurrent jobs migration from a different source shard to
|
178
|
+
# this target shard, so we shouldn't unlock them yet. We only ever unlock one job here to keep the
|
179
|
+
# logic cleaner; if the job is n-stranded, after the first one runs, the trigger will unlock larger
|
180
|
+
# batches
|
181
|
+
break if ::Delayed::Job.where(id: ::Delayed::Job.select('DISTINCT ON (strand) id').
|
182
|
+
where.not(strand: nil).
|
183
|
+
where.not(shard_id: ::Switchman::Shard.where(block_stranded: true).pluck(:id)).where(
|
184
|
+
::Delayed::Job.select(1).from("#{::Delayed::Job.quoted_table_name} dj2").
|
185
|
+
where("dj2.next_in_strand = true OR dj2.source = 'JobsMigrator::StrandBlocker'").
|
186
|
+
where('dj2.strand = delayed_jobs.strand').arel.exists.not
|
187
|
+
).order(:strand, :strand_order_override, :id)).limit(500).update_all(next_in_strand: true).zero?
|
173
188
|
end
|
174
189
|
end
|
175
190
|
end
|
176
191
|
|
177
192
|
def migrate_everything
|
178
|
-
source_shard = ::Switchman::Shard.current(
|
179
|
-
scope = ::Delayed::Job.shard(source_shard).where(
|
193
|
+
source_shard = ::Switchman::Shard.current(::Delayed::Backend::ActiveRecord::AbstractJob)
|
194
|
+
scope = ::Delayed::Job.shard(source_shard).where(strand: nil)
|
180
195
|
|
181
196
|
shard_map = build_shard_map(scope, source_shard)
|
182
197
|
shard_map.each do |(target_shard, source_shard_ids)|
|
@@ -209,7 +224,7 @@ module SwitchmanInstJobs
|
|
209
224
|
# Adapted from get_and_lock_next_available in delayed/backend/active_record.rb
|
210
225
|
target_jobs = scope.limit(1000).lock('FOR UPDATE SKIP LOCKED')
|
211
226
|
|
212
|
-
query = source_shard.activate(
|
227
|
+
query = source_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
213
228
|
"WITH limited_jobs AS (#{target_jobs.to_sql}) " \
|
214
229
|
"UPDATE #{::Delayed::Job.quoted_table_name} " \
|
215
230
|
"SET locked_by = #{::Delayed::Job.connection.quote(::Delayed::Backend::Base::ON_HOLD_LOCKED_BY)}, " \
|
@@ -218,7 +233,9 @@ module SwitchmanInstJobs
|
|
218
233
|
"RETURNING #{::Delayed::Job.quoted_table_name}.*"
|
219
234
|
end
|
220
235
|
|
221
|
-
jobs = source_shard.activate(
|
236
|
+
jobs = source_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
237
|
+
::Delayed::Job.find_by_sql(query)
|
238
|
+
end
|
222
239
|
new_jobs = jobs.map do |job|
|
223
240
|
new_job = job.dup
|
224
241
|
new_job.shard = target_shard
|
@@ -236,10 +253,10 @@ module SwitchmanInstJobs
|
|
236
253
|
new_job
|
237
254
|
end
|
238
255
|
transaction_on([source_shard, target_shard]) do
|
239
|
-
target_shard.activate(
|
256
|
+
target_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
240
257
|
bulk_insert_jobs(new_jobs)
|
241
258
|
end
|
242
|
-
source_shard.activate(
|
259
|
+
source_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
243
260
|
::Delayed::Job.delete(jobs)
|
244
261
|
end
|
245
262
|
end
|
@@ -14,7 +14,7 @@ module SwitchmanInstJobs
|
|
14
14
|
shard = ::Switchman::Shard.lookup(delayed_jobs_shard_id)
|
15
15
|
return shard if shard
|
16
16
|
end
|
17
|
-
database_server&.delayed_jobs_shard(self)
|
17
|
+
@delayed_jobs_shard ||= database_server&.delayed_jobs_shard(self)
|
18
18
|
end
|
19
19
|
|
20
20
|
# Adapted from hold/unhold methods in base delayed jobs base
|
@@ -22,12 +22,12 @@ module SwitchmanInstJobs
|
|
22
22
|
def hold_jobs!(wait: false)
|
23
23
|
self.jobs_held = true
|
24
24
|
save! if changed?
|
25
|
-
delayed_jobs_shard.activate(
|
25
|
+
delayed_jobs_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
26
26
|
lock_jobs_for_hold
|
27
27
|
end
|
28
28
|
return unless wait
|
29
29
|
|
30
|
-
delayed_jobs_shard.activate(
|
30
|
+
delayed_jobs_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
31
31
|
while ::Delayed::Job.where(shard_id: id).
|
32
32
|
where.not(locked_at: nil).
|
33
33
|
where.not(locked_by: ::Delayed::Backend::Base::ON_HOLD_LOCKED_BY).exists?
|
@@ -39,8 +39,15 @@ module SwitchmanInstJobs
|
|
39
39
|
|
40
40
|
def unhold_jobs!
|
41
41
|
self.jobs_held = false
|
42
|
-
|
43
|
-
|
42
|
+
if changed?
|
43
|
+
save!
|
44
|
+
# Wait a little over the 60 second in-process shard cache clearing
|
45
|
+
# threshold to ensure that all new jobs are now being enqueued
|
46
|
+
# unlocked
|
47
|
+
Rails.logger.debug('Waiting for caches to clear')
|
48
|
+
sleep(65)
|
49
|
+
end
|
50
|
+
delayed_jobs_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
44
51
|
::Delayed::Job.where(locked_by: ::Delayed::Backend::Base::ON_HOLD_LOCKED_BY, shard_id: id).
|
45
52
|
in_batches(of: 10_000).
|
46
53
|
update_all(
|
@@ -68,22 +75,14 @@ module SwitchmanInstJobs
|
|
68
75
|
remove_instance_variable(:@delayed_jobs_shards) if instance_variable_defined?(:@delayed_jobs_shards)
|
69
76
|
end
|
70
77
|
|
71
|
-
def current(category = :primary)
|
72
|
-
if category == :delayed_jobs
|
73
|
-
active_shards[category] || super(:primary).delayed_jobs_shard
|
74
|
-
else
|
75
|
-
super
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
78
|
def activate!(categories)
|
80
79
|
if !@skip_delayed_job_auto_activation &&
|
81
|
-
!categories[
|
82
|
-
categories[
|
83
|
-
categories[
|
80
|
+
!categories[::Delayed::Backend::ActiveRecord::AbstractJob] &&
|
81
|
+
categories[::ActiveRecord::Base] &&
|
82
|
+
categories[::ActiveRecord::Base] != ::Switchman::Shard.current(::ActiveRecord::Base)
|
84
83
|
skip_delayed_job_auto_activation do
|
85
|
-
categories[
|
86
|
-
categories[
|
84
|
+
categories[::Delayed::Backend::ActiveRecord::AbstractJob] =
|
85
|
+
categories[::ActiveRecord::Base].delayed_jobs_shard
|
87
86
|
end
|
88
87
|
end
|
89
88
|
super
|
@@ -97,11 +96,6 @@ module SwitchmanInstJobs
|
|
97
96
|
@skip_delayed_job_auto_activation = was
|
98
97
|
end
|
99
98
|
|
100
|
-
def create
|
101
|
-
db = ::Switchman::DatabaseServer.server_for_new_shard
|
102
|
-
db.create_new_shard
|
103
|
-
end
|
104
|
-
|
105
99
|
def periodic_clear_shard_cache
|
106
100
|
# TODO: make this configurable
|
107
101
|
@timed_cache ||= TimedCache.new(-> { 60.to_i.seconds.ago }) do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: switchman-inst-jobs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bryan Petty
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: inst-jobs
|
@@ -16,20 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.0'
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
22
|
+
version: 2.3.1
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
29
|
+
version: '2.0'
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
32
|
+
version: 2.3.1
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: parallel
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -50,34 +50,40 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: '
|
53
|
+
version: '6.1'
|
54
54
|
- - "<"
|
55
55
|
- !ruby/object:Gem::Version
|
56
|
-
version: '6.
|
56
|
+
version: '6.2'
|
57
57
|
type: :runtime
|
58
58
|
prerelease: false
|
59
59
|
version_requirements: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
61
|
- - ">="
|
62
62
|
- !ruby/object:Gem::Version
|
63
|
-
version: '
|
63
|
+
version: '6.1'
|
64
64
|
- - "<"
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
version: '6.
|
66
|
+
version: '6.2'
|
67
67
|
- !ruby/object:Gem::Dependency
|
68
68
|
name: switchman
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|
70
70
|
requirements:
|
71
71
|
- - "~>"
|
72
72
|
- !ruby/object:Gem::Version
|
73
|
-
version: '
|
73
|
+
version: '3.0'
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 3.0.1
|
74
77
|
type: :runtime
|
75
78
|
prerelease: false
|
76
79
|
version_requirements: !ruby/object:Gem::Requirement
|
77
80
|
requirements:
|
78
81
|
- - "~>"
|
79
82
|
- !ruby/object:Gem::Version
|
80
|
-
version: '
|
83
|
+
version: '3.0'
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: 3.0.1
|
81
87
|
- !ruby/object:Gem::Dependency
|
82
88
|
name: bundler
|
83
89
|
requirement: !ruby/object:Gem::Requirement
|
@@ -107,19 +113,19 @@ dependencies:
|
|
107
113
|
- !ruby/object:Gem::Version
|
108
114
|
version: '0'
|
109
115
|
- !ruby/object:Gem::Dependency
|
110
|
-
name:
|
116
|
+
name: diplomat
|
111
117
|
requirement: !ruby/object:Gem::Requirement
|
112
118
|
requirements:
|
113
|
-
- - "
|
119
|
+
- - "~>"
|
114
120
|
- !ruby/object:Gem::Version
|
115
|
-
version:
|
121
|
+
version: 2.5.1
|
116
122
|
type: :development
|
117
123
|
prerelease: false
|
118
124
|
version_requirements: !ruby/object:Gem::Requirement
|
119
125
|
requirements:
|
120
|
-
- - "
|
126
|
+
- - "~>"
|
121
127
|
- !ruby/object:Gem::Version
|
122
|
-
version:
|
128
|
+
version: 2.5.1
|
123
129
|
- !ruby/object:Gem::Dependency
|
124
130
|
name: newrelic_rpm
|
125
131
|
requirement: !ruby/object:Gem::Requirement
|
@@ -182,70 +188,70 @@ dependencies:
|
|
182
188
|
requirements:
|
183
189
|
- - "~>"
|
184
190
|
- !ruby/object:Gem::Version
|
185
|
-
version: '3.
|
191
|
+
version: '3.10'
|
186
192
|
type: :development
|
187
193
|
prerelease: false
|
188
194
|
version_requirements: !ruby/object:Gem::Requirement
|
189
195
|
requirements:
|
190
196
|
- - "~>"
|
191
197
|
- !ruby/object:Gem::Version
|
192
|
-
version: '3.
|
198
|
+
version: '3.10'
|
193
199
|
- !ruby/object:Gem::Dependency
|
194
200
|
name: rspec-rails
|
195
201
|
requirement: !ruby/object:Gem::Requirement
|
196
202
|
requirements:
|
197
203
|
- - "~>"
|
198
204
|
- !ruby/object:Gem::Version
|
199
|
-
version: '
|
205
|
+
version: '5.0'
|
200
206
|
type: :development
|
201
207
|
prerelease: false
|
202
208
|
version_requirements: !ruby/object:Gem::Requirement
|
203
209
|
requirements:
|
204
210
|
- - "~>"
|
205
211
|
- !ruby/object:Gem::Version
|
206
|
-
version: '
|
212
|
+
version: '5.0'
|
207
213
|
- !ruby/object:Gem::Dependency
|
208
214
|
name: rubocop
|
209
215
|
requirement: !ruby/object:Gem::Requirement
|
210
216
|
requirements:
|
211
217
|
- - "~>"
|
212
218
|
- !ruby/object:Gem::Version
|
213
|
-
version: 1.
|
219
|
+
version: '1.15'
|
214
220
|
type: :development
|
215
221
|
prerelease: false
|
216
222
|
version_requirements: !ruby/object:Gem::Requirement
|
217
223
|
requirements:
|
218
224
|
- - "~>"
|
219
225
|
- !ruby/object:Gem::Version
|
220
|
-
version: 1.
|
226
|
+
version: '1.15'
|
221
227
|
- !ruby/object:Gem::Dependency
|
222
228
|
name: rubocop-rails
|
223
229
|
requirement: !ruby/object:Gem::Requirement
|
224
230
|
requirements:
|
225
231
|
- - "~>"
|
226
232
|
- !ruby/object:Gem::Version
|
227
|
-
version: 2.
|
233
|
+
version: '2.10'
|
228
234
|
type: :development
|
229
235
|
prerelease: false
|
230
236
|
version_requirements: !ruby/object:Gem::Requirement
|
231
237
|
requirements:
|
232
238
|
- - "~>"
|
233
239
|
- !ruby/object:Gem::Version
|
234
|
-
version: 2.
|
240
|
+
version: '2.10'
|
235
241
|
- !ruby/object:Gem::Dependency
|
236
242
|
name: simplecov
|
237
243
|
requirement: !ruby/object:Gem::Requirement
|
238
244
|
requirements:
|
239
245
|
- - "~>"
|
240
246
|
- !ruby/object:Gem::Version
|
241
|
-
version: '0.
|
247
|
+
version: '0.21'
|
242
248
|
type: :development
|
243
249
|
prerelease: false
|
244
250
|
version_requirements: !ruby/object:Gem::Requirement
|
245
251
|
requirements:
|
246
252
|
- - "~>"
|
247
253
|
- !ruby/object:Gem::Version
|
248
|
-
version: '0.
|
254
|
+
version: '0.21'
|
249
255
|
- !ruby/object:Gem::Dependency
|
250
256
|
name: wwtd
|
251
257
|
requirement: !ruby/object:Gem::Requirement
|
@@ -289,6 +295,7 @@ files:
|
|
289
295
|
- db/migrate/20151210162949_improve_max_concurrent.rb
|
290
296
|
- db/migrate/20161206323555_add_back_default_string_limits_jobs.rb
|
291
297
|
- db/migrate/20170308045400_add_shard_id_to_delayed_jobs.rb
|
298
|
+
- db/migrate/20170308045401_add_delayed_jobs_shard_id_to_switchman_shards.rb
|
292
299
|
- db/migrate/20181217155351_speed_up_max_concurrent_triggers.rb
|
293
300
|
- db/migrate/20190726154743_make_critical_columns_not_null.rb
|
294
301
|
- db/migrate/20200330230722_add_id_to_get_delayed_jobs_index.rb
|
@@ -298,11 +305,13 @@ files:
|
|
298
305
|
- db/migrate/20200825011002_add_strand_order_override.rb
|
299
306
|
- lib/switchman-inst-jobs.rb
|
300
307
|
- lib/switchman_inst_jobs.rb
|
308
|
+
- lib/switchman_inst_jobs/active_record/connection_adapters/connection_pool.rb
|
301
309
|
- lib/switchman_inst_jobs/active_record/connection_adapters/postgresql_adapter.rb
|
302
310
|
- lib/switchman_inst_jobs/active_record/migration.rb
|
303
311
|
- lib/switchman_inst_jobs/delayed/backend/base.rb
|
304
312
|
- lib/switchman_inst_jobs/delayed/message_sending.rb
|
305
313
|
- lib/switchman_inst_jobs/delayed/pool.rb
|
314
|
+
- lib/switchman_inst_jobs/delayed/settings.rb
|
306
315
|
- lib/switchman_inst_jobs/delayed/worker.rb
|
307
316
|
- lib/switchman_inst_jobs/delayed/worker/health_check.rb
|
308
317
|
- lib/switchman_inst_jobs/engine.rb
|
@@ -327,14 +336,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
327
336
|
requirements:
|
328
337
|
- - ">="
|
329
338
|
- !ruby/object:Gem::Version
|
330
|
-
version: '2.
|
339
|
+
version: '2.6'
|
331
340
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
332
341
|
requirements:
|
333
342
|
- - ">="
|
334
343
|
- !ruby/object:Gem::Version
|
335
344
|
version: '0'
|
336
345
|
requirements: []
|
337
|
-
rubygems_version: 3.
|
346
|
+
rubygems_version: 3.2.15
|
338
347
|
signing_key:
|
339
348
|
specification_version: 4
|
340
349
|
summary: Switchman and Instructure Jobs compatibility gem.
|