inst-jobs 1.0.4 → 2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7018574b89f64cc8e0a8e601ba594b2ae346cd1fed66237a62b02a71cdaa14c
4
- data.tar.gz: bb5865eb7e5933717394ef47e85904827d4e065a2994de2eb9d7c4a43790da80
3
+ metadata.gz: d674b7da21caf04eb87ff9823ed549c93a901219669316090d088f0699564e59
4
+ data.tar.gz: 021456d34f12eff8cc988db866018d701fc77ffdbb57e9fb308fc1bd25a91ecb
5
5
  SHA512:
6
- metadata.gz: 9eeed8f4e9f853ed216c1c1817ab499f27ca6fe4c66837ce07b6e54e4d81f5ada261f19180403f9c6cf211f32daec480559ae9566f5d76b5c975196fe7998688
7
- data.tar.gz: fea866765cec29e00c5b94da17d5ce3f0b432ca6d79111325d5fee2829a33b3bc445896079fe93d0a70537a59abc0dddd45d8869a32ab1c1e0e6ab3c24848482
6
+ metadata.gz: ad78cfdd9026db24b714c532c8ee837a875e443afc375909f0c130e3cfbf87d1f872344f982d931838bfa6649a2f1edc59430f6444a2baee08f8afb568015cfc
7
+ data.tar.gz: e2b127477f0687958178505628b9544aa5c49e7aa1d0ceef32892250aa26aeb1c77f12bcacd6682e17c2bc379f987b154a0f982e029852432c39f7b3a5335df8
@@ -49,10 +49,20 @@ class Periodic
49
49
  end
50
50
 
51
51
  def enqueue
52
- Delayed::Job.enqueue(self, **@job_args.merge(:max_attempts => 1,
53
- :run_at => @cron.next_time(Delayed::Periodic.now).utc.to_time,
54
- :singleton => tag,
55
- on_conflict: :patient))
52
+ Delayed::Job.enqueue(self, **enqueue_args)
53
+ end
54
+
55
+ def enqueue_args
56
+ inferred_args = {
57
+ max_attempts: 1,
58
+ run_at: @cron.next_time(Delayed::Periodic.now).utc.to_time,
59
+ singleton: (@job_args[:singleton] == false ? nil : tag),
60
+ # yes, checking for whether it is actually the boolean literal false,
61
+ # which means the consuming code really does not want this job to be
62
+ # a singleton at all.
63
+ on_conflict: :patient
64
+ }
65
+ @job_args.merge(inferred_args)
56
66
  end
57
67
 
58
68
  def perform
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Delayed
4
- VERSION = "1.0.4"
4
+ VERSION = "2.0.0"
5
5
  end
@@ -22,31 +22,46 @@ module Delayed
22
22
 
23
23
  def reschedule_abandoned_jobs
24
24
  return if Settings.worker_health_check_type == :none
25
+ Delayed::Job.transaction do
26
+ # this job is a special case, and is not a singleton
27
+ # because if it gets wiped out suddenly during execution
28
+ # it can't go clean up it's abandoned self. Therefore,
29
+ # we try to get an advisory lock when it runs. If we succeed,
30
+ # no other job is trying to do this right now (and if we abandon the
31
+ # job, the transaction will end, releasing the advisory lock).
32
+ result = attempt_advisory_lock
33
+ return unless result
34
+ checker = Worker::HealthCheck.build(
35
+ type: Settings.worker_health_check_type,
36
+ config: Settings.worker_health_check_config,
37
+ worker_name: 'cleanup-crew'
38
+ )
39
+ live_workers = checker.live_workers
25
40
 
26
- checker = Worker::HealthCheck.build(
27
- type: Settings.worker_health_check_type,
28
- config: Settings.worker_health_check_config,
29
- worker_name: 'cleanup-crew'
30
- )
31
- live_workers = checker.live_workers
32
-
33
- Delayed::Job.running_jobs.each do |job|
34
- # prefetched jobs have their own way of automatically unlocking themselves
35
- next if job.locked_by.start_with?("prefetch:")
36
- unless live_workers.include?(job.locked_by)
37
- begin
38
- Delayed::Job.transaction do
39
- # double check that the job is still there. locked_by will immediately be reset
40
- # to nil in this transaction by Job#reschedule
41
- next unless Delayed::Job.where(id: job, locked_by: job.locked_by).update_all(locked_by: "abandoned job cleanup") == 1
42
- job.reschedule
41
+ Delayed::Job.running_jobs.each do |job|
42
+ # prefetched jobs have their own way of automatically unlocking themselves
43
+ next if job.locked_by.start_with?("prefetch:")
44
+ unless live_workers.include?(job.locked_by)
45
+ begin
46
+ Delayed::Job.transaction do
47
+ # double check that the job is still there. locked_by will immediately be reset
48
+ # to nil in this transaction by Job#reschedule
49
+ next unless Delayed::Job.where(id: job, locked_by: job.locked_by).update_all(locked_by: "abandoned job cleanup") == 1
50
+ job.reschedule
51
+ end
52
+ rescue
53
+ ::Rails.logger.error "Failure rescheduling abandoned job #{job.id} #{$!.inspect}"
43
54
  end
44
- rescue
45
- ::Rails.logger.error "Failure rescheduling abandoned job #{job.id} #{$!.inspect}"
46
55
  end
47
56
  end
48
57
  end
49
58
  end
59
+
60
+ def attempt_advisory_lock
61
+ lock_name = "Delayed::Worker::HealthCheck#reschedule_abandoned_jobs"
62
+ output = ActiveRecord::Base.connection.execute("SELECT pg_try_advisory_xact_lock(half_md5_as_bigint('#{lock_name}'));")
63
+ output.getvalue(0, 0)
64
+ end
50
65
  end
51
66
 
52
67
  attr_accessor :config, :worker_name
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Delayed::Periodic do
6
+ around(:each) do |block|
7
+ # make sure we can use ".cron" and
8
+ # such safely without leaking global state
9
+ prev_sched = Delayed::Periodic.scheduled
10
+ prev_ovr = Delayed::Periodic.overrides
11
+ Delayed::Periodic.scheduled = {}
12
+ Delayed::Periodic.overrides = {}
13
+ block.call
14
+ ensure
15
+ Delayed::Periodic.scheduled = prev_sched
16
+ Delayed::Periodic.overrides = prev_ovr
17
+ end
18
+
19
+ describe ".cron" do
20
+ let(:job_name){ 'just a test'}
21
+ it "provides a tag by default for periodic jobs" do
22
+ Delayed::Periodic.cron job_name, '*/10 * * * *' do
23
+ # no-op
24
+ end
25
+ instance = Delayed::Periodic.scheduled[job_name]
26
+ expect(instance).to_not be_nil
27
+ expect(instance.enqueue_args[:singleton]).to eq("periodic: just a test")
28
+ end
29
+
30
+ it "uses no singleton if told to skip" do
31
+ Delayed::Periodic.cron job_name, '*/10 * * * *', {singleton: false} do
32
+ # no-op
33
+ end
34
+ instance = Delayed::Periodic.scheduled[job_name]
35
+ expect(instance).to_not be_nil
36
+ expect(instance.enqueue_args[:singleton]).to be_nil
37
+ end
38
+ end
39
+ end
@@ -107,6 +107,15 @@ RSpec.describe Delayed::Worker::HealthCheck do
107
107
  @dead_job.reload
108
108
  expect(@dead_job.locked_by).to eq 'prefetch:some_node'
109
109
  end
110
+
111
+ it "bails immediately if advisory lock already taken" do
112
+ allow(Delayed::Worker::HealthCheck).to receive(:attempt_advisory_lock).and_return(false)
113
+ Delayed::Worker::HealthCheck.reschedule_abandoned_jobs
114
+ @dead_job.reload
115
+ expect(@dead_job.run_at.to_i).to eq(initial_run_at.to_i)
116
+ expect(@dead_job.locked_at).to_not be_nil
117
+ expect(@dead_job.locked_by).to_not be_nil
118
+ end
110
119
  end
111
120
 
112
121
  describe '#initialize' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inst-jobs
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Luetke
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2020-11-25 00:00:00.000000000 Z
12
+ date: 2020-12-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -412,6 +412,7 @@ files:
412
412
  - spec/delayed/cli_spec.rb
413
413
  - spec/delayed/daemon_spec.rb
414
414
  - spec/delayed/message_sending_spec.rb
415
+ - spec/delayed/periodic_spec.rb
415
416
  - spec/delayed/server_spec.rb
416
417
  - spec/delayed/settings_spec.rb
417
418
  - spec/delayed/work_queue/in_process_spec.rb
@@ -448,7 +449,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
448
449
  requirements:
449
450
  - - ">="
450
451
  - !ruby/object:Gem::Version
451
- version: '2.3'
452
+ version: '2.6'
452
453
  required_rubygems_version: !ruby/object:Gem::Requirement
453
454
  requirements:
454
455
  - - ">="
@@ -480,6 +481,7 @@ test_files:
480
481
  - spec/delayed/cli_spec.rb
481
482
  - spec/delayed/daemon_spec.rb
482
483
  - spec/delayed/worker_spec.rb
484
+ - spec/delayed/periodic_spec.rb
483
485
  - spec/delayed/message_sending_spec.rb
484
486
  - spec/delayed/settings_spec.rb
485
487
  - spec/delayed/work_queue/in_process_spec.rb