switchman-inst-jobs 4.0.12 → 4.0.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/db/migrate/20101216224513_create_delayed_jobs.rb +5 -3
- data/db/migrate/20110208031356_add_delayed_jobs_tag.rb +2 -0
- data/db/migrate/20110426161613_add_delayed_jobs_max_attempts.rb +2 -0
- data/db/migrate/20110516225834_add_delayed_jobs_strand.rb +2 -0
- data/db/migrate/20110531144916_cleanup_delayed_jobs_indexes.rb +9 -7
- data/db/migrate/20110610213249_optimize_delayed_jobs.rb +20 -18
- data/db/migrate/20110831210257_add_delayed_jobs_next_in_strand.rb +28 -26
- data/db/migrate/20120510004759_delayed_jobs_delete_trigger_lock_for_update.rb +20 -18
- data/db/migrate/20120531150712_drop_psql_jobs_pop_fn.rb +4 -2
- data/db/migrate/20120607164022_delayed_jobs_use_advisory_locks.rb +61 -59
- data/db/migrate/20120607181141_index_jobs_on_locked_by.rb +3 -1
- data/db/migrate/20120608191051_add_jobs_run_at_index.rb +3 -1
- data/db/migrate/20120927184213_change_delayed_jobs_handler_to_text.rb +2 -0
- data/db/migrate/20140505215131_add_failed_jobs_original_job_id.rb +2 -0
- data/db/migrate/20140505215510_copy_failed_jobs_original_id.rb +2 -0
- data/db/migrate/20140505223637_drop_failed_jobs_original_id.rb +2 -0
- data/db/migrate/20140512213941_add_source_to_jobs.rb +2 -0
- data/db/migrate/20150807133223_add_max_concurrent_to_jobs.rb +51 -49
- data/db/migrate/20151123210429_add_expires_at_to_jobs.rb +2 -0
- data/db/migrate/20151210162949_improve_max_concurrent.rb +37 -35
- data/db/migrate/20161206323555_add_back_default_string_limits_jobs.rb +4 -2
- data/db/migrate/20170308045400_add_shard_id_to_delayed_jobs.rb +2 -0
- data/db/migrate/20170308045401_add_delayed_jobs_shard_id_to_switchman_shards.rb +2 -0
- data/db/migrate/20181217155351_speed_up_max_concurrent_triggers.rb +73 -71
- data/db/migrate/20190726154743_make_critical_columns_not_null.rb +2 -0
- data/db/migrate/20200330230722_add_id_to_get_delayed_jobs_index.rb +14 -10
- data/db/migrate/20200818130101_add_on_hold_to_switchman_shards.rb +2 -0
- data/db/migrate/20200822014259_add_block_stranded_to_switchman_shards.rb +2 -0
- data/db/migrate/20200824222232_speed_up_max_concurrent_delete_trigger.rb +6 -4
- data/db/migrate/20200825011002_add_strand_order_override.rb +10 -7
- data/db/migrate/20210809145804_add_n_strand_index.rb +4 -3
- data/db/migrate/20210812210128_add_singleton_column.rb +12 -12
- data/db/migrate/20210917232626_add_delete_conflicting_singletons_before_unlock_trigger.rb +3 -3
- data/db/migrate/20210928174754_fix_singleton_condition_in_before_insert.rb +2 -2
- data/db/migrate/20210929204903_update_conflicting_singleton_function_to_use_index.rb +2 -2
- data/db/migrate/20211101190934_update_after_delete_trigger_for_singleton_index.rb +2 -2
- data/db/migrate/20211207094200_update_after_delete_trigger_for_singleton_transition_cases.rb +2 -2
- data/db/migrate/20211220112800_fix_singleton_race_condition_insert.rb +2 -2
- data/db/migrate/20211220113000_fix_singleton_race_condition_delete.rb +2 -2
- data/db/migrate/20220127091200_fix_singleton_unique_constraint.rb +6 -6
- data/db/migrate/20220128084800_update_insert_trigger_for_singleton_unique_constraint_change.rb +2 -2
- data/db/migrate/20220128084900_update_delete_trigger_for_singleton_unique_constraint_change.rb +2 -2
- data/db/migrate/20220203063200_remove_old_singleton_index.rb +8 -8
- data/db/migrate/20220328152900_add_failed_jobs_indicies.rb +2 -2
- data/lib/switchman-inst-jobs.rb +3 -1
- data/lib/switchman_inst_jobs/active_record/connection_adapters/postgresql_adapter.rb +3 -1
- data/lib/switchman_inst_jobs/active_record/migration.rb +5 -3
- data/lib/switchman_inst_jobs/delayed/backend/active_record/abstract_job.rb +2 -0
- data/lib/switchman_inst_jobs/delayed/backend/base.rb +10 -2
- data/lib/switchman_inst_jobs/delayed/message_sending.rb +2 -0
- data/lib/switchman_inst_jobs/delayed/pool.rb +2 -0
- data/lib/switchman_inst_jobs/delayed/settings.rb +3 -1
- data/lib/switchman_inst_jobs/delayed/worker/health_check.rb +5 -3
- data/lib/switchman_inst_jobs/delayed/worker.rb +2 -0
- data/lib/switchman_inst_jobs/engine.rb +7 -5
- data/lib/switchman_inst_jobs/guard_rail.rb +2 -0
- data/lib/switchman_inst_jobs/jobs_migrator.rb +62 -55
- data/lib/switchman_inst_jobs/new_relic.rb +2 -0
- data/lib/switchman_inst_jobs/switchman/database_server.rb +3 -1
- data/lib/switchman_inst_jobs/switchman/default_shard.rb +2 -0
- data/lib/switchman_inst_jobs/switchman/shard.rb +27 -23
- data/lib/switchman_inst_jobs/timed_cache.rb +2 -0
- data/lib/switchman_inst_jobs/version.rb +3 -1
- data/lib/switchman_inst_jobs/yaml_extensions.rb +3 -1
- data/lib/switchman_inst_jobs.rb +22 -20
- metadata +5 -5
@@ -3,7 +3,7 @@
|
|
3
3
|
class FixSingletonRaceConditionDelete < ActiveRecord::Migration[5.2]
|
4
4
|
def up
|
5
5
|
execute(<<~SQL)
|
6
|
-
CREATE OR REPLACE FUNCTION #{connection.quote_table_name(
|
6
|
+
CREATE OR REPLACE FUNCTION #{connection.quote_table_name("delayed_jobs_after_delete_row_tr_fn")} () RETURNS trigger AS $$
|
7
7
|
DECLARE
|
8
8
|
next_strand varchar;
|
9
9
|
running_count integer;
|
@@ -106,7 +106,7 @@ class FixSingletonRaceConditionDelete < ActiveRecord::Migration[5.2]
|
|
106
106
|
|
107
107
|
def down
|
108
108
|
execute(<<~SQL)
|
109
|
-
CREATE OR REPLACE FUNCTION #{connection.quote_table_name(
|
109
|
+
CREATE OR REPLACE FUNCTION #{connection.quote_table_name("delayed_jobs_after_delete_row_tr_fn")} () RETURNS trigger AS $$
|
110
110
|
DECLARE
|
111
111
|
next_strand varchar;
|
112
112
|
running_count integer;
|
@@ -4,15 +4,15 @@ class FixSingletonUniqueConstraint < ActiveRecord::Migration[5.2]
|
|
4
4
|
disable_ddl_transaction!
|
5
5
|
|
6
6
|
def up
|
7
|
-
rename_index :delayed_jobs,
|
8
|
-
rename_index :delayed_jobs,
|
7
|
+
rename_index :delayed_jobs, "index_delayed_jobs_on_singleton_not_running", "index_delayed_jobs_on_singleton_not_running_old"
|
8
|
+
rename_index :delayed_jobs, "index_delayed_jobs_on_singleton_running", "index_delayed_jobs_on_singleton_running_old"
|
9
9
|
|
10
10
|
# only one job can be queued in a singleton
|
11
11
|
add_index :delayed_jobs,
|
12
12
|
:singleton,
|
13
13
|
where: "singleton IS NOT NULL AND (locked_by IS NULL OR locked_by = '#{::Delayed::Backend::Base::ON_HOLD_LOCKED_BY}')",
|
14
14
|
unique: true,
|
15
|
-
name:
|
15
|
+
name: "index_delayed_jobs_on_singleton_not_running",
|
16
16
|
algorithm: :concurrently
|
17
17
|
|
18
18
|
# only one job can be running for a singleton
|
@@ -20,12 +20,12 @@ class FixSingletonUniqueConstraint < ActiveRecord::Migration[5.2]
|
|
20
20
|
:singleton,
|
21
21
|
where: "singleton IS NOT NULL AND locked_by IS NOT NULL AND locked_by <> '#{::Delayed::Backend::Base::ON_HOLD_LOCKED_BY}'",
|
22
22
|
unique: true,
|
23
|
-
name:
|
23
|
+
name: "index_delayed_jobs_on_singleton_running",
|
24
24
|
algorithm: :concurrently
|
25
25
|
end
|
26
26
|
|
27
27
|
def down
|
28
|
-
remove_index :delayed_jobs, name:
|
29
|
-
remove_index :delayed_jobs, name:
|
28
|
+
remove_index :delayed_jobs, name: "index_delayed_jobs_on_singleton_not_running_old"
|
29
|
+
remove_index :delayed_jobs, name: "index_delayed_jobs_on_singleton_running_old"
|
30
30
|
end
|
31
31
|
end
|
data/db/migrate/20220128084800_update_insert_trigger_for_singleton_unique_constraint_change.rb
CHANGED
@@ -5,7 +5,7 @@ class UpdateInsertTriggerForSingletonUniqueConstraintChange < ActiveRecord::Migr
|
|
5
5
|
reversible do |direction|
|
6
6
|
direction.up do
|
7
7
|
execute(<<~SQL)
|
8
|
-
CREATE OR REPLACE FUNCTION #{connection.quote_table_name(
|
8
|
+
CREATE OR REPLACE FUNCTION #{connection.quote_table_name("delayed_jobs_before_insert_row_tr_fn")} () RETURNS trigger AS $$
|
9
9
|
BEGIN
|
10
10
|
IF NEW.strand IS NOT NULL THEN
|
11
11
|
PERFORM pg_advisory_xact_lock(half_md5_as_bigint(NEW.strand));
|
@@ -31,7 +31,7 @@ class UpdateInsertTriggerForSingletonUniqueConstraintChange < ActiveRecord::Migr
|
|
31
31
|
end
|
32
32
|
direction.down do
|
33
33
|
execute(<<~SQL)
|
34
|
-
CREATE OR REPLACE FUNCTION #{connection.quote_table_name(
|
34
|
+
CREATE OR REPLACE FUNCTION #{connection.quote_table_name("delayed_jobs_before_insert_row_tr_fn")} () RETURNS trigger AS $$
|
35
35
|
BEGIN
|
36
36
|
IF NEW.strand IS NOT NULL THEN
|
37
37
|
PERFORM pg_advisory_xact_lock(half_md5_as_bigint(NEW.strand));
|
data/db/migrate/20220128084900_update_delete_trigger_for_singleton_unique_constraint_change.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
class UpdateDeleteTriggerForSingletonUniqueConstraintChange < ActiveRecord::Migration[5.2]
|
4
4
|
def up
|
5
5
|
execute(<<~SQL)
|
6
|
-
CREATE OR REPLACE FUNCTION #{connection.quote_table_name(
|
6
|
+
CREATE OR REPLACE FUNCTION #{connection.quote_table_name("delayed_jobs_after_delete_row_tr_fn")} () RETURNS trigger AS $$
|
7
7
|
DECLARE
|
8
8
|
next_strand varchar;
|
9
9
|
running_count integer;
|
@@ -106,7 +106,7 @@ class UpdateDeleteTriggerForSingletonUniqueConstraintChange < ActiveRecord::Migr
|
|
106
106
|
|
107
107
|
def down
|
108
108
|
execute(<<~SQL)
|
109
|
-
CREATE OR REPLACE FUNCTION #{connection.quote_table_name(
|
109
|
+
CREATE OR REPLACE FUNCTION #{connection.quote_table_name("delayed_jobs_after_delete_row_tr_fn")} () RETURNS trigger AS $$
|
110
110
|
DECLARE
|
111
111
|
next_strand varchar;
|
112
112
|
running_count integer;
|
@@ -4,28 +4,28 @@ class RemoveOldSingletonIndex < ActiveRecord::Migration[5.2]
|
|
4
4
|
disable_ddl_transaction!
|
5
5
|
|
6
6
|
def up
|
7
|
-
remove_index :delayed_jobs, name:
|
8
|
-
remove_index :delayed_jobs, name:
|
7
|
+
remove_index :delayed_jobs, name: "index_delayed_jobs_on_singleton_not_running_old"
|
8
|
+
remove_index :delayed_jobs, name: "index_delayed_jobs_on_singleton_running_old"
|
9
9
|
end
|
10
10
|
|
11
11
|
def down
|
12
|
-
rename_index :delayed_jobs,
|
13
|
-
rename_index :delayed_jobs,
|
12
|
+
rename_index :delayed_jobs, "index_delayed_jobs_on_singleton_not_running", "index_delayed_jobs_on_singleton_not_running_old"
|
13
|
+
rename_index :delayed_jobs, "index_delayed_jobs_on_singleton_running", "index_delayed_jobs_on_singleton_running_old"
|
14
14
|
|
15
15
|
# only one job can be queued in a singleton
|
16
16
|
add_index :delayed_jobs,
|
17
17
|
:singleton,
|
18
|
-
where:
|
18
|
+
where: "singleton IS NOT NULL AND locked_by IS NULL",
|
19
19
|
unique: true,
|
20
|
-
name:
|
20
|
+
name: "index_delayed_jobs_on_singleton_not_running",
|
21
21
|
algorithm: :concurrently
|
22
22
|
|
23
23
|
# only one job can be running for a singleton
|
24
24
|
add_index :delayed_jobs,
|
25
25
|
:singleton,
|
26
|
-
where:
|
26
|
+
where: "singleton IS NOT NULL AND locked_by IS NOT NULL",
|
27
27
|
unique: true,
|
28
|
-
name:
|
28
|
+
name: "index_delayed_jobs_on_singleton_running",
|
29
29
|
algorithm: :concurrently
|
30
30
|
end
|
31
31
|
end
|
@@ -5,8 +5,8 @@ class AddFailedJobsIndicies < ActiveRecord::Migration[5.2]
|
|
5
5
|
|
6
6
|
def change
|
7
7
|
add_index :failed_jobs, :failed_at, algorithm: :concurrently
|
8
|
-
add_index :failed_jobs, :strand, where:
|
9
|
-
add_index :failed_jobs, :singleton, where:
|
8
|
+
add_index :failed_jobs, :strand, where: "strand IS NOT NULL", algorithm: :concurrently
|
9
|
+
add_index :failed_jobs, :singleton, where: "singleton IS NOT NULL", algorithm: :concurrently
|
10
10
|
add_index :failed_jobs, :tag, algorithm: :concurrently
|
11
11
|
end
|
12
12
|
end
|
data/lib/switchman-inst-jobs.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SwitchmanInstJobs
|
2
4
|
module ActiveRecord
|
3
5
|
module ConnectionAdapters
|
4
6
|
module PostgreSQLAdapter
|
5
|
-
def set_search_path(function, args =
|
7
|
+
def set_search_path(function, args = "()", path = ::Switchman::Shard.current.name)
|
6
8
|
execute <<-SQL
|
7
9
|
ALTER FUNCTION #{quote_table_name(function)}#{args}
|
8
10
|
SET search_path TO #{path}
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SwitchmanInstJobs
|
2
4
|
module ActiveRecord
|
3
5
|
module Migration
|
@@ -19,10 +21,10 @@ module SwitchmanInstJobs
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def copy(destination, sources, options = {})
|
22
|
-
if sources.delete(
|
24
|
+
if sources.delete("delayed_engine")
|
23
25
|
# rubocop:disable Rails/Output
|
24
|
-
puts
|
25
|
-
puts
|
26
|
+
puts "NOTE: Not installing delayed_engine migrations in an application using switchman-inst-jobs"
|
27
|
+
puts "(use rake switchman_inst_jobs:install:migrations instead)"
|
26
28
|
# rubocop:enable Rails/Output
|
27
29
|
end
|
28
30
|
super
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SwitchmanInstJobs
|
2
4
|
class ShardNotFoundError < RuntimeError
|
3
5
|
attr_reader :shard_id
|
@@ -65,7 +67,7 @@ module SwitchmanInstJobs
|
|
65
67
|
|
66
68
|
def self.prepended(base)
|
67
69
|
base.singleton_class.prepend(ClassMethods)
|
68
|
-
return unless base.name ==
|
70
|
+
return unless base.name == "Delayed::Backend::ActiveRecord::Job"
|
69
71
|
|
70
72
|
::Delayed::Backend::ActiveRecord::AbstractJob.sharded_model
|
71
73
|
end
|
@@ -79,7 +81,7 @@ module SwitchmanInstJobs
|
|
79
81
|
self.shard_id = shard.id
|
80
82
|
self.shard_id = nil if shard.is_a?(::Switchman::DefaultShard)
|
81
83
|
# If jobs are held for a shard, enqueue new ones as held as well
|
82
|
-
return unless ::Switchman::Shard.columns_hash.key?(
|
84
|
+
return unless ::Switchman::Shard.columns_hash.key?("jobs_held") && shard.jobs_held
|
83
85
|
|
84
86
|
self.locked_by = ::Delayed::Backend::Base::ON_HOLD_LOCKED_BY
|
85
87
|
self.locked_at = ::Delayed::Job.db_time_now
|
@@ -92,6 +94,12 @@ module SwitchmanInstJobs
|
|
92
94
|
current_shard.activate { super }
|
93
95
|
end
|
94
96
|
|
97
|
+
def invoke_payload_object_cb(...)
|
98
|
+
raise ShardNotFoundError, shard_id unless current_shard
|
99
|
+
|
100
|
+
current_shard.activate { super }
|
101
|
+
end
|
102
|
+
|
95
103
|
def deserialize(source)
|
96
104
|
raise ShardNotFoundError, shard_id unless current_shard
|
97
105
|
|
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SwitchmanInstJobs
|
2
4
|
module Delayed
|
3
5
|
module Settings
|
4
6
|
def self.configured_shard_ids
|
5
|
-
(::Delayed::Settings.worker_config.try(:[],
|
7
|
+
(::Delayed::Settings.worker_config.try(:[], "workers") || []).pluck("shard").compact.uniq
|
6
8
|
end
|
7
9
|
end
|
8
10
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SwitchmanInstJobs
|
2
4
|
module Delayed
|
3
5
|
module Worker
|
@@ -9,13 +11,13 @@ module SwitchmanInstJobs
|
|
9
11
|
module ClassMethods
|
10
12
|
def munge_service_name(shard)
|
11
13
|
# munge the name to add the current shard
|
12
|
-
original_service_name = ::Delayed::Settings.worker_health_check_config[
|
14
|
+
original_service_name = ::Delayed::Settings.worker_health_check_config["service_name"]
|
13
15
|
consul_service_name = ::Delayed::Worker::ConsulHealthCheck::DEFAULT_SERVICE_NAME
|
14
|
-
::Delayed::Settings.worker_health_check_config[
|
16
|
+
::Delayed::Settings.worker_health_check_config["service_name"] =
|
15
17
|
"#{original_service_name || consul_service_name}/#{shard.id}"
|
16
18
|
yield
|
17
19
|
ensure
|
18
|
-
::Delayed::Settings.worker_health_check_config[
|
20
|
+
::Delayed::Settings.worker_health_check_config["service_name"] = original_service_name
|
19
21
|
end
|
20
22
|
|
21
23
|
def reschedule_abandoned_jobs
|
@@ -1,12 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SwitchmanInstJobs
|
2
4
|
class Engine < ::Rails::Engine
|
3
5
|
isolate_namespace SwitchmanInstJobs
|
4
6
|
|
5
|
-
initializer
|
7
|
+
initializer "sharding.active_record", after: "switchman.extend_connection_adapters" do
|
6
8
|
SwitchmanInstJobs.initialize_active_record
|
7
9
|
end
|
8
10
|
|
9
|
-
initializer
|
11
|
+
initializer "sharding.delayed" do
|
10
12
|
SwitchmanInstJobs.initialize_inst_jobs
|
11
13
|
|
12
14
|
::Delayed::Worker.lifecycle.around(:work_queue_pop) do |worker, config, &block|
|
@@ -29,7 +31,7 @@ module SwitchmanInstJobs
|
|
29
31
|
current_job_shard = ::Switchman::Shard.lookup(job.shard_id).delayed_jobs_shard
|
30
32
|
if current_job_shard != ::Switchman::Shard.current(::Delayed::Backend::ActiveRecord::AbstractJob)
|
31
33
|
current_job_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
32
|
-
::Delayed::Job.where(source:
|
34
|
+
::Delayed::Job.where(source: "JobsMigrator::StrandBlocker", **{ column => job.try(column) }).delete_all
|
33
35
|
|
34
36
|
j = ::Delayed::Job.where(**{ column => job.try(column) }).next_in_strand_order.first
|
35
37
|
j.update_column(:next_in_strand, true) if j && !j.next_in_strand
|
@@ -40,11 +42,11 @@ module SwitchmanInstJobs
|
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
43
|
-
initializer
|
45
|
+
initializer "sharding.guard_rail", after: "switchman.extend_guard_rail" do
|
44
46
|
SwitchmanInstJobs.initialize_guard_rail
|
45
47
|
end
|
46
48
|
|
47
|
-
initializer
|
49
|
+
initializer "sharding.switchman" do
|
48
50
|
SwitchmanInstJobs.initialize_switchman
|
49
51
|
end
|
50
52
|
|
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
require "parallel"
|
3
5
|
|
4
6
|
module SwitchmanInstJobs
|
5
7
|
class JobsMigrator
|
@@ -50,7 +52,7 @@ module SwitchmanInstJobs
|
|
50
52
|
# Do the updates in batches and then just clear redis instead of clearing them one at a time
|
51
53
|
target_shards.each do |target_shard, shards|
|
52
54
|
updates = { delayed_jobs_shard_id: target_shard, block_stranded: true }
|
53
|
-
updates[:updated_at] = Time.zone.now if ::Switchman::Shard.column_names.include?(
|
55
|
+
updates[:updated_at] = Time.zone.now if ::Switchman::Shard.column_names.include?("updated_at")
|
54
56
|
::Switchman::Shard.where(id: shards).update_all(updates)
|
55
57
|
end
|
56
58
|
clear_shard_cache(default: ::Switchman::Shard.exists?(id: target_shards.values.flatten, default: true))
|
@@ -85,8 +87,8 @@ module SwitchmanInstJobs
|
|
85
87
|
end
|
86
88
|
|
87
89
|
def clear_shard_cache(debug_message = nil, default:)
|
88
|
-
::Switchman.cache.delete_matched(
|
89
|
-
::Switchman.cache.delete(
|
90
|
+
::Switchman.cache.delete_matched("shard/*")
|
91
|
+
::Switchman.cache.delete("default_shard") if default
|
90
92
|
Rails.logger.debug { "Waiting for caches to clear #{debug_message}" }
|
91
93
|
# Wait a little over the 60 second in-process shard cache clearing
|
92
94
|
# threshold to ensure that all new stranded jobs are now being
|
@@ -96,9 +98,9 @@ module SwitchmanInstJobs
|
|
96
98
|
end
|
97
99
|
|
98
100
|
def acquire_advisory_lock(type, name)
|
99
|
-
@quoted_function_name ||= ::Delayed::Job.connection.quote_table_name(
|
101
|
+
@quoted_function_name ||= ::Delayed::Job.connection.quote_table_name("half_md5_as_bigint")
|
100
102
|
|
101
|
-
value = type == :singleton ? "singleton:#{name}" : name
|
103
|
+
value = (type == :singleton) ? "singleton:#{name}" : name
|
102
104
|
::Delayed::Job.connection.execute(
|
103
105
|
::Delayed::Job.sanitize_sql_for_conditions(
|
104
106
|
["SELECT pg_advisory_xact_lock(#{@quoted_function_name}(?))", value]
|
@@ -162,8 +164,8 @@ module SwitchmanInstJobs
|
|
162
164
|
|
163
165
|
# the rest of 3) is taken care of here
|
164
166
|
# make sure that all the jobs moved over are NOT next in strand
|
165
|
-
::Delayed::Job.where(next_in_strand: true, locked_by: nil, **{ column => value })
|
166
|
-
|
167
|
+
::Delayed::Job.where(next_in_strand: true, locked_by: nil, **{ column => value })
|
168
|
+
.update_all(next_in_strand: false)
|
167
169
|
end
|
168
170
|
|
169
171
|
# 4) is taken care of here, by leaving next_in_strand alone and
|
@@ -184,8 +186,8 @@ module SwitchmanInstJobs
|
|
184
186
|
}
|
185
187
|
|
186
188
|
strand_scope = ::Delayed::Job.shard(source_shard).where.not(strand: nil)
|
187
|
-
singleton_scope = ::Delayed::Job.shard(source_shard).where(
|
188
|
-
all_scope = ::Delayed::Job.shard(source_shard).where(
|
189
|
+
singleton_scope = ::Delayed::Job.shard(source_shard).where("strand IS NULL AND singleton IS NOT NULL")
|
190
|
+
all_scope = ::Delayed::Job.shard(source_shard).where("strand IS NOT NULL OR singleton IS NOT NULL")
|
189
191
|
|
190
192
|
singleton_blocker_additional_kwargs = {
|
191
193
|
locked_at: DateTime.now,
|
@@ -206,8 +208,8 @@ module SwitchmanInstJobs
|
|
206
208
|
shard_map = build_shard_map(all_scope, source_shard)
|
207
209
|
shard_map.each do |(target_shard, source_shard_ids)|
|
208
210
|
target_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
209
|
-
updated = ::Switchman::Shard.where(id: source_shard_ids, block_stranded: true)
|
210
|
-
|
211
|
+
updated = ::Switchman::Shard.where(id: source_shard_ids, block_stranded: true)
|
212
|
+
.update_all(block_stranded: false)
|
211
213
|
# If this is being manually re-run for some reason to clean something up, don't wait for nothing to happen
|
212
214
|
unless updated.zero?
|
213
215
|
clear_shard_cache("(#{source_shard.id} -> #{target_shard.id})",
|
@@ -227,16 +229,16 @@ module SwitchmanInstJobs
|
|
227
229
|
def unblock_strands(target_shard, batch_size: 10_000)
|
228
230
|
blocked_shard_ids = blocked_shards.pluck(:id)
|
229
231
|
query = lambda { |column, scope|
|
230
|
-
::Delayed::Job
|
231
|
-
where(id: ::Delayed::Job.select("DISTINCT ON (#{column}) id")
|
232
|
-
where(scope)
|
233
|
-
where.not(shard_id: blocked_shard_ids)
|
234
|
-
where(
|
235
|
-
::Delayed::Job.select(1).from("#{::Delayed::Job.quoted_table_name} dj2")
|
236
|
-
where("dj2.next_in_strand = true OR dj2.source = 'JobsMigrator::StrandBlocker'")
|
237
|
-
where("dj2.#{column} = delayed_jobs.#{column}").arel.exists.not
|
238
|
-
)
|
239
|
-
order(column, :strand_order_override, :id)).limit(batch_size)
|
232
|
+
::Delayed::Job
|
233
|
+
.where(id: ::Delayed::Job.select("DISTINCT ON (#{column}) id")
|
234
|
+
.where(scope)
|
235
|
+
.where.not(shard_id: blocked_shard_ids)
|
236
|
+
.where(
|
237
|
+
::Delayed::Job.select(1).from("#{::Delayed::Job.quoted_table_name} dj2")
|
238
|
+
.where("dj2.next_in_strand = true OR dj2.source = 'JobsMigrator::StrandBlocker'")
|
239
|
+
.where("dj2.#{column} = delayed_jobs.#{column}").arel.exists.not
|
240
|
+
)
|
241
|
+
.order(column, :strand_order_override, :id)).limit(batch_size)
|
240
242
|
}
|
241
243
|
|
242
244
|
target_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
@@ -247,12 +249,12 @@ module SwitchmanInstJobs
|
|
247
249
|
# batches
|
248
250
|
|
249
251
|
loop do
|
250
|
-
break if query.call(:strand,
|
252
|
+
break if query.call(:strand, "strand IS NOT NULL").update_all(next_in_strand: true).zero?
|
251
253
|
end
|
252
254
|
|
253
255
|
loop do
|
254
256
|
break if query.call(:singleton,
|
255
|
-
|
257
|
+
"strand IS NULL AND singleton IS NOT NULL").update_all(next_in_strand: true).zero?
|
256
258
|
end
|
257
259
|
end
|
258
260
|
end
|
@@ -277,16 +279,15 @@ module SwitchmanInstJobs
|
|
277
279
|
end
|
278
280
|
|
279
281
|
def blocked_by_migrator?(job_scope)
|
280
|
-
job_scope.exists?(source:
|
282
|
+
job_scope.exists?(source: "JobsMigrator::StrandBlocker") ||
|
281
283
|
blocked_shards.exists?(id: job_scope.distinct.pluck(:shard_id))
|
282
284
|
end
|
283
285
|
|
284
286
|
def blocked_strands
|
285
|
-
::Delayed::Job
|
286
|
-
where.not(strand: nil)
|
287
|
-
group(:strand)
|
288
|
-
having(
|
289
|
-
pluck(:strand)
|
287
|
+
::Delayed::Job
|
288
|
+
.where.not(strand: nil)
|
289
|
+
.group(:strand)
|
290
|
+
.having("NOT BOOL_OR(next_in_strand)")
|
290
291
|
end
|
291
292
|
|
292
293
|
def unblock_strand!(strand, new_parallelism: nil)
|
@@ -296,13 +297,14 @@ module SwitchmanInstJobs
|
|
296
297
|
::Delayed::Job.transaction do
|
297
298
|
acquire_advisory_lock(:strand, strand)
|
298
299
|
|
299
|
-
new_parallelism ||= job_scope.pick(
|
300
|
+
new_parallelism ||= job_scope.pick("MAX(max_concurrent)")
|
300
301
|
if new_parallelism
|
301
302
|
needed_jobs = new_parallelism - job_scope.where(next_in_strand: true).count
|
302
303
|
if needed_jobs.positive?
|
303
|
-
job_scope.where(next_in_strand: false,
|
304
|
-
|
305
|
-
|
304
|
+
job_scope.where(next_in_strand: false,
|
305
|
+
locked_by: nil,
|
306
|
+
singleton: nil).order(:strand_order_override, :id)
|
307
|
+
.limit(needed_jobs).update_all(next_in_strand: true)
|
306
308
|
else
|
307
309
|
0
|
308
310
|
end
|
@@ -311,12 +313,11 @@ module SwitchmanInstJobs
|
|
311
313
|
end
|
312
314
|
|
313
315
|
def blocked_singletons
|
314
|
-
::Delayed::Job
|
315
|
-
where(strand: nil)
|
316
|
-
where.not(singleton: nil)
|
317
|
-
group(:singleton)
|
318
|
-
having(
|
319
|
-
pluck(:singleton)
|
316
|
+
::Delayed::Job
|
317
|
+
.where(strand: nil)
|
318
|
+
.where.not(singleton: nil)
|
319
|
+
.group(:singleton)
|
320
|
+
.having("NOT BOOL_OR(next_in_strand)")
|
320
321
|
end
|
321
322
|
|
322
323
|
def unblock_singleton!(singleton)
|
@@ -326,9 +327,9 @@ module SwitchmanInstJobs
|
|
326
327
|
::Delayed::Job.transaction do
|
327
328
|
acquire_advisory_lock(:singleton, singleton)
|
328
329
|
|
329
|
-
id, next_in_strand = job_scope
|
330
|
-
|
331
|
-
|
330
|
+
id, next_in_strand = job_scope
|
331
|
+
.group(:singleton)
|
332
|
+
.pick("MIN(id), BOOL_OR(next_in_strand)")
|
332
333
|
|
333
334
|
if next_in_strand
|
334
335
|
0
|
@@ -338,13 +339,19 @@ module SwitchmanInstJobs
|
|
338
339
|
end
|
339
340
|
end
|
340
341
|
|
342
|
+
def blocked_job_count
|
343
|
+
::Delayed::Job.from(blocked_strands.select("count(id) AS ssize")).sum("ssize").to_i +
|
344
|
+
::Delayed::Job.from(blocked_singletons.select("count(id) AS ssize")).sum("ssize").to_i +
|
345
|
+
::Delayed::Job.where(strand: nil, singleton: nil, next_in_strand: false).count
|
346
|
+
end
|
347
|
+
|
341
348
|
private
|
342
349
|
|
343
350
|
def create_blocker_job(**kwargs)
|
344
351
|
first_job = ::Delayed::Job.create!(**kwargs, next_in_strand: false)
|
345
352
|
first_job.payload_object = ::Delayed::PerformableMethod.new(Kernel, :sleep, args: [0])
|
346
|
-
first_job.tag =
|
347
|
-
first_job.source =
|
353
|
+
first_job.tag = "Kernel.sleep"
|
354
|
+
first_job.source = "JobsMigrator::StrandBlocker"
|
348
355
|
first_job.max_attempts = 1
|
349
356
|
# If we ever have jobs left over from 9999 jobs moves of a single shard,
|
350
357
|
# something has gone terribly wrong
|
@@ -369,7 +376,7 @@ module SwitchmanInstJobs
|
|
369
376
|
def batch_move_jobs(target_shard:, source_shard:, scope:, batch_size:)
|
370
377
|
while scope.exists?
|
371
378
|
# Adapted from get_and_lock_next_available in delayed/backend/active_record.rb
|
372
|
-
target_jobs = scope.limit(batch_size).lock(
|
379
|
+
target_jobs = scope.limit(batch_size).lock("FOR UPDATE SKIP LOCKED")
|
373
380
|
|
374
381
|
query = source_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
|
375
382
|
<<~SQL
|
@@ -416,23 +423,23 @@ module SwitchmanInstJobs
|
|
416
423
|
# Once we stop supporting rails 5.2 we can just use insert_all from activerecord
|
417
424
|
def bulk_insert_jobs(objects)
|
418
425
|
records = objects.map do |object|
|
419
|
-
object.attributes.
|
426
|
+
object.attributes.filter_map do |(name, value)|
|
420
427
|
next if name == ::Delayed::Job.primary_key
|
421
428
|
|
422
429
|
if (type = ::Delayed::Job.attribute_types[name]).is_a?(::ActiveRecord::Type::Serialized)
|
423
430
|
value = type.serialize(value)
|
424
431
|
end
|
425
432
|
[name, value]
|
426
|
-
end.
|
433
|
+
end.to_h
|
427
434
|
end
|
428
|
-
return if records.
|
435
|
+
return if records.empty?
|
429
436
|
|
430
437
|
keys = records.first.keys
|
431
438
|
|
432
439
|
connection = ::Delayed::Job.connection
|
433
|
-
quoted_keys = keys.map { |k| connection.quote_column_name(k) }.join(
|
440
|
+
quoted_keys = keys.map { |k| connection.quote_column_name(k) }.join(", ")
|
434
441
|
|
435
|
-
connection.execute
|
442
|
+
connection.execute "DROP TABLE IF EXISTS delayed_jobs_bulk_copy"
|
436
443
|
connection.execute "CREATE TEMPORARY TABLE delayed_jobs_bulk_copy
|
437
444
|
(LIKE #{::Delayed::Job.quoted_table_name} INCLUDING DEFAULTS)"
|
438
445
|
connection.execute "COPY delayed_jobs_bulk_copy (#{quoted_keys}) FROM STDIN"
|
@@ -444,8 +451,8 @@ module SwitchmanInstJobs
|
|
444
451
|
result = connection.raw_connection.get_result
|
445
452
|
begin
|
446
453
|
result.check
|
447
|
-
rescue
|
448
|
-
raise connection.send(:translate_exception, e,
|
454
|
+
rescue => e
|
455
|
+
raise connection.send(:translate_exception, e, "COPY FROM STDIN")
|
449
456
|
end
|
450
457
|
connection.execute "INSERT INTO #{::Delayed::Job.quoted_table_name} (#{quoted_keys})
|
451
458
|
SELECT #{quoted_keys} FROM delayed_jobs_bulk_copy
|
@@ -460,7 +467,7 @@ module SwitchmanInstJobs
|
|
460
467
|
elsif value.is_a?(::ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array::Data)
|
461
468
|
quote_text(encode_array(value))
|
462
469
|
else
|
463
|
-
hash = { "\n" => '\\n', "\r" => '\\r', "\t" => '\\t',
|
470
|
+
hash = { "\n" => '\\n', "\r" => '\\r', "\t" => '\\t', "\\" => "\\\\" }
|
464
471
|
value.to_s.gsub(/[\n\r\t\\]/) { |c| hash[c] }
|
465
472
|
end
|
466
473
|
end
|
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SwitchmanInstJobs
|
2
4
|
module Switchman
|
3
5
|
module DatabaseServer
|
4
6
|
def delayed_jobs_shard(shard = nil)
|
5
|
-
return shard if config[:delayed_jobs_shard] ==
|
7
|
+
return shard if config[:delayed_jobs_shard] == "self"
|
6
8
|
|
7
9
|
dj_shard =
|
8
10
|
config[:delayed_jobs_shard] &&
|