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