acidic_job 1.0.0.pre8 → 1.0.0.pre11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +49 -25
- data/lib/acidic_job/extensions/action_mailer.rb +12 -5
- data/lib/acidic_job/extensions/active_job.rb +8 -1
- data/lib/acidic_job/extensions/noticed.rb +11 -4
- data/lib/acidic_job/extensions/sidekiq.rb +8 -1
- data/lib/acidic_job/idempotency_key.rb +48 -6
- data/lib/acidic_job/staging.rb +10 -0
- data/lib/acidic_job/version.rb +1 -1
- data/lib/acidic_job.rb +27 -27
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53b5f12879bffb6a461e3574b96f8a41464ec83ce0a21eaa8e244261ce53a9be
|
4
|
+
data.tar.gz: 7b90c9864347564706466e62c61f08eb377d8e3a33475ad40ba559de59e7b70e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '08b4a0bdd41ac80f8ac879003f455f577f93fc431acdb780dfd3e54260b07a61bf0557c3e7d99192e1ad809134b709211fd2c5efe581b797563187dcfbb34467'
|
7
|
+
data.tar.gz: 66e8b3bfe0226c3e1f5168d45431a01a36ee3dfb796c855c49fc009296cb223d33922bc2db5febbe6d6453b4a01c46661b805aa1e2c80a4e37f3dce183b5e748
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -64,6 +64,7 @@ It provides a suite of functionality that empowers you to create complex, robust
|
|
64
64
|
* Transactional Steps — break your job into a series of steps, each of which will be run within an acidic database transaction, allowing retries to jump back to the last "recovery point".
|
65
65
|
* Persisted Attributes — when retrying jobs at later steps, we need to ensure that data created in previous steps is still available to later steps on retry.
|
66
66
|
* Transactionally Staged Jobs — enqueue additional jobs within the acidic transaction safely
|
67
|
+
* Custom Idempotency Keys — use something other than the job ID for the idempotency key of the job run
|
67
68
|
* Sidekiq Callbacks — bring ActiveJob-like callbacks into your pure Sidekiq Workers
|
68
69
|
* Sidekiq Batches — leverage the power of Sidekiq Pro's `batch` functionality without the hassle
|
69
70
|
|
@@ -78,7 +79,7 @@ class RideCreateJob < ActiveJob::Base
|
|
78
79
|
def perform(user_id, ride_params)
|
79
80
|
user = User.find(user_id)
|
80
81
|
|
81
|
-
with_acidity
|
82
|
+
with_acidity providing: { user: user, params: ride_params, ride: nil } do
|
82
83
|
step :create_ride_and_audit_record
|
83
84
|
step :create_stripe_charge
|
84
85
|
step :send_receipt
|
@@ -99,20 +100,20 @@ class RideCreateJob < ActiveJob::Base
|
|
99
100
|
end
|
100
101
|
```
|
101
102
|
|
102
|
-
`with_acidity` takes only the `
|
103
|
+
`with_acidity` takes only the `providing:` named parameter and a block where you define the steps of this operation. `step` simply takes the name of a method available in the job. That's all!
|
103
104
|
|
104
105
|
Now, each execution of this job will find or create an `AcidicJob::Run` record, which we leverage to wrap every step in a database transaction. Moreover, this database record allows `acidic_job` to ensure that if your job fails on step 3, when it retries, it will simply jump right back to trying to execute the method defined for the 3rd step, and won't even execute the first two step methods. This means your step methods only need to be idempotent on failure, not on success, since they will never be run again if they succeed.
|
105
106
|
|
106
107
|
### Persisted Attributes
|
107
108
|
|
108
|
-
Any objects passed to the `
|
109
|
+
Any objects passed to the `providing` option on the `with_acidity` method are not just made available to each of your step methods, they are made available across retries. This means that you can set an attribute in step 1, access it in step 2, have step 2 fail, have the job retry, jump directly back to step 2 on retry, and have that object still accessible. This is done by serializing all objects to a field on the `AcidicJob::Run` and manually providing getters and setters that sync with the database record.
|
109
110
|
|
110
111
|
```ruby
|
111
112
|
class RideCreateJob < ActiveJob::Base
|
112
113
|
include AcidicJob
|
113
114
|
|
114
115
|
def perform(ride_params)
|
115
|
-
with_acidity
|
116
|
+
with_acidity providing: { ride: nil } do
|
116
117
|
step :create_ride_and_audit_record
|
117
118
|
step :create_stripe_charge
|
118
119
|
step :send_receipt
|
@@ -148,7 +149,7 @@ class RideCreateJob < ActiveJob::Base
|
|
148
149
|
def perform(user_id, ride_params)
|
149
150
|
user = User.find(user_id)
|
150
151
|
|
151
|
-
with_acidity
|
152
|
+
with_acidity providing: { user: user, params: ride_params, ride: nil } do
|
152
153
|
step :create_ride_and_audit_record
|
153
154
|
step :create_stripe_charge
|
154
155
|
step :send_receipt
|
@@ -163,6 +164,47 @@ class RideCreateJob < ActiveJob::Base
|
|
163
164
|
end
|
164
165
|
```
|
165
166
|
|
167
|
+
### Custom Idempotency Keys
|
168
|
+
|
169
|
+
By default, `AcidicJob` uses the job identifier provided by the queueing system (ActiveJob or Sidekiq) as the idempotency key for the job run. The idempotency key is what is used to guarantee that no two runs of the same job occur. However, sometimes we need particular jobs to be idempotent based on some other criteria. In these cases, `AcidicJob` provides a collection of tools to allow you to ensure the idempotency of your jobs.
|
170
|
+
|
171
|
+
Firstly, you can configure your job class to explicitly use either the job identifier or the job arguments as the foundation for the idempotency key. A job class that calls the `acidic_by_job_id` class method (which is the default behavior) will simply make the job run's idempotency key the job's identifier:
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
class ExampleJob < ActiveJob::Base
|
175
|
+
include AcidicJob
|
176
|
+
acidic_by_job_id
|
177
|
+
|
178
|
+
def perform
|
179
|
+
end
|
180
|
+
end
|
181
|
+
```
|
182
|
+
|
183
|
+
Conversely, a job class can use the `acidic_by_job_args` method to configure that job class to use the arguments passed to the job as the foundation for the job run's idempotency key:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
class ExampleJob < ActiveJob::Base
|
187
|
+
include AcidicJob
|
188
|
+
acidic_by_job_args
|
189
|
+
|
190
|
+
def perform(arg_1, arg_2)
|
191
|
+
# the idempotency key will be based on whatever the values of `arg_1` and `arg_2` are
|
192
|
+
end
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
196
|
+
These options cover the two common situations, but sometimes our systems need finer-grained control. For example, our job might take some record as the job argument, but we need to use a combination of the record identifier and record status as the foundation for the idempotency key. In these cases we can't configure the idempotency key logic at the class level, so instead we can provide the logic when enqueuing the job itself.
|
197
|
+
|
198
|
+
When you call any `deliver_acidicly` or `perform_acidicly` method you can pass an optional `unique_by` argument which will be used to generate the idempotency key:
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
ExampleJob.perform_acidicly(unique_by: { id: record.id, status: record.status })
|
202
|
+
UserMailer.with(user, record).deliver_acidicly(unique_by: [user.id, record.id, record.status])
|
203
|
+
```
|
204
|
+
|
205
|
+
As you see, the value to the `unique_by` option can be a Hash or an Array or even a simple scalar value.
|
206
|
+
|
207
|
+
|
166
208
|
### Sidekiq Callbacks
|
167
209
|
|
168
210
|
In order to ensure that `AcidicJob::Staged` records are only destroyed once the related job has been successfully performed, whether it is an ActiveJob or a Sidekiq Worker, `acidic_job` also extends Sidekiq to support the [ActiveJob callback interface](https://edgeguides.rubyonrails.org/active_job_basics.html#callbacks).
|
@@ -182,7 +224,7 @@ class RideCreateJob < ActiveJob::Base
|
|
182
224
|
def perform(user_id, ride_params)
|
183
225
|
user = User.find(user_id)
|
184
226
|
|
185
|
-
with_acidity
|
227
|
+
with_acidity providing: { user: user, params: ride_params, ride: nil } do
|
186
228
|
step :create_ride_and_audit_record, awaits: [SomeJob]
|
187
229
|
step :create_stripe_charge, args: [1, 2, 3], kwargs: { some: 'thing' }
|
188
230
|
step :send_receipt
|
@@ -203,25 +245,7 @@ This error is thrown because by default RSpec and most MiniTest test suites use
|
|
203
245
|
|
204
246
|
In order to avoid this error, you need to ensure firstly that your tests that run your acidic jobs are not using a database transaction and secondly that they use some different strategy to keep your test database clean. The [DatabaseCleaner](https://github.com/DatabaseCleaner/database_cleaner) gem is a commonly used tool to manage different strategies for keeping your test database clean. As for which strategy to use, `truncation` and `deletion` are both safe, but their speed varies based on our app's table structure (see https://github.com/DatabaseCleaner/database_cleaner#what-strategy-is-fastest). Either is fine; use whichever is faster for your app.
|
205
247
|
|
206
|
-
In order to make this test setup simpler, `AcidicJob` provides a `TestCase` class that your MiniTest jobs tests can inherit from. It is simple; it inherits from `ActiveJob::TestCase`, sets `use_transactional_tests` to `false`, and ensures `DatabaseCleaner` is run for each of your tests
|
207
|
-
|
208
|
-
```ruby
|
209
|
-
class AcidicJob::TestCase < ActiveJob::TestCase
|
210
|
-
self.use_transactional_tests = false
|
211
|
-
|
212
|
-
def before_setup
|
213
|
-
super
|
214
|
-
DatabaseCleaner.start
|
215
|
-
end
|
216
|
-
|
217
|
-
def after_teardown
|
218
|
-
DatabaseCleaner.clean
|
219
|
-
super
|
220
|
-
end
|
221
|
-
|
222
|
-
# ...
|
223
|
-
end
|
224
|
-
```
|
248
|
+
In order to make this test setup simpler, `AcidicJob` provides a `TestCase` class that your MiniTest jobs tests can inherit from. It is simple; it inherits from `ActiveJob::TestCase`, sets `use_transactional_tests` to `false`, and ensures `DatabaseCleaner` is run for each of your tests. Moreover, it ensures that the system's original DatabaseCleaner configuration is maintained, options included, except that any `transaction` strategies for any ORMs are replaced with a `deletion` strategy. It does so by storing whatever the system DatabaseCleaner configuration is at the start of `before_setup` phase in an instance variable and then restores that configuration at the end of `after_teardown` phase. In between, it runs the configuration thru a pipeline that selectively replaces any `transaction` strategies with a corresponding `deletion` strategy, leaving any other configured strategies untouched.
|
225
249
|
|
226
250
|
For those of you using RSpec, you can require the `acidic_job/rspec_configuration` file, which will configure RSpec in the exact same way I have used in my RSpec projects to allow me to test acidic jobs with either the `deletion` strategy but still have all of my other tests use the fast `transaction` strategy:
|
227
251
|
|
@@ -7,18 +7,25 @@ module AcidicJob
|
|
7
7
|
module ActionMailer
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
|
-
def deliver_acidicly(_options = {})
|
11
|
-
|
10
|
+
def deliver_acidicly(_options = {}, unique_by: nil)
|
11
|
+
job_class = ::ActionMailer::MailDeliveryJob
|
12
12
|
|
13
13
|
job_args = [@mailer_class.name, @action.to_s, "deliver_now", @params, *@args]
|
14
14
|
# for Sidekiq, this depends on the Sidekiq::Serialization extension
|
15
|
-
serialized_job =
|
15
|
+
serialized_job = job_class.new(job_args).serialize
|
16
|
+
acidic_identifier = job_class.respond_to?(:acidic_identifier) ? job_class.acidic_identifier : :job_id
|
17
|
+
# use either [1] provided uniqueness constraint or [2] computed key
|
18
|
+
key = if unique_by
|
19
|
+
IdempotencyKey.generate(unique_by: unique_by, job_class: job_class.name)
|
20
|
+
else
|
21
|
+
IdempotencyKey.new(acidic_identifier).value_for(serialized_job)
|
22
|
+
end
|
16
23
|
|
17
24
|
AcidicJob::Run.create!(
|
18
25
|
staged: true,
|
19
|
-
job_class:
|
26
|
+
job_class: job_class.name,
|
20
27
|
serialized_job: serialized_job,
|
21
|
-
idempotency_key:
|
28
|
+
idempotency_key: key
|
22
29
|
)
|
23
30
|
end
|
24
31
|
alias deliver_transactionally deliver_acidicly
|
@@ -24,12 +24,19 @@ module AcidicJob
|
|
24
24
|
raise UnsupportedExtension unless defined?(::ActiveJob) && self < ::ActiveJob::Base
|
25
25
|
|
26
26
|
serialized_job = serialize_with_arguments(*args, **kwargs)
|
27
|
+
# use either [1] provided uniqueness constraint or [2] computed key
|
28
|
+
key = if kwargs.key?(:unique_by) || kwargs.key?("unique_by")
|
29
|
+
unique_by = [kwargs[:unique_by], kwargs["unique_by"]].compact.first
|
30
|
+
IdempotencyKey.generate(unique_by: unique_by, job_class: name)
|
31
|
+
else
|
32
|
+
IdempotencyKey.new(acidic_identifier).value_for(serialized_job)
|
33
|
+
end
|
27
34
|
|
28
35
|
AcidicJob::Run.create!(
|
29
36
|
staged: true,
|
30
37
|
job_class: name,
|
31
38
|
serialized_job: serialized_job,
|
32
|
-
idempotency_key:
|
39
|
+
idempotency_key: key
|
33
40
|
)
|
34
41
|
end
|
35
42
|
alias_method :perform_transactionally, :perform_acidicly
|
@@ -6,12 +6,12 @@ module AcidicJob
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
class_methods do
|
9
|
-
def deliver_acidicly(recipients)
|
10
|
-
new.deliver_acidicly(recipients)
|
9
|
+
def deliver_acidicly(recipients, unique_by: nil)
|
10
|
+
new.deliver_acidicly(recipients, unique_by: unique_by)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
def deliver_acidicly(recipients)
|
14
|
+
def deliver_acidicly(recipients, unique_by: nil)
|
15
15
|
# THIS IS A HACK THAT COPIES AND PASTES KEY PARTS OF THE `Noticed::Base` CODE
|
16
16
|
# IN ORDER TO ALLOW US TO TRANSACTIONALLY DELIVER NOTIFICATIONS
|
17
17
|
# THIS IS THUS LIABLE TO BREAK WHENEVER THAT GEM IS UPDATED
|
@@ -36,12 +36,19 @@ module AcidicJob
|
|
36
36
|
record: record
|
37
37
|
}
|
38
38
|
serialized_job = job_class.send(:job_or_instantiate, args).serialize
|
39
|
+
acidic_identifier = job_class.respond_to?(:acidic_identifier) ? job_class.acidic_identifier : :job_id
|
40
|
+
# use either [1] provided uniqueness constraint or [2] computed key
|
41
|
+
key = if unique_by
|
42
|
+
IdempotencyKey.generate(unique_by: unique_by, job_class: job_class.name)
|
43
|
+
else
|
44
|
+
IdempotencyKey.new(acidic_identifier).value_for(serialized_job)
|
45
|
+
end
|
39
46
|
|
40
47
|
AcidicJob::Run.create!(
|
41
48
|
staged: true,
|
42
49
|
job_class: job_class.name,
|
43
50
|
serialized_job: serialized_job,
|
44
|
-
idempotency_key:
|
51
|
+
idempotency_key: key
|
45
52
|
)
|
46
53
|
end
|
47
54
|
end
|
@@ -58,12 +58,19 @@ module AcidicJob
|
|
58
58
|
class_methods do
|
59
59
|
def perform_acidicly(*args, **kwargs)
|
60
60
|
serialized_job = serialize_with_arguments(*args, **kwargs)
|
61
|
+
# use either [1] provided uniqueness constraint or [2] computed key
|
62
|
+
key = if kwargs.key?(:unique_by) || kwargs.key?("unique_by")
|
63
|
+
unique_by = [kwargs[:unique_by], kwargs["unique_by"]].compact.first
|
64
|
+
IdempotencyKey.generate(unique_by: unique_by, job_class: name)
|
65
|
+
else
|
66
|
+
IdempotencyKey.new(acidic_identifier).value_for(serialized_job)
|
67
|
+
end
|
61
68
|
|
62
69
|
AcidicJob::Run.create!(
|
63
70
|
staged: true,
|
64
71
|
job_class: name,
|
65
72
|
serialized_job: serialized_job,
|
66
|
-
idempotency_key:
|
73
|
+
idempotency_key: key
|
67
74
|
)
|
68
75
|
end
|
69
76
|
alias_method :perform_transactionally, :perform_acidicly
|
@@ -2,15 +2,57 @@
|
|
2
2
|
|
3
3
|
module AcidicJob
|
4
4
|
class IdempotencyKey
|
5
|
-
def self.
|
6
|
-
|
7
|
-
|
5
|
+
def self.generate(unique_by:, job_class:)
|
6
|
+
new(:job_args).value_for({ "job_class" => job_class }, Marshal.dump(unique_by))
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(identifier = :job_id)
|
10
|
+
@identifier = identifier
|
11
|
+
end
|
12
|
+
|
13
|
+
def value_for(hash_or_job, *args, **kwargs)
|
14
|
+
return value_from_job_args(hash_or_job, *args, **kwargs) if @identifier == :job_args
|
15
|
+
|
16
|
+
value = if hash_or_job.is_a?(Hash)
|
17
|
+
value_from_job_id_for_hash(hash_or_job)
|
18
|
+
else
|
19
|
+
value_from_job_id_for_obj(hash_or_job)
|
20
|
+
end
|
21
|
+
|
22
|
+
value || value_from_job_args(hash_or_job, *args, **kwargs)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
8
26
|
|
9
|
-
|
10
|
-
|
27
|
+
def value_from_job_id_for_hash(hash)
|
28
|
+
if hash.key?("job_id")
|
29
|
+
return if hash["job_id"].nil?
|
30
|
+
return if hash["job_id"].empty?
|
31
|
+
|
32
|
+
hash["job_id"]
|
33
|
+
elsif hash.key?("jid")
|
34
|
+
return if hash["jid"].nil?
|
35
|
+
return if hash["jid"].empty?
|
36
|
+
|
37
|
+
hash["jid"]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def value_from_job_id_for_obj(obj)
|
42
|
+
if obj.respond_to?(:job_id)
|
43
|
+
return if obj.job_id.nil?
|
44
|
+
return if obj.job_id.empty?
|
45
|
+
|
46
|
+
obj.job_id
|
47
|
+
elsif obj.respond_to?(:jid)
|
48
|
+
return if obj.jid.nil?
|
49
|
+
return if obj.jid.empty?
|
50
|
+
|
51
|
+
obj.jid
|
11
52
|
end
|
12
|
-
|
53
|
+
end
|
13
54
|
|
55
|
+
def value_from_job_args(hash_or_job, *args, **kwargs)
|
14
56
|
worker_class = case hash_or_job
|
15
57
|
when Hash
|
16
58
|
hash_or_job["worker"] || hash_or_job["job_class"]
|
data/lib/acidic_job/staging.rb
CHANGED
@@ -26,5 +26,15 @@ module AcidicJob
|
|
26
26
|
|
27
27
|
GlobalID::Locator.locate(staged_job_gid)
|
28
28
|
end
|
29
|
+
|
30
|
+
def identifier
|
31
|
+
return jid if defined?(jid) && !jid.nil?
|
32
|
+
return job_id if defined?(job_id) && !job_id.nil?
|
33
|
+
|
34
|
+
# might be defined already in `with_acidity` method
|
35
|
+
@__acidic_job_idempotency_key ||= IdempotencyKey.value_for(self, @__acidic_job_args, @__acidic_job_kwargs)
|
36
|
+
|
37
|
+
@__acidic_job_idempotency_key
|
38
|
+
end
|
29
39
|
end
|
30
40
|
end
|
data/lib/acidic_job/version.rb
CHANGED
data/lib/acidic_job.rb
CHANGED
@@ -46,6 +46,10 @@ module AcidicJob
|
|
46
46
|
end
|
47
47
|
|
48
48
|
klass.set_callback :perform, :after, :delete_staged_job_record, if: :was_staged_job?
|
49
|
+
|
50
|
+
klass.instance_variable_set(:@acidic_identifier, :job_id)
|
51
|
+
klass.define_singleton_method(:acidic_by_job_id) { @acidic_identifier = :job_id }
|
52
|
+
klass.define_singleton_method(:acidic_by_job_args) { @acidic_identifier = :job_args }
|
49
53
|
end
|
50
54
|
|
51
55
|
included do
|
@@ -57,34 +61,35 @@ module AcidicJob
|
|
57
61
|
AcidicJob.wire_everything_up(subclass)
|
58
62
|
super
|
59
63
|
end
|
64
|
+
|
65
|
+
def acidic_identifier
|
66
|
+
@acidic_identifier
|
67
|
+
end
|
60
68
|
end
|
61
69
|
|
62
|
-
def with_acidity(
|
63
|
-
#
|
70
|
+
def with_acidity(providing: {})
|
71
|
+
# ensure this instance variable is always defined
|
64
72
|
@__acidic_job_steps = []
|
65
|
-
steps
|
73
|
+
# execute the block to gather the info on what steps are defined for this job workflow
|
74
|
+
yield
|
66
75
|
|
67
76
|
# check that the block actually defined at least one step
|
68
77
|
# TODO: WRITE TESTS FOR FAULTY BLOCK VALUES
|
69
78
|
raise NoDefinedSteps if @__acidic_job_steps.nil? || @__acidic_job_steps.empty?
|
70
79
|
|
71
80
|
# convert the array of steps into a hash of recovery_points and next steps
|
72
|
-
workflow = define_workflow(
|
73
|
-
|
74
|
-
# determine the idempotency key value for this job run (`job_id` or `jid`)
|
75
|
-
# might be defined already in `identifier` method
|
76
|
-
# TODO: allow idempotency to be defined by args OR job id
|
77
|
-
@__acidic_job_idempotency_key ||= IdempotencyKey.value_for(self, @__acidic_job_args, @__acidic_job_kwargs)
|
81
|
+
workflow = define_workflow(@__acidic_job_steps)
|
78
82
|
|
79
|
-
@run = ensure_run_record(
|
83
|
+
@run = ensure_run_record(workflow, providing)
|
80
84
|
|
81
85
|
# begin the workflow
|
82
86
|
process_run(@run)
|
83
87
|
end
|
84
88
|
|
85
89
|
# DEPRECATED
|
86
|
-
def idempotently(with
|
87
|
-
|
90
|
+
def idempotently(with: {}, &blk)
|
91
|
+
ActiveSupport::Deprecation.new("1.0", "AcidicJob").deprecation_warning(:idempotently)
|
92
|
+
with_acidity(providing: with, &blk)
|
88
93
|
end
|
89
94
|
|
90
95
|
def safely_finish_acidic_job
|
@@ -93,11 +98,16 @@ module AcidicJob
|
|
93
98
|
FinishedPoint.new
|
94
99
|
end
|
95
100
|
|
101
|
+
# TODO: allow idempotency to be defined by args OR job id
|
96
102
|
# rubocop:disable Naming/MemoizedInstanceVariableName
|
97
103
|
def idempotency_key
|
98
|
-
|
104
|
+
if defined?(@__acidic_job_idempotency_key) && !@__acidic_job_idempotency_key.nil?
|
105
|
+
return @__acidic_job_idempotency_key
|
106
|
+
end
|
99
107
|
|
100
|
-
|
108
|
+
acidic_identifier = self.class.acidic_identifier
|
109
|
+
@__acidic_job_idempotency_key ||= IdempotencyKey.new(acidic_identifier)
|
110
|
+
.value_for(self, @__acidic_job_args, @__acidic_job_kwargs)
|
101
111
|
end
|
102
112
|
# rubocop:enable Naming/MemoizedInstanceVariableName
|
103
113
|
|
@@ -171,7 +181,7 @@ module AcidicJob
|
|
171
181
|
# { "step 1": { does: "step 1", awaits: [], then: "step 2" }, ... }
|
172
182
|
end
|
173
183
|
|
174
|
-
def ensure_run_record(
|
184
|
+
def ensure_run_record(workflow, accessors)
|
175
185
|
isolation_level = case ActiveRecord::Base.connection.adapter_name.downcase.to_sym
|
176
186
|
when :sqlite
|
177
187
|
:read_uncommitted
|
@@ -180,7 +190,7 @@ module AcidicJob
|
|
180
190
|
end
|
181
191
|
|
182
192
|
ActiveRecord::Base.transaction(isolation: isolation_level) do
|
183
|
-
run = Run.find_by(idempotency_key:
|
193
|
+
run = Run.find_by(idempotency_key: idempotency_key)
|
184
194
|
serialized_job = serialize_job(@__acidic_job_args, @__acidic_job_kwargs)
|
185
195
|
|
186
196
|
if run.present?
|
@@ -201,7 +211,7 @@ module AcidicJob
|
|
201
211
|
else
|
202
212
|
run = Run.create!(
|
203
213
|
staged: false,
|
204
|
-
idempotency_key:
|
214
|
+
idempotency_key: idempotency_key,
|
205
215
|
job_class: self.class.name,
|
206
216
|
locked_at: Time.current,
|
207
217
|
last_run_at: Time.current,
|
@@ -246,14 +256,4 @@ module AcidicJob
|
|
246
256
|
|
247
257
|
true
|
248
258
|
end
|
249
|
-
|
250
|
-
def identifier
|
251
|
-
return jid if defined?(jid) && !jid.nil?
|
252
|
-
return job_id if defined?(job_id) && !job_id.nil?
|
253
|
-
|
254
|
-
# might be defined already in `with_acidity` method
|
255
|
-
@__acidic_job_idempotency_key ||= IdempotencyKey.value_for(self, @__acidic_job_args, @__acidic_job_kwargs)
|
256
|
-
|
257
|
-
@__acidic_job_idempotency_key
|
258
|
-
end
|
259
259
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acidic_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.pre11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- fractaledmind
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-03-
|
11
|
+
date: 2022-03-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|