switchman-inst-jobs 4.0.10 → 4.0.13

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 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