switchman-inst-jobs 3.2.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/db/migrate/20101216224513_create_delayed_jobs.rb +1 -1
  3. data/db/migrate/20110208031356_add_delayed_jobs_tag.rb +1 -1
  4. data/db/migrate/20110426161613_add_delayed_jobs_max_attempts.rb +1 -1
  5. data/db/migrate/20110516225834_add_delayed_jobs_strand.rb +1 -1
  6. data/db/migrate/20110531144916_cleanup_delayed_jobs_indexes.rb +1 -1
  7. data/db/migrate/20110610213249_optimize_delayed_jobs.rb +1 -1
  8. data/db/migrate/20110831210257_add_delayed_jobs_next_in_strand.rb +1 -1
  9. data/db/migrate/20120510004759_delayed_jobs_delete_trigger_lock_for_update.rb +1 -1
  10. data/db/migrate/20120531150712_drop_psql_jobs_pop_fn.rb +1 -1
  11. data/db/migrate/20120607164022_delayed_jobs_use_advisory_locks.rb +1 -1
  12. data/db/migrate/20120607181141_index_jobs_on_locked_by.rb +2 -2
  13. data/db/migrate/20120608191051_add_jobs_run_at_index.rb +2 -2
  14. data/db/migrate/20120927184213_change_delayed_jobs_handler_to_text.rb +1 -1
  15. data/db/migrate/20140505215131_add_failed_jobs_original_job_id.rb +1 -1
  16. data/db/migrate/20140505215510_copy_failed_jobs_original_id.rb +2 -2
  17. data/db/migrate/20140505223637_drop_failed_jobs_original_id.rb +1 -1
  18. data/db/migrate/20140512213941_add_source_to_jobs.rb +1 -1
  19. data/db/migrate/20150807133223_add_max_concurrent_to_jobs.rb +1 -1
  20. data/db/migrate/20151123210429_add_expires_at_to_jobs.rb +1 -1
  21. data/db/migrate/20151210162949_improve_max_concurrent.rb +1 -1
  22. data/db/migrate/20161206323555_add_back_default_string_limits_jobs.rb +1 -1
  23. data/db/migrate/20170308045400_add_shard_id_to_delayed_jobs.rb +1 -11
  24. data/db/migrate/20170308045401_add_delayed_jobs_shard_id_to_switchman_shards.rb +5 -0
  25. data/db/migrate/20181217155351_speed_up_max_concurrent_triggers.rb +1 -1
  26. data/db/migrate/20190726154743_make_critical_columns_not_null.rb +1 -1
  27. data/db/migrate/20200330230722_add_id_to_get_delayed_jobs_index.rb +2 -2
  28. data/db/migrate/20200824222232_speed_up_max_concurrent_delete_trigger.rb +1 -1
  29. data/db/migrate/20200825011002_add_strand_order_override.rb +2 -2
  30. data/lib/switchman_inst_jobs/active_record/connection_adapters/connection_pool.rb +15 -0
  31. data/lib/switchman_inst_jobs/delayed/backend/base.rb +10 -9
  32. data/lib/switchman_inst_jobs/delayed/pool.rb +1 -1
  33. data/lib/switchman_inst_jobs/delayed/worker/health_check.rb +10 -12
  34. data/lib/switchman_inst_jobs/delayed/worker.rb +2 -2
  35. data/lib/switchman_inst_jobs/engine.rb +6 -4
  36. data/lib/switchman_inst_jobs/jobs_migrator.rb +23 -21
  37. data/lib/switchman_inst_jobs/switchman/shard.rb +8 -21
  38. data/lib/switchman_inst_jobs/version.rb +1 -1
  39. data/lib/switchman_inst_jobs.rb +8 -0
  40. metadata +39 -36
  41. data/db/migrate/20210809145804_add_n_strand_index.rb +0 -12
  42. data/db/migrate/20210812210128_add_singleton_column.rb +0 -200
  43. data/db/migrate/20210917232626_add_delete_conflicting_singletons_before_unlock_trigger.rb +0 -27
  44. data/db/migrate/20210928174754_fix_singleton_condition_in_before_insert.rb +0 -56
  45. data/db/migrate/20210929204903_update_conflicting_singleton_function_to_use_index.rb +0 -27
@@ -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(:delayed_jobs) do
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(:delayed_jobs) do
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?
@@ -47,7 +47,7 @@ module SwitchmanInstJobs
47
47
  Rails.logger.debug('Waiting for caches to clear')
48
48
  sleep(65)
49
49
  end
50
- delayed_jobs_shard.activate(:delayed_jobs) do
50
+ delayed_jobs_shard.activate(::Delayed::Backend::ActiveRecord::AbstractJob) do
51
51
  ::Delayed::Job.where(locked_by: ::Delayed::Backend::Base::ON_HOLD_LOCKED_BY, shard_id: id).
52
52
  in_batches(of: 10_000).
53
53
  update_all(
@@ -75,22 +75,14 @@ module SwitchmanInstJobs
75
75
  remove_instance_variable(:@delayed_jobs_shards) if instance_variable_defined?(:@delayed_jobs_shards)
76
76
  end
77
77
 
78
- def current(category = :primary)
79
- if category == :delayed_jobs
80
- active_shards[category] || super(:primary).delayed_jobs_shard
81
- else
82
- super
83
- end
84
- end
85
-
86
78
  def activate!(categories)
87
79
  if !@skip_delayed_job_auto_activation &&
88
- !categories[:delayed_jobs] &&
89
- categories[:primary] &&
90
- categories[:primary] != active_shards[:primary]
80
+ !categories[::Delayed::Backend::ActiveRecord::AbstractJob] &&
81
+ categories[::ActiveRecord::Base] &&
82
+ categories[::ActiveRecord::Base] != ::Switchman::Shard.current(::ActiveRecord::Base)
91
83
  skip_delayed_job_auto_activation do
92
- categories[:delayed_jobs] =
93
- categories[:primary].delayed_jobs_shard
84
+ categories[::Delayed::Backend::ActiveRecord::AbstractJob] =
85
+ categories[::ActiveRecord::Base].delayed_jobs_shard
94
86
  end
95
87
  end
96
88
  super
@@ -104,11 +96,6 @@ module SwitchmanInstJobs
104
96
  @skip_delayed_job_auto_activation = was
105
97
  end
106
98
 
107
- def create
108
- db = ::Switchman::DatabaseServer.server_for_new_shard
109
- db.create_new_shard
110
- end
111
-
112
99
  def periodic_clear_shard_cache
113
100
  # TODO: make this configurable
114
101
  @timed_cache ||= TimedCache.new(-> { 60.to_i.seconds.ago }) do
@@ -1,3 +1,3 @@
1
1
  module SwitchmanInstJobs
2
- VERSION = '3.2.4'.freeze
2
+ VERSION = '4.0.0'.freeze
3
3
  end
@@ -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
  )
@@ -14,6 +17,10 @@ module SwitchmanInstJobs
14
17
  ::Delayed::Backend::ActiveRecord::Job.prepend(
15
18
  Delayed::Backend::Base
16
19
  )
20
+ ::Delayed::Backend::Redis::Job.prepend(
21
+ Delayed::Backend::Base
22
+ )
23
+ ::Delayed::Backend::Redis::Job.column :shard_id, :integer
17
24
  ::Delayed::Pool.prepend Delayed::Pool
18
25
  ::Delayed::Worker.prepend Delayed::Worker
19
26
  ::Delayed::Worker::HealthCheck.prepend Delayed::Worker::HealthCheck
@@ -32,6 +39,7 @@ module SwitchmanInstJobs
32
39
  end
33
40
  end
34
41
 
42
+ require 'switchman_inst_jobs/active_record/connection_adapters/connection_pool'
35
43
  require 'switchman_inst_jobs/active_record/connection_adapters/postgresql_adapter'
36
44
  require 'switchman_inst_jobs/active_record/migration'
37
45
  require 'switchman_inst_jobs/delayed/settings'
metadata CHANGED
@@ -1,35 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: switchman-inst-jobs
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.4
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: 2021-10-07 00:00:00.000000000 Z
11
+ date: 2021-05-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inst-jobs
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 2.4.9
20
- - - "<"
19
+ version: '2.0'
20
+ - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: '4.0'
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: 2.4.9
30
- - - "<"
29
+ version: '2.0'
30
+ - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: '4.0'
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: '5.2'
53
+ version: '6.1'
54
54
  - - "<"
55
55
  - !ruby/object:Gem::Version
56
- version: '6.1'
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: '5.2'
63
+ version: '6.1'
64
64
  - - "<"
65
65
  - !ruby/object:Gem::Version
66
- version: '6.1'
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: '2.0'
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: '2.0'
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
@@ -110,16 +116,16 @@ dependencies:
110
116
  name: diplomat
111
117
  requirement: !ruby/object:Gem::Requirement
112
118
  requirements:
113
- - - ">="
119
+ - - "~>"
114
120
  - !ruby/object:Gem::Version
115
- version: '0'
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: '0'
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.6'
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.6'
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: '3.6'
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: '3.6'
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.3.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.3.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.8.1
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.8.1
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.18'
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.18'
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
@@ -296,13 +303,9 @@ files:
296
303
  - db/migrate/20200822014259_add_block_stranded_to_switchman_shards.rb
297
304
  - db/migrate/20200824222232_speed_up_max_concurrent_delete_trigger.rb
298
305
  - db/migrate/20200825011002_add_strand_order_override.rb
299
- - db/migrate/20210809145804_add_n_strand_index.rb
300
- - db/migrate/20210812210128_add_singleton_column.rb
301
- - db/migrate/20210917232626_add_delete_conflicting_singletons_before_unlock_trigger.rb
302
- - db/migrate/20210928174754_fix_singleton_condition_in_before_insert.rb
303
- - db/migrate/20210929204903_update_conflicting_singleton_function_to_use_index.rb
304
306
  - lib/switchman-inst-jobs.rb
305
307
  - lib/switchman_inst_jobs.rb
308
+ - lib/switchman_inst_jobs/active_record/connection_adapters/connection_pool.rb
306
309
  - lib/switchman_inst_jobs/active_record/connection_adapters/postgresql_adapter.rb
307
310
  - lib/switchman_inst_jobs/active_record/migration.rb
308
311
  - lib/switchman_inst_jobs/delayed/backend/base.rb
@@ -340,7 +343,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
340
343
  - !ruby/object:Gem::Version
341
344
  version: '0'
342
345
  requirements: []
343
- rubygems_version: 3.1.4
346
+ rubygems_version: 3.2.15
344
347
  signing_key:
345
348
  specification_version: 4
346
349
  summary: Switchman and Instructure Jobs compatibility gem.
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class AddNStrandIndex < ActiveRecord::Migration[5.2]
4
- disable_ddl_transaction!
5
-
6
- def change
7
- add_index :delayed_jobs, %i[strand next_in_strand id],
8
- name: 'n_strand_index',
9
- where: 'strand IS NOT NULL',
10
- algorithm: :concurrently
11
- end
12
- end
@@ -1,200 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class AddSingletonColumn < ActiveRecord::Migration[5.2]
4
- disable_ddl_transaction!
5
-
6
- def change
7
- add_column :delayed_jobs, :singleton, :string, if_not_exists: true
8
- add_column :failed_jobs, :singleton, :string, if_not_exists: true
9
- # only one job can be queued in a singleton
10
- add_index :delayed_jobs,
11
- :singleton,
12
- where: 'singleton IS NOT NULL AND locked_by IS NULL',
13
- unique: true,
14
- name: 'index_delayed_jobs_on_singleton_not_running',
15
- algorithm: :concurrently
16
- # only one job can be running for a singleton
17
- add_index :delayed_jobs,
18
- :singleton,
19
- where: 'singleton IS NOT NULL AND locked_by IS NOT NULL',
20
- unique: true,
21
- name: 'index_delayed_jobs_on_singleton_running',
22
- algorithm: :concurrently
23
-
24
- reversible do |direction|
25
- direction.up do
26
- execute(<<~SQL)
27
- CREATE OR REPLACE FUNCTION #{connection.quote_table_name('delayed_jobs_after_delete_row_tr_fn')} () RETURNS trigger AS $$
28
- DECLARE
29
- running_count integer;
30
- should_lock boolean;
31
- should_be_precise boolean;
32
- update_query varchar;
33
- skip_locked varchar;
34
- BEGIN
35
- IF OLD.strand IS NOT NULL THEN
36
- should_lock := true;
37
- should_be_precise := OLD.id % (OLD.max_concurrent * 4) = 0;
38
-
39
- IF NOT should_be_precise AND OLD.max_concurrent > 16 THEN
40
- running_count := (SELECT COUNT(*) FROM (
41
- SELECT 1 as one FROM delayed_jobs WHERE strand = OLD.strand AND next_in_strand = 't' LIMIT OLD.max_concurrent
42
- ) subquery_for_count);
43
- should_lock := running_count < OLD.max_concurrent;
44
- END IF;
45
-
46
- IF should_lock THEN
47
- PERFORM pg_advisory_xact_lock(half_md5_as_bigint(OLD.strand));
48
- END IF;
49
-
50
- -- note that we don't really care if the row we're deleting has a singleton, or if it even
51
- -- matches the row(s) we're going to update. we just need to make sure that whatever
52
- -- singleton we grab isn't already running (which is a simple existence check, since
53
- -- the unique indexes ensure there is at most one singleton running, and one queued)
54
- update_query := 'UPDATE delayed_jobs SET next_in_strand=true WHERE id IN (
55
- SELECT id FROM delayed_jobs j2
56
- WHERE next_in_strand=false AND
57
- j2.strand=$1.strand AND
58
- (j2.singleton IS NULL OR NOT EXISTS (SELECT 1 FROM delayed_jobs j3 WHERE j3.singleton=j2.singleton AND j3.id<>j2.id))
59
- ORDER BY j2.strand_order_override ASC, j2.id ASC
60
- LIMIT ';
61
-
62
- IF should_be_precise THEN
63
- running_count := (SELECT COUNT(*) FROM (
64
- SELECT 1 FROM delayed_jobs WHERE strand = OLD.strand AND next_in_strand = 't' LIMIT OLD.max_concurrent
65
- ) s);
66
- IF running_count < OLD.max_concurrent THEN
67
- update_query := update_query || '($1.max_concurrent - $2)';
68
- ELSE
69
- -- we have too many running already; just bail
70
- RETURN OLD;
71
- END IF;
72
- ELSE
73
- update_query := update_query || '1';
74
-
75
- -- n-strands don't require precise ordering; we can make this query more performant
76
- IF OLD.max_concurrent > 1 THEN
77
- skip_locked := ' SKIP LOCKED';
78
- END IF;
79
- END IF;
80
-
81
- update_query := update_query || ' FOR UPDATE' || COALESCE(skip_locked, '') || ')';
82
- EXECUTE update_query USING OLD, running_count;
83
- ELSIF OLD.singleton IS NOT NULL THEN
84
- UPDATE delayed_jobs SET next_in_strand = 't' WHERE singleton=OLD.singleton AND next_in_strand=false;
85
- END IF;
86
- RETURN OLD;
87
- END;
88
- $$ LANGUAGE plpgsql SET search_path TO #{::Switchman::Shard.current.name};
89
- SQL
90
- execute(<<~SQL)
91
- CREATE OR REPLACE FUNCTION #{connection.quote_table_name('delayed_jobs_before_insert_row_tr_fn')} () RETURNS trigger AS $$
92
- BEGIN
93
- IF NEW.strand IS NOT NULL THEN
94
- PERFORM pg_advisory_xact_lock(half_md5_as_bigint(NEW.strand));
95
- IF (SELECT COUNT(*) FROM (
96
- SELECT 1 FROM delayed_jobs WHERE strand = NEW.strand AND next_in_strand=true LIMIT NEW.max_concurrent
97
- ) s) = NEW.max_concurrent THEN
98
- NEW.next_in_strand := false;
99
- END IF;
100
- END IF;
101
- IF NEW.singleton IS NOT NULL THEN
102
- PERFORM 1 FROM delayed_jobs WHERE singleton = NEW.singleton;
103
- IF FOUND THEN
104
- NEW.next_in_strand := false;
105
- END IF;
106
- END IF;
107
- RETURN NEW;
108
- END;
109
- $$ LANGUAGE plpgsql SET search_path TO #{::Switchman::Shard.current.name};
110
- SQL
111
- end
112
- direction.down do
113
- execute(<<~SQL)
114
- CREATE OR REPLACE FUNCTION #{connection.quote_table_name('delayed_jobs_after_delete_row_tr_fn')} () RETURNS trigger AS $$
115
- DECLARE
116
- running_count integer;
117
- should_lock boolean;
118
- should_be_precise boolean;
119
- BEGIN
120
- IF OLD.strand IS NOT NULL THEN
121
- should_lock := true;
122
- should_be_precise := OLD.id % (OLD.max_concurrent * 4) = 0;
123
-
124
- IF NOT should_be_precise AND OLD.max_concurrent > 16 THEN
125
- running_count := (SELECT COUNT(*) FROM (
126
- SELECT 1 as one FROM delayed_jobs WHERE strand = OLD.strand AND next_in_strand = 't' LIMIT OLD.max_concurrent
127
- ) subquery_for_count);
128
- should_lock := running_count < OLD.max_concurrent;
129
- END IF;
130
-
131
- IF should_lock THEN
132
- PERFORM pg_advisory_xact_lock(half_md5_as_bigint(OLD.strand));
133
- END IF;
134
-
135
- IF should_be_precise THEN
136
- running_count := (SELECT COUNT(*) FROM (
137
- SELECT 1 as one FROM delayed_jobs WHERE strand = OLD.strand AND next_in_strand = 't' LIMIT OLD.max_concurrent
138
- ) subquery_for_count);
139
- IF running_count < OLD.max_concurrent THEN
140
- UPDATE delayed_jobs SET next_in_strand = 't' WHERE id IN (
141
- SELECT id FROM delayed_jobs j2 WHERE next_in_strand = 'f' AND
142
- j2.strand = OLD.strand ORDER BY j2.strand_order_override ASC, j2.id ASC LIMIT (OLD.max_concurrent - running_count) FOR UPDATE
143
- );
144
- END IF;
145
- ELSE
146
- -- n-strands don't require precise ordering; we can make this query more performant
147
- IF OLD.max_concurrent > 1 THEN
148
- UPDATE delayed_jobs SET next_in_strand = 't' WHERE id =
149
- (SELECT id FROM delayed_jobs j2 WHERE next_in_strand = 'f' AND
150
- j2.strand = OLD.strand ORDER BY j2.strand_order_override ASC, j2.id ASC LIMIT 1 FOR UPDATE SKIP LOCKED);
151
- ELSE
152
- UPDATE delayed_jobs SET next_in_strand = 't' WHERE id =
153
- (SELECT id FROM delayed_jobs j2 WHERE next_in_strand = 'f' AND
154
- j2.strand = OLD.strand ORDER BY j2.strand_order_override ASC, j2.id ASC LIMIT 1 FOR UPDATE);
155
- END IF;
156
- END IF;
157
- END IF;
158
- RETURN OLD;
159
- END;
160
- $$ LANGUAGE plpgsql SET search_path TO #{::Switchman::Shard.current.name};
161
- SQL
162
- execute(<<~SQL)
163
- CREATE OR REPLACE FUNCTION #{connection.quote_table_name('delayed_jobs_before_insert_row_tr_fn')} () RETURNS trigger AS $$
164
- BEGIN
165
- IF NEW.strand IS NOT NULL THEN
166
- PERFORM pg_advisory_xact_lock(half_md5_as_bigint(NEW.strand));
167
- IF (SELECT COUNT(*) FROM (
168
- SELECT 1 AS one FROM delayed_jobs WHERE strand = NEW.strand LIMIT NEW.max_concurrent
169
- ) subquery_for_count) = NEW.max_concurrent THEN
170
- NEW.next_in_strand := 'f';
171
- END IF;
172
- END IF;
173
- RETURN NEW;
174
- END;
175
- $$ LANGUAGE plpgsql SET search_path TO #{::Switchman::Shard.current.name};
176
- SQL
177
- end
178
- end
179
-
180
- connection.transaction do
181
- reversible do |direction|
182
- direction.up do
183
- drop_triggers
184
- execute("CREATE TRIGGER delayed_jobs_before_insert_row_tr BEFORE INSERT ON #{::Delayed::Job.quoted_table_name} FOR EACH ROW WHEN (NEW.strand IS NOT NULL OR NEW.singleton IS NOT NULL) EXECUTE PROCEDURE #{connection.quote_table_name('delayed_jobs_before_insert_row_tr_fn')}()")
185
- execute("CREATE TRIGGER delayed_jobs_after_delete_row_tr AFTER DELETE ON #{::Delayed::Job.quoted_table_name} FOR EACH ROW WHEN ((OLD.strand IS NOT NULL OR OLD.singleton IS NOT NULL) AND OLD.next_in_strand=true) EXECUTE PROCEDURE #{connection.quote_table_name('delayed_jobs_after_delete_row_tr_fn')}()")
186
- end
187
- direction.down do
188
- drop_triggers
189
- execute("CREATE TRIGGER delayed_jobs_before_insert_row_tr BEFORE INSERT ON #{::Delayed::Job.quoted_table_name} FOR EACH ROW WHEN (NEW.strand IS NOT NULL) EXECUTE PROCEDURE #{connection.quote_table_name('delayed_jobs_before_insert_row_tr_fn')}()")
190
- execute("CREATE TRIGGER delayed_jobs_after_delete_row_tr AFTER DELETE ON #{::Delayed::Job.quoted_table_name} FOR EACH ROW WHEN (OLD.strand IS NOT NULL AND OLD.next_in_strand = 't') EXECUTE PROCEDURE #{connection.quote_table_name('delayed_jobs_after_delete_row_tr_fn()')}")
191
- end
192
- end
193
- end
194
- end
195
-
196
- def drop_triggers
197
- execute("DROP TRIGGER delayed_jobs_before_insert_row_tr ON #{::Delayed::Job.quoted_table_name}")
198
- execute("DROP TRIGGER delayed_jobs_after_delete_row_tr ON #{::Delayed::Job.quoted_table_name}")
199
- end
200
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class AddDeleteConflictingSingletonsBeforeUnlockTrigger < ActiveRecord::Migration[5.2]
4
- def up
5
- execute(<<~SQL)
6
- CREATE FUNCTION #{connection.quote_table_name('delayed_jobs_before_unlock_delete_conflicting_singletons_row_fn')} () RETURNS trigger AS $$
7
- BEGIN
8
- IF EXISTS (SELECT 1 FROM delayed_jobs j2 WHERE j2.singleton=OLD.singleton) THEN
9
- DELETE FROM delayed_jobs WHERE id<>OLD.id AND singleton=OLD.singleton;
10
- END IF;
11
- RETURN NEW;
12
- END;
13
- $$ LANGUAGE plpgsql SET search_path TO #{::Switchman::Shard.current.name};
14
- SQL
15
- execute(<<~SQL)
16
- CREATE TRIGGER delayed_jobs_before_unlock_delete_conflicting_singletons_row_tr BEFORE UPDATE ON #{::Delayed::Job.quoted_table_name} FOR EACH ROW WHEN (
17
- OLD.singleton IS NOT NULL AND
18
- OLD.singleton=NEW.singleton AND
19
- OLD.locked_by IS NOT NULL AND
20
- NEW.locked_by IS NULL) EXECUTE PROCEDURE #{connection.quote_table_name('delayed_jobs_before_unlock_delete_conflicting_singletons_row_fn')}();
21
- SQL
22
- end
23
-
24
- def down
25
- execute("DROP FUNCTION #{connection.quote_table_name('delayed_jobs_before_unlock_delete_conflicting_singletons_row_tr_fn')}() CASCADE")
26
- end
27
- end
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class FixSingletonConditionInBeforeInsert < ActiveRecord::Migration[5.2]
4
- def change
5
- reversible do |direction|
6
- direction.up do
7
- execute(<<~SQL)
8
- CREATE OR REPLACE FUNCTION #{connection.quote_table_name('delayed_jobs_before_insert_row_tr_fn')} () RETURNS trigger AS $$
9
- BEGIN
10
- IF NEW.strand IS NOT NULL THEN
11
- PERFORM pg_advisory_xact_lock(half_md5_as_bigint(NEW.strand));
12
- IF (SELECT COUNT(*) FROM (
13
- SELECT 1 FROM delayed_jobs WHERE strand = NEW.strand AND next_in_strand=true LIMIT NEW.max_concurrent
14
- ) s) = NEW.max_concurrent THEN
15
- NEW.next_in_strand := false;
16
- END IF;
17
- END IF;
18
- IF NEW.singleton IS NOT NULL THEN
19
- -- this condition seems silly, but it forces postgres to use the two partial indexes on singleton,
20
- -- rather than doing a seq scan
21
- PERFORM 1 FROM delayed_jobs WHERE singleton = NEW.singleton AND (locked_by IS NULL OR locked_by IS NOT NULL);
22
- IF FOUND THEN
23
- NEW.next_in_strand := false;
24
- END IF;
25
- END IF;
26
- RETURN NEW;
27
- END;
28
- $$ LANGUAGE plpgsql SET search_path TO #{::Switchman::Shard.current.name};
29
- SQL
30
- end
31
- direction.down do
32
- execute(<<~SQL)
33
- CREATE OR REPLACE FUNCTION #{connection.quote_table_name('delayed_jobs_before_insert_row_tr_fn')} () RETURNS trigger AS $$
34
- BEGIN
35
- IF NEW.strand IS NOT NULL THEN
36
- PERFORM pg_advisory_xact_lock(half_md5_as_bigint(NEW.strand));
37
- IF (SELECT COUNT(*) FROM (
38
- SELECT 1 FROM delayed_jobs WHERE strand = NEW.strand AND next_in_strand=true LIMIT NEW.max_concurrent
39
- ) s) = NEW.max_concurrent THEN
40
- NEW.next_in_strand := false;
41
- END IF;
42
- END IF;
43
- IF NEW.singleton IS NOT NULL THEN
44
- PERFORM 1 FROM delayed_jobs WHERE singleton = NEW.singleton;
45
- IF FOUND THEN
46
- NEW.next_in_strand := false;
47
- END IF;
48
- END IF;
49
- RETURN NEW;
50
- END;
51
- $$ LANGUAGE plpgsql SET search_path TO #{::Switchman::Shard.current.name};
52
- SQL
53
- end
54
- end
55
- end
56
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class UpdateConflictingSingletonFunctionToUseIndex < ActiveRecord::Migration[5.2]
4
- def up
5
- execute(<<~SQL)
6
- CREATE OR REPLACE FUNCTION #{connection.quote_table_name('delayed_jobs_before_unlock_delete_conflicting_singletons_row_fn')} () RETURNS trigger AS $$
7
- BEGIN
8
- DELETE FROM delayed_jobs WHERE id<>OLD.id AND singleton=OLD.singleton AND locked_by IS NULL;
9
- RETURN NEW;
10
- END;
11
- $$ LANGUAGE plpgsql SET search_path TO #{::Switchman::Shard.current.name};
12
- SQL
13
- end
14
-
15
- def down
16
- execute(<<~SQL)
17
- CREATE OR REPLACE FUNCTION #{connection.quote_table_name('delayed_jobs_before_unlock_delete_conflicting_singletons_row_fn')} () RETURNS trigger AS $$
18
- BEGIN
19
- IF EXISTS (SELECT 1 FROM delayed_jobs j2 WHERE j2.singleton=OLD.singleton) THEN
20
- DELETE FROM delayed_jobs WHERE id<>OLD.id AND singleton=OLD.singleton;
21
- END IF;
22
- RETURN NEW;
23
- END;
24
- $$ LANGUAGE plpgsql SET search_path TO #{::Switchman::Shard.current.name};
25
- SQL
26
- end
27
- end