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.
- 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/active_record/connection_adapters/connection_pool.rb +15 -0
- data/lib/switchman_inst_jobs/delayed/backend/base.rb +10 -9
- data/lib/switchman_inst_jobs/delayed/pool.rb +1 -1
- data/lib/switchman_inst_jobs/delayed/worker/health_check.rb +10 -12
- data/lib/switchman_inst_jobs/delayed/worker.rb +2 -2
- data/lib/switchman_inst_jobs/engine.rb +6 -4
- data/lib/switchman_inst_jobs/jobs_migrator.rb +23 -21
- data/lib/switchman_inst_jobs/switchman/shard.rb +8 -21
- data/lib/switchman_inst_jobs/version.rb +1 -1
- data/lib/switchman_inst_jobs.rb +8 -0
- metadata +39 -36
- data/db/migrate/20210809145804_add_n_strand_index.rb +0 -12
- data/db/migrate/20210812210128_add_singleton_column.rb +0 -200
- data/db/migrate/20210917232626_add_delete_conflicting_singletons_before_unlock_trigger.rb +0 -27
- data/db/migrate/20210928174754_fix_singleton_condition_in_before_insert.rb +0 -56
- 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(
|
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?
|
@@ -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(
|
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[
|
89
|
-
categories[
|
90
|
-
categories[
|
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[
|
93
|
-
categories[
|
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
|
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
|
)
|
@@ -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:
|
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-
|
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.
|
20
|
-
- - "
|
19
|
+
version: '2.0'
|
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: 2.
|
30
|
-
- - "
|
29
|
+
version: '2.0'
|
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
|
@@ -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:
|
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
|
@@ -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.
|
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
|