switchman-inst-jobs 4.0.10 → 4.0.13

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: 8663d7c19e1900c68ab1595b7306cc2105233b1aa02bb687beee88cdbfa9ada5
4
+ data.tar.gz: 63f99e858a89ff31c8cda4a1dd35e072e58cda21ebf9d1cdb6a7434ad37f5968
5
5
  SHA512:
6
- metadata.gz: 0b00fb9a88dcec5e8c9d76175cb9de7194f107b98732cd27a46cfa63ecfd310d080055df26008f9301d16e9780e73ca6605e63ab30963d9a0753bd5b746fcf67
7
- data.tar.gz: 5954e29022225b3f7dcf8c9f32c37f97cd0edc6db535753da7633f79167c72141fc96f479e9e334106540205eec91f0901344e0f369f98edec65638a21b9b621
6
+ metadata.gz: '0322296a92637bb28cc00cfe41446fc39eb189fb4c5e433fb1adf115f1ab80204d91228c96a5902d5f2ece3906805e478ecf166dee37ef9d17b7ce9d4c28c1bd'
7
+ data.tar.gz: 769694ebfd1343c2449b18b14b8883aaab0cf3819e01446878328461542657d31df576501db5539f572cf5bae8ca5c460354e960e4c27f3b3996a6c3439894c4
@@ -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,76 @@ 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
+ end
290
+
291
+ def unblock_strand!(strand, new_parallelism: nil)
292
+ job_scope = ::Delayed::Job.where(strand: strand)
293
+ raise JobsBlockedError if blocked_by_migrator?(job_scope)
294
+
295
+ ::Delayed::Job.transaction do
296
+ acquire_advisory_lock(:strand, strand)
297
+
298
+ new_parallelism ||= job_scope.pick('MAX(max_concurrent)')
299
+ if new_parallelism
300
+ needed_jobs = new_parallelism - job_scope.where(next_in_strand: true).count
301
+ if needed_jobs.positive?
302
+ job_scope.where(next_in_strand: false, locked_by: nil,
303
+ singleton: nil).order(:strand_order_override, :id).
304
+ limit(needed_jobs).update_all(next_in_strand: true)
305
+ else
306
+ 0
307
+ end
308
+ end
309
+ end
310
+ end
311
+
312
+ def blocked_singletons
313
+ ::Delayed::Job.
314
+ where(strand: nil).
315
+ where.not(singleton: nil).
316
+ group(:singleton).
317
+ having('NOT BOOL_OR(next_in_strand)')
318
+ end
319
+
320
+ def unblock_singleton!(singleton)
321
+ job_scope = ::Delayed::Job.where(strand: nil, singleton: singleton)
322
+ raise JobsBlockedError if blocked_by_migrator?(job_scope)
323
+
324
+ ::Delayed::Job.transaction do
325
+ acquire_advisory_lock(:singleton, singleton)
326
+
327
+ id, next_in_strand = job_scope.
328
+ group(:singleton).
329
+ pick('MIN(id), BOOL_OR(next_in_strand)')
330
+
331
+ if next_in_strand
332
+ 0
333
+ elsif id
334
+ ::Delayed::Job.where(id: id).update_all(next_in_strand: true)
335
+ end
336
+ end
337
+ end
338
+
339
+ def blocked_job_count
340
+ ::Delayed::Job.from(blocked_strands.select('count(id) AS ssize')).sum('ssize').to_i +
341
+ ::Delayed::Job.from(blocked_singletons.select('count(id) AS ssize')).sum('ssize').to_i +
342
+ ::Delayed::Job.where(strand: nil, singleton: nil, next_in_strand: false).count
343
+ end
344
+
253
345
  private
254
346
 
255
347
  def create_blocker_job(**kwargs)
@@ -1,3 +1,3 @@
1
1
  module SwitchmanInstJobs
2
- VERSION = '4.0.10'.freeze
2
+ VERSION = '4.0.13'.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.13
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-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inst-jobs