job_contracts 0.1.2 → 0.1.5

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: c8f52af511321598aed75762fb9b133a2f92af32365148881b9ab5834399ed64
4
- data.tar.gz: 8d1646b541bcbc1599b2d8a68d41d8b8e030c333d33acda8d1b5a473ce850a49
3
+ metadata.gz: d04fb7e6db54d4ed074bd176cd6a966f74ad964889c3970c542d921a1d605936
4
+ data.tar.gz: 3c3612c4dfb226a3155b0192c08e133b4610023f490f051e4cd12852df55ba45
5
5
  SHA512:
6
- metadata.gz: 4572323c6a8aa24eba91c36111fb8155f442d265eea89e041b36b6e66e3ddd1367acd1ba340cc79281b33d365d010127e993170b2cb7b54f8712004dce9e7ba4
7
- data.tar.gz: 66d24736ad86e8f787c2a3ea53f1c24b8d6527daa9b998f00efa4471196ebc85fee8bf01118821c3c2c9ac035c65a98f236867f44cea188f33407127dbb893be
6
+ metadata.gz: b56ddc45288e5b424e1844af3e4cde8d07fffa90a37c4236c8beef2f0654b27b13a760918fef9e338e69c3ffe1da0ea0978b7c195e02048ee3703295cd4f8dda
7
+ data.tar.gz: b560b5b6d179ca0d35f2b734600c265ec4c96526c18ca1f9df7d4d92a1f20bd7fa72b089a637a0807b416b43af64aeb31d9794d1a41912fcace75812b89b3079
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  [![Lines of Code](http://img.shields.io/badge/lines_of_code-236-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
2
2
  [![Code Quality](https://app.codacy.com/project/badge/Grade/f604d4bc6db0474c802ef51182732488)](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
- ![tests](https://github.com/hopsoft/job_contracts/actions/workflows/test.yml/badge.svg)
3
+ ![Tests](https://github.com/hopsoft/job_contracts/actions/workflows/test.yml/badge.svg)
4
+ [![Gem Version](https://badge.fury.io/rb/job_contracts.svg)](https://badge.fury.io/rb/job_contracts)
5
+ [![Gem Downloads](https://img.shields.io/gem/dt/job_contracts)](https://rubygems.org/gems/job_contracts)
6
+ [![Follow Hopsoft](https://img.shields.io/twitter/follow/hopsoft?style=social)](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 jobs/workers are supported.
241
- Unfortunately this support comes with a performance penalty *(i.e. additional latency)* because executing
242
- Sidekiq jobs don't have access to their own metadata. To get around this, we wait to find job metadata in the active
243
- [`WorkSet`](https://github.com/hopsoft/job_contracts/blob/main/lib/job_contracts/concerns/sidekiq_contractable.rb#L23-L25)
244
- which is only updated [every 5 seconds](https://github.com/mperham/sidekiq/wiki/API#workers).
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 = false
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
- # enforce after contracts in a separate thread to ensure that any perform related behavior
26
- # defined in ContractablePrepends will finish executing before we invoke contract.enforce!
27
- Thread.new do
28
- sleep 0
29
- synchronize do
30
- contracts.select(&:after?).each do |contract|
31
- contract.enforce! self unless halted
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
- def sidekiq_job_metadata
20
- @sidekiq_job_metadata ||= begin
21
- hit = nil
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
- sidekiq_job_metadata["queue"]
25
+ sidekiq_job_hash["queue"]
48
26
  end
49
27
 
50
28
  # Matches the ActiveJob API
51
29
  def enqueued_at
52
- seconds = sidekiq_job_metadata.dig("payload", "enqueued_at")
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
- sidekiq_job_metadata.dig("payload", "args") || []
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
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JobContracts
4
+ class SidekiqJobHashMiddleware
5
+ def call(worker, job, _queue)
6
+ worker.instance_variable_set :@sidekiq_job_hash, job
7
+ yield
8
+ end
9
+ end
10
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JobContracts
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.5"
5
5
  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.2
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-03 00:00:00.000000000 Z
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: 7.0.2.3
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: 7.0.2.3
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.1
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.1
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: