switchman-inst-jobs 4.0.10 → 4.0.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f11936008b976dcf8421f1e8c232c04f70e9e1883c9aff5f2191d838bca2155
4
- data.tar.gz: d43598b5e9998f83564282b7900c696fd815c55cd80088e14f60c14d1a01d850
3
+ metadata.gz: e4081fb390625cd34169a6585ffaddb12143403e3545988a1ada3ca90d34dc2d
4
+ data.tar.gz: 2f60cfa5de1529a206115bd2d68758f41f71763afbdaa980095de67dda1da544
5
5
  SHA512:
6
- metadata.gz: 0b00fb9a88dcec5e8c9d76175cb9de7194f107b98732cd27a46cfa63ecfd310d080055df26008f9301d16e9780e73ca6605e63ab30963d9a0753bd5b746fcf67
7
- data.tar.gz: 5954e29022225b3f7dcf8c9f32c37f97cd0edc6db535753da7633f79167c72141fc96f479e9e334106540205eec91f0901344e0f369f98edec65638a21b9b621
6
+ metadata.gz: 86bb9901b00dd38410092f17e15e1ff9b70b4907426ad32b7f2779a65844d7442e01e8df056d2293c90354eb814e880068854713e9169099e7d772294fc0a1d9
7
+ data.tar.gz: 9376187ebecfaab79e07696fd44ed762694e5ed06e9b539183f93f0f49e9706fa9eef59d6e9ef5456bccae2be780360d021da600e854eb8f2afcace71ca25bb5
@@ -8,6 +8,9 @@ module SwitchmanInstJobs
8
8
  end
9
9
  end
10
10
 
11
+ class JobsBlockedError < RuntimeError
12
+ end
13
+
11
14
  module Delayed
12
15
  module Backend
13
16
  module Base
@@ -9,6 +9,16 @@ module SwitchmanInstJobs
9
9
  @before_move_callbacks << proc
10
10
  end
11
11
 
12
+ def add_validation_callback(proc)
13
+ @validation_callbacks ||= []
14
+ @validation_callbacks << proc
15
+ end
16
+
17
+ def clear_callbacks!
18
+ @before_move_callbacks = []
19
+ @validation_callbacks = []
20
+ end
21
+
12
22
  def transaction_on(shards, &block)
13
23
  return yield if shards.empty?
14
24
 
@@ -31,6 +41,10 @@ module SwitchmanInstJobs
31
41
  source_shards << shard.delayed_jobs_shard.id
32
42
  target_shard = target_shard.try(:id) || target_shard
33
43
  target_shards[target_shard] += [shard.id]
44
+
45
+ @validation_callbacks&.each do |proc|
46
+ proc.call(shard: shard, target_shard: ::Switchman::Shard.find(target_shard))
47
+ end
34
48
  end
35
49
 
36
50
  # Do the updates in batches and then just clear redis instead of clearing them one at a time
@@ -81,6 +95,17 @@ module SwitchmanInstJobs
81
95
  sleep(65) unless @skip_cache_wait
82
96
  end
83
97
 
98
+ def acquire_advisory_lock(type, name)
99
+ @quoted_function_name ||= ::Delayed::Job.connection.quote_table_name('half_md5_as_bigint')
100
+
101
+ value = type == :singleton ? "singleton:#{name}" : name
102
+ ::Delayed::Job.connection.execute(
103
+ ::Delayed::Job.sanitize_sql_for_conditions(
104
+ ["SELECT pg_advisory_xact_lock(#{@quoted_function_name}(?))", value]
105
+ )
106
+ )
107
+ end
108
+
84
109
  # This method expects that all relevant shards already have block_stranded: true
85
110
  # but otherwise jobs can be running normally
86
111
  def run
@@ -167,15 +192,12 @@ module SwitchmanInstJobs
167
192
  locked_by: ::Delayed::Backend::Base::ON_HOLD_BLOCKER
168
193
  }
169
194
 
170
- quoted_function_name = ::Delayed::Job.connection.quote_table_name('half_md5_as_bigint')
171
195
  strand_advisory_lock_fn = lambda do |value|
172
- ::Delayed::Job.connection.execute("SELECT pg_advisory_xact_lock(#{quoted_function_name}('#{value}'))")
196
+ acquire_advisory_lock(:strand, value)
173
197
  end
174
198
 
175
199
  singleton_advisory_lock_fn = lambda do |value|
176
- ::Delayed::Job.connection.execute(
177
- "SELECT pg_advisory_xact_lock(#{quoted_function_name}('singleton:#{value}'))"
178
- )
200
+ acquire_advisory_lock(:singleton, value)
179
201
  end
180
202
 
181
203
  handler.call(strand_scope, :strand, {}, strand_advisory_lock_fn)
@@ -203,12 +225,12 @@ module SwitchmanInstJobs
203
225
  end
204
226
 
205
227
  def unblock_strands(target_shard, batch_size: 10_000)
206
- block_stranded_ids = ::Switchman::Shard.where(block_stranded: true).pluck(:id)
228
+ blocked_shard_ids = blocked_shards.pluck(:id)
207
229
  query = lambda { |column, scope|
208
230
  ::Delayed::Job.
209
231
  where(id: ::Delayed::Job.select("DISTINCT ON (#{column}) id").
210
232
  where(scope).
211
- where.not(shard_id: block_stranded_ids).
233
+ where.not(shard_id: blocked_shard_ids).
212
234
  where(
213
235
  ::Delayed::Job.select(1).from("#{::Delayed::Job.quoted_table_name} dj2").
214
236
  where("dj2.next_in_strand = true OR dj2.source = 'JobsMigrator::StrandBlocker'").
@@ -250,6 +272,72 @@ module SwitchmanInstJobs
250
272
  end
251
273
  end
252
274
 
275
+ def blocked_shards
276
+ ::Switchman::Shard.where(block_stranded: true).or(::Switchman::Shard.where(jobs_held: true))
277
+ end
278
+
279
+ def blocked_by_migrator?(job_scope)
280
+ job_scope.exists?(source: 'JobsMigrator::StrandBlocker') ||
281
+ blocked_shards.exists?(id: job_scope.distinct.pluck(:shard_id))
282
+ end
283
+
284
+ 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)
290
+ end
291
+
292
+ def unblock_strand!(strand, new_parallelism: nil)
293
+ job_scope = ::Delayed::Job.where(strand: strand)
294
+ raise JobsBlockedError if blocked_by_migrator?(job_scope)
295
+
296
+ ::Delayed::Job.transaction do
297
+ acquire_advisory_lock(:strand, strand)
298
+
299
+ new_parallelism ||= job_scope.pick('MAX(max_concurrent)')
300
+ if new_parallelism
301
+ needed_jobs = new_parallelism - job_scope.where(next_in_strand: true).count
302
+ 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)
306
+ else
307
+ 0
308
+ end
309
+ end
310
+ end
311
+ end
312
+
313
+ 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)
320
+ end
321
+
322
+ def unblock_singleton!(singleton)
323
+ job_scope = ::Delayed::Job.where(strand: nil, singleton: singleton)
324
+ raise JobsBlockedError if blocked_by_migrator?(job_scope)
325
+
326
+ ::Delayed::Job.transaction do
327
+ acquire_advisory_lock(:singleton, singleton)
328
+
329
+ id, next_in_strand = job_scope.
330
+ group(:singleton).
331
+ pick('MIN(id), BOOL_OR(next_in_strand)')
332
+
333
+ if next_in_strand
334
+ 0
335
+ elsif id
336
+ ::Delayed::Job.where(id: id).update_all(next_in_strand: true)
337
+ end
338
+ end
339
+ end
340
+
253
341
  private
254
342
 
255
343
  def create_blocker_job(**kwargs)
@@ -1,3 +1,3 @@
1
1
  module SwitchmanInstJobs
2
- VERSION = '4.0.10'.freeze
2
+ VERSION = '4.0.12'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: switchman-inst-jobs
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.10
4
+ version: 4.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bryan Petty
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-01 00:00:00.000000000 Z
11
+ date: 2022-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inst-jobs