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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/db/migrate/20101216224513_create_delayed_jobs.rb +5 -3
  3. data/db/migrate/20110208031356_add_delayed_jobs_tag.rb +2 -0
  4. data/db/migrate/20110426161613_add_delayed_jobs_max_attempts.rb +2 -0
  5. data/db/migrate/20110516225834_add_delayed_jobs_strand.rb +2 -0
  6. data/db/migrate/20110531144916_cleanup_delayed_jobs_indexes.rb +9 -7
  7. data/db/migrate/20110610213249_optimize_delayed_jobs.rb +20 -18
  8. data/db/migrate/20110831210257_add_delayed_jobs_next_in_strand.rb +28 -26
  9. data/db/migrate/20120510004759_delayed_jobs_delete_trigger_lock_for_update.rb +20 -18
  10. data/db/migrate/20120531150712_drop_psql_jobs_pop_fn.rb +4 -2
  11. data/db/migrate/20120607164022_delayed_jobs_use_advisory_locks.rb +61 -59
  12. data/db/migrate/20120607181141_index_jobs_on_locked_by.rb +3 -1
  13. data/db/migrate/20120608191051_add_jobs_run_at_index.rb +3 -1
  14. data/db/migrate/20120927184213_change_delayed_jobs_handler_to_text.rb +2 -0
  15. data/db/migrate/20140505215131_add_failed_jobs_original_job_id.rb +2 -0
  16. data/db/migrate/20140505215510_copy_failed_jobs_original_id.rb +2 -0
  17. data/db/migrate/20140505223637_drop_failed_jobs_original_id.rb +2 -0
  18. data/db/migrate/20140512213941_add_source_to_jobs.rb +2 -0
  19. data/db/migrate/20150807133223_add_max_concurrent_to_jobs.rb +51 -49
  20. data/db/migrate/20151123210429_add_expires_at_to_jobs.rb +2 -0
  21. data/db/migrate/20151210162949_improve_max_concurrent.rb +37 -35
  22. data/db/migrate/20161206323555_add_back_default_string_limits_jobs.rb +4 -2
  23. data/db/migrate/20170308045400_add_shard_id_to_delayed_jobs.rb +2 -0
  24. data/db/migrate/20170308045401_add_delayed_jobs_shard_id_to_switchman_shards.rb +2 -0
  25. data/db/migrate/20181217155351_speed_up_max_concurrent_triggers.rb +73 -71
  26. data/db/migrate/20190726154743_make_critical_columns_not_null.rb +2 -0
  27. data/db/migrate/20200330230722_add_id_to_get_delayed_jobs_index.rb +14 -10
  28. data/db/migrate/20200818130101_add_on_hold_to_switchman_shards.rb +2 -0
  29. data/db/migrate/20200822014259_add_block_stranded_to_switchman_shards.rb +2 -0
  30. data/db/migrate/20200824222232_speed_up_max_concurrent_delete_trigger.rb +6 -4
  31. data/db/migrate/20200825011002_add_strand_order_override.rb +10 -7
  32. data/db/migrate/20210809145804_add_n_strand_index.rb +4 -3
  33. data/db/migrate/20210812210128_add_singleton_column.rb +12 -12
  34. data/db/migrate/20210917232626_add_delete_conflicting_singletons_before_unlock_trigger.rb +3 -3
  35. data/db/migrate/20210928174754_fix_singleton_condition_in_before_insert.rb +2 -2
  36. data/db/migrate/20210929204903_update_conflicting_singleton_function_to_use_index.rb +2 -2
  37. data/db/migrate/20211101190934_update_after_delete_trigger_for_singleton_index.rb +2 -2
  38. data/db/migrate/20211207094200_update_after_delete_trigger_for_singleton_transition_cases.rb +2 -2
  39. data/db/migrate/20211220112800_fix_singleton_race_condition_insert.rb +2 -2
  40. data/db/migrate/20211220113000_fix_singleton_race_condition_delete.rb +2 -2
  41. data/db/migrate/20220127091200_fix_singleton_unique_constraint.rb +6 -6
  42. data/db/migrate/20220128084800_update_insert_trigger_for_singleton_unique_constraint_change.rb +2 -2
  43. data/db/migrate/20220128084900_update_delete_trigger_for_singleton_unique_constraint_change.rb +2 -2
  44. data/db/migrate/20220203063200_remove_old_singleton_index.rb +8 -8
  45. data/db/migrate/20220328152900_add_failed_jobs_indicies.rb +2 -2
  46. data/lib/switchman-inst-jobs.rb +3 -1
  47. data/lib/switchman_inst_jobs/active_record/connection_adapters/postgresql_adapter.rb +3 -1
  48. data/lib/switchman_inst_jobs/active_record/migration.rb +5 -3
  49. data/lib/switchman_inst_jobs/delayed/backend/active_record/abstract_job.rb +2 -0
  50. data/lib/switchman_inst_jobs/delayed/backend/base.rb +10 -2
  51. data/lib/switchman_inst_jobs/delayed/message_sending.rb +2 -0
  52. data/lib/switchman_inst_jobs/delayed/pool.rb +2 -0
  53. data/lib/switchman_inst_jobs/delayed/settings.rb +3 -1
  54. data/lib/switchman_inst_jobs/delayed/worker/health_check.rb +5 -3
  55. data/lib/switchman_inst_jobs/delayed/worker.rb +2 -0
  56. data/lib/switchman_inst_jobs/engine.rb +7 -5
  57. data/lib/switchman_inst_jobs/guard_rail.rb +2 -0
  58. data/lib/switchman_inst_jobs/jobs_migrator.rb +62 -55
  59. data/lib/switchman_inst_jobs/new_relic.rb +2 -0
  60. data/lib/switchman_inst_jobs/switchman/database_server.rb +3 -1
  61. data/lib/switchman_inst_jobs/switchman/default_shard.rb +2 -0
  62. data/lib/switchman_inst_jobs/switchman/shard.rb +27 -23
  63. data/lib/switchman_inst_jobs/timed_cache.rb +2 -0
  64. data/lib/switchman_inst_jobs/version.rb +3 -1
  65. data/lib/switchman_inst_jobs/yaml_extensions.rb +3 -1
  66. data/lib/switchman_inst_jobs.rb +22 -20
  67. 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('delayed_jobs_after_delete_row_tr_fn')} () RETURNS trigger AS $$
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('delayed_jobs_after_delete_row_tr_fn')} () RETURNS trigger AS $$
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, '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'
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: 'index_delayed_jobs_on_singleton_not_running',
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: 'index_delayed_jobs_on_singleton_running',
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: 'index_delayed_jobs_on_singleton_not_running_old'
29
- remove_index :delayed_jobs, name: 'index_delayed_jobs_on_singleton_running_old'
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
@@ -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('delayed_jobs_before_insert_row_tr_fn')} () RETURNS trigger AS $$
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('delayed_jobs_before_insert_row_tr_fn')} () RETURNS trigger AS $$
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));
@@ -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('delayed_jobs_after_delete_row_tr_fn')} () RETURNS trigger AS $$
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('delayed_jobs_after_delete_row_tr_fn')} () RETURNS trigger AS $$
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: 'index_delayed_jobs_on_singleton_not_running_old'
8
- remove_index :delayed_jobs, name: 'index_delayed_jobs_on_singleton_running_old'
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, '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'
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: 'singleton IS NOT NULL AND locked_by IS NULL',
18
+ where: "singleton IS NOT NULL AND locked_by IS NULL",
19
19
  unique: true,
20
- name: 'index_delayed_jobs_on_singleton_not_running',
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: 'singleton IS NOT NULL AND locked_by IS NOT NULL',
26
+ where: "singleton IS NOT NULL AND locked_by IS NOT NULL",
27
27
  unique: true,
28
- name: 'index_delayed_jobs_on_singleton_running',
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: 'strand IS NOT NULL', algorithm: :concurrently
9
- add_index :failed_jobs, :singleton, where: 'singleton IS NOT NULL', algorithm: :concurrently
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
@@ -1 +1,3 @@
1
- require 'switchman_inst_jobs'
1
+ # frozen_string_literal: true
2
+
3
+ require "switchman_inst_jobs"
@@ -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 = '()', path = ::Switchman::Shard.current.name)
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('delayed_engine')
24
+ if sources.delete("delayed_engine")
23
25
  # rubocop:disable Rails/Output
24
- puts 'NOTE: Not installing delayed_engine migrations in an application using switchman-inst-jobs'
25
- puts '(use rake switchman_inst_jobs:install:migrations instead)'
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
  module Delayed
3
5
  module Backend
@@ -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 == 'Delayed::Backend::ActiveRecord::Job'
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?('jobs_held') && shard.jobs_held
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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SwitchmanInstJobs
2
4
  module Delayed
3
5
  module MessageSending
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SwitchmanInstJobs
2
4
  module Delayed
3
5
  module Pool
@@ -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(:[], 'workers') || []).map { |w| w['shard'] }.compact.uniq
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['service_name']
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['service_name'] =
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['service_name'] = original_service_name
20
+ ::Delayed::Settings.worker_health_check_config["service_name"] = original_service_name
19
21
  end
20
22
 
21
23
  def reschedule_abandoned_jobs
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SwitchmanInstJobs
2
4
  module Delayed
3
5
  module Worker
@@ -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 'sharding.active_record', after: 'switchman.extend_connection_adapters' do
7
+ initializer "sharding.active_record", after: "switchman.extend_connection_adapters" do
6
8
  SwitchmanInstJobs.initialize_active_record
7
9
  end
8
10
 
9
- initializer 'sharding.delayed' do
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: 'JobsMigrator::StrandBlocker', **{ column => job.try(column) }).delete_all
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 'sharding.guard_rail', after: 'switchman.extend_guard_rail' do
45
+ initializer "sharding.guard_rail", after: "switchman.extend_guard_rail" do
44
46
  SwitchmanInstJobs.initialize_guard_rail
45
47
  end
46
48
 
47
- initializer 'sharding.switchman' do
49
+ initializer "sharding.switchman" do
48
50
  SwitchmanInstJobs.initialize_switchman
49
51
  end
50
52
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SwitchmanInstJobs
2
4
  module GuardRail
3
5
  module ClassMethods
@@ -1,5 +1,7 @@
1
- require 'set'
2
- require 'parallel'
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?('updated_at')
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('shard/*')
89
- ::Switchman.cache.delete('default_shard') if default
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('half_md5_as_bigint')
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
- update_all(next_in_strand: false)
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('strand IS NULL AND singleton IS NOT NULL')
188
- all_scope = ::Delayed::Job.shard(source_shard).where('strand IS NOT NULL OR singleton IS NOT NULL')
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
- update_all(block_stranded: false)
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, 'strand IS NOT NULL').update_all(next_in_strand: true).zero?
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
- 'strand IS NULL AND singleton IS NOT NULL').update_all(next_in_strand: true).zero?
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: 'JobsMigrator::StrandBlocker') ||
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('NOT BOOL_OR(next_in_strand)').
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('MAX(max_concurrent)')
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, locked_by: nil,
304
- singleton: nil).order(:strand_order_override, :id).
305
- limit(needed_jobs).update_all(next_in_strand: true)
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('NOT BOOL_OR(next_in_strand)').
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
- group(:singleton).
331
- pick('MIN(id), BOOL_OR(next_in_strand)')
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 = 'Kernel.sleep'
347
- first_job.source = 'JobsMigrator::StrandBlocker'
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('FOR UPDATE SKIP LOCKED')
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.map do |(name, value)|
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.compact.to_h
433
+ end.to_h
427
434
  end
428
- return if records.length.zero?
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 'DROP TABLE IF EXISTS delayed_jobs_bulk_copy'
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 StandardError => e
448
- raise connection.send(:translate_exception, e, 'COPY FROM STDIN')
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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SwitchmanInstJobs
2
4
  module NewRelic
3
5
  module FixNewRelicDelayedJobs
@@ -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] == 'self'
7
+ return shard if config[:delayed_jobs_shard] == "self"
6
8
 
7
9
  dj_shard =
8
10
  config[:delayed_jobs_shard] &&
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SwitchmanInstJobs
2
4
  module Switchman
3
5
  module DefaultShard