job_contracts 0.1.2 → 0.1.5
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/README.md +26 -13
- data/lib/job_contracts/concerns/contractable.rb +20 -13
- data/lib/job_contracts/concerns/sidekiq_contractable.rb +7 -29
- data/lib/job_contracts/railtie.rb +6 -0
- data/lib/job_contracts/sidekiq_job_hash_middleware.rb +10 -0
- data/lib/job_contracts/version.rb +1 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d04fb7e6db54d4ed074bd176cd6a966f74ad964889c3970c542d921a1d605936
|
4
|
+
data.tar.gz: 3c3612c4dfb226a3155b0192c08e133b4610023f490f051e4cd12852df55ba45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b56ddc45288e5b424e1844af3e4cde8d07fffa90a37c4236c8beef2f0654b27b13a760918fef9e338e69c3ffe1da0ea0978b7c195e02048ee3703295cd4f8dda
|
7
|
+
data.tar.gz: b560b5b6d179ca0d35f2b734600c265ec4c96526c18ca1f9df7d4d92a1f20bd7fa72b089a637a0807b416b43af64aeb31d9794d1a41912fcace75812b89b3079
|
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
[](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
|
2
2
|
[](https://www.codacy.com/gh/hopsoft/job_contracts/dashboard?utm_source=github.com&utm_medium=referral&utm_content=hopsoft/job_contracts&utm_campaign=Badge_Grade)
|
3
|
-

|
4
|
+
[](https://badge.fury.io/rb/job_contracts)
|
5
|
+
[](https://rubygems.org/gems/job_contracts)
|
6
|
+
[](https://twitter.com/hopsoft)
|
4
7
|
|
5
8
|
# Job Contracts
|
6
9
|
|
@@ -47,7 +50,6 @@ class ImportantJob < ApplicationJob
|
|
47
50
|
include JobContracts::Contractable
|
48
51
|
|
49
52
|
queue_as :default
|
50
|
-
|
51
53
|
add_contract JobContracts::DurationContract.new(max: 5.seconds)
|
52
54
|
|
53
55
|
def perform
|
@@ -97,7 +99,7 @@ Contracts support the following constructor arguments.
|
|
97
99
|
|
98
100
|
### Defining a Contract
|
99
101
|
|
100
|
-
Here's a contrived but simple example that ensures the first argument to perform fits within a specific range of values.
|
102
|
+
Here's a contrived, but simple, example that ensures the first argument passed to perform fits within a specific range of values.
|
101
103
|
|
102
104
|
```ruby
|
103
105
|
# app/contracts/argument_contract.rb
|
@@ -203,7 +205,6 @@ class ImportantJob < ApplicationJob
|
|
203
205
|
include JobContracts::Contractable
|
204
206
|
|
205
207
|
queue_as :default
|
206
|
-
|
207
208
|
on_contract_breach :take_action
|
208
209
|
add_contract JobContracts::DurationContract.new(max: 5.seconds)
|
209
210
|
|
@@ -222,10 +223,7 @@ class ImportantJob < ApplicationJob
|
|
222
223
|
include JobContracts::Contractable
|
223
224
|
|
224
225
|
queue_as :default
|
225
|
-
|
226
|
-
on_contract_breach -> (contract) {
|
227
|
-
# take action...
|
228
|
-
}
|
226
|
+
on_contract_breach -> (contract) { # take action... }
|
229
227
|
|
230
228
|
add_contract JobContracts::DurationContract.new(max: 5.seconds)
|
231
229
|
|
@@ -237,11 +235,26 @@ end
|
|
237
235
|
|
238
236
|
## Sidekiq
|
239
237
|
|
240
|
-
Sidekiq
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
238
|
+
`Sidekiq::Job`s are also supported.
|
239
|
+
|
240
|
+
```ruby
|
241
|
+
class ImportantJob
|
242
|
+
include Sidekiq::Job
|
243
|
+
include JobContracts::SidekiqContractable
|
244
|
+
|
245
|
+
sidekiq_options queue: :default
|
246
|
+
add_contract JobContracts::DurationContract.new(max: 1.second)
|
247
|
+
|
248
|
+
def perform
|
249
|
+
# logic...
|
250
|
+
end
|
251
|
+
|
252
|
+
# default callback that's invoked if the contract is breached
|
253
|
+
def contract_breached!(contract)
|
254
|
+
# handle breach...
|
255
|
+
end
|
256
|
+
end
|
257
|
+
```
|
245
258
|
|
246
259
|
## Todo
|
247
260
|
|
@@ -15,21 +15,16 @@ module JobContracts
|
|
15
15
|
# fetch sidekiq job/worker metadata on main thread
|
16
16
|
try :sidekiq_job_metadata
|
17
17
|
|
18
|
-
halted =
|
19
|
-
contracts.select(&:before?).each do |contract|
|
20
|
-
contract.enforce! self unless halted
|
21
|
-
halted = true if contract.breached? && contract.halt?
|
22
|
-
end
|
18
|
+
halted = enforce_contracts!(contracts.select(&:before?))
|
23
19
|
super unless halted
|
24
20
|
ensure
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
21
|
+
unless halted
|
22
|
+
# enforce after-contracts in a separate thread to ensure that any perform related behavior
|
23
|
+
# defined in ContractablePrepends will finish executing before we invoke contract.enforce!
|
24
|
+
# important when multiple contracts have been applied
|
25
|
+
Thread.new do
|
26
|
+
sleep 0
|
27
|
+
synchronize { enforce_contracts! contracts.select(&:after?) }
|
33
28
|
end
|
34
29
|
end
|
35
30
|
end
|
@@ -75,5 +70,17 @@ module JobContracts
|
|
75
70
|
def contract_breached!
|
76
71
|
# noop / override in job subclasses
|
77
72
|
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def enforce_contracts!(contracts)
|
77
|
+
halted = false
|
78
|
+
contracts.each do |contract|
|
79
|
+
next if halted
|
80
|
+
contract.enforce! self
|
81
|
+
halted ||= contract.breached? && contract.halt?
|
82
|
+
end
|
83
|
+
halted
|
84
|
+
end
|
78
85
|
end
|
79
86
|
end
|
@@ -8,54 +8,32 @@ module JobContracts
|
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
include Contractable
|
10
10
|
|
11
|
-
class SidekiqJobMetadataNotFoundError < StandardError; end
|
12
|
-
|
13
11
|
module ClassMethods
|
12
|
+
# Matches the ActiveJob API
|
14
13
|
def queue_name
|
15
14
|
sidekiq_options_hash["queue"]
|
16
15
|
end
|
17
16
|
end
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
begin
|
23
|
-
attempts ||= 1
|
24
|
-
hit = Sidekiq::Workers.new.find do |_process_id, _thread_id, work|
|
25
|
-
work.dig("payload", "jid") == jid
|
26
|
-
end
|
27
|
-
raise SidekiqJobMetadataNotFoundError if hit.blank?
|
28
|
-
rescue SidekiqJobMetadataNotFoundError
|
29
|
-
# The WorkSet only updates every 5 seconds
|
30
|
-
# SEE: https://github.com/mperham/sidekiq/wiki/API#workers
|
31
|
-
# Re-attempt up to 10 times with a simple backoff strategy (up to 5.5 seconds)
|
32
|
-
# TODO: Is there a faster and more reliable way to fetch the job's metadata after perform has begun?
|
33
|
-
# May need to query Redis directly if the data is still in there at this point
|
34
|
-
attempts += 1
|
35
|
-
if attempts <= 10
|
36
|
-
sleep 0.1 * attempts
|
37
|
-
retry
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
hit&.last || {}
|
42
|
-
end
|
18
|
+
# Metadata used to enqueue the job
|
19
|
+
def sidekiq_job_hash
|
20
|
+
@sidekiq_job_hash ||= {}
|
43
21
|
end
|
44
22
|
|
45
23
|
# Matches the ActiveJob API
|
46
24
|
def queue_name
|
47
|
-
|
25
|
+
sidekiq_job_hash["queue"]
|
48
26
|
end
|
49
27
|
|
50
28
|
# Matches the ActiveJob API
|
51
29
|
def enqueued_at
|
52
|
-
seconds =
|
30
|
+
seconds = sidekiq_job_hash["enqueued_at"]
|
53
31
|
(seconds ? Time.at(seconds) : nil)&.iso8601.to_s
|
54
32
|
end
|
55
33
|
|
56
34
|
# Matches the ActiveJob API
|
57
35
|
def arguments
|
58
|
-
|
36
|
+
sidekiq_job_hash["args"] || []
|
59
37
|
end
|
60
38
|
end
|
61
39
|
end
|
@@ -1,6 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "sidekiq"
|
4
|
+
require_relative "sidekiq_job_hash_middleware"
|
5
|
+
|
3
6
|
module JobContracts
|
4
7
|
class Railtie < ::Rails::Railtie
|
8
|
+
initializer "job_contracts.register_sidekiq_middleware" do
|
9
|
+
Sidekiq.server_middleware.add SidekiqJobHashMiddleware
|
10
|
+
end
|
5
11
|
end
|
6
12
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: job_contracts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Hopkins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-05-
|
11
|
+
date: 2022-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 6.1.5
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 6.1.5
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: sidekiq
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 6.4.
|
33
|
+
version: 6.4.2
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 6.4.
|
40
|
+
version: 6.4.2
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: standard
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -126,6 +126,7 @@ files:
|
|
126
126
|
- lib/job_contracts/contracts/queue_name_contract.rb
|
127
127
|
- lib/job_contracts/contracts/read_only_contract.rb
|
128
128
|
- lib/job_contracts/railtie.rb
|
129
|
+
- lib/job_contracts/sidekiq_job_hash_middleware.rb
|
129
130
|
- lib/job_contracts/version.rb
|
130
131
|
homepage: https://github.com/hopsoft/job_contracts
|
131
132
|
licenses:
|