acidic_job 0.8.2 → 0.8.5
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 +12 -11
- data/lib/acidic_job/active_kiq.rb +37 -8
- data/lib/acidic_job/configured_job.rb +11 -0
- data/lib/acidic_job/mixin.rb +17 -14
- data/lib/acidic_job/perform_acidicly.rb +23 -0
- data/lib/acidic_job/rails.rb +4 -1
- data/lib/acidic_job/serializers/active_kiq_serializer.rb +25 -0
- data/lib/acidic_job/serializers/exception_serializer.rb +1 -1
- data/lib/acidic_job/serializers/job_serializer.rb +1 -1
- data/lib/acidic_job/serializers/worker_serializer.rb +4 -6
- data/lib/acidic_job/version.rb +1 -1
- data/lib/acidic_job.rb +3 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54586a38aa48a14ef168657659a81b0db14446d011bd76085641dd4fb21f7838
|
4
|
+
data.tar.gz: 849af5f8e34684cd22d454f85ac816e749c3a4dd91ba58dba34ce67931916d3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe54d708708a8316020979400dda7afd73480d34597f9416b30218b7307d8e4ce3cfbf65e14e05681810de88e21d131f06f4a3f72549a7dedef62be209b3137d
|
7
|
+
data.tar.gz: 58ba97af8e30c9bff0df8cf4015a3920ce722a5242048469b2224a4d9ca32c67971f03792862c2548515adc33b8407309c386ab1ff1dd509735f420029c11085
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# AcidicJob
|
2
2
|
|
3
|
-
[![Gem Version](https://badge.fury.io/rb/acidic_job.svg)](https://
|
4
|
-
![
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/acidic_job.svg)](https://rubygems.org/gems/acidic_job)
|
4
|
+
[![Gem Downloads](https://img.shields.io/gem/dt/acidic_job)](https://rubygems.org/gems/acidic_job)
|
5
|
+
![Tests](https://github.com/fractaledmind/acidic_job/actions/workflows/main.yml/badge.svg)
|
6
|
+
![Coverage](https://img.shields.io/badge/code%20coverage-98%25-success)
|
7
|
+
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/e0df63f7a6f141d4aecc3c477314fdb2)](https://www.codacy.com/gh/fractaledmind/acidic_job/dashboard?utm_source=github.com&utm_medium=referral&utm_content=fractaledmind/acidic_job&utm_campaign=Badge_Grade)
|
5
8
|
|
6
9
|
## Idempotent operations for Rails apps (for ActiveJob or Sidekiq)
|
7
10
|
|
@@ -306,24 +309,22 @@ class ExampleJob < AcidicJob::Base
|
|
306
309
|
end
|
307
310
|
```
|
308
311
|
|
309
|
-
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 you can pass a `Proc` to an `acidic_by` class method:
|
312
|
+
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 you can pass a `Proc` or a `Block` to an `acidic_by` class method. This code will be executed in the context of the newly initialized job instance, so you will have access to whatever data the job is initialized with (like the `arguments`, for example):
|
310
313
|
|
311
314
|
```ruby
|
312
315
|
class ExampleJob < AcidicJob::Base
|
313
|
-
acidic_by
|
316
|
+
acidic_by do
|
317
|
+
record = arguments.first[:record]
|
318
|
+
[record.id, record.status]
|
319
|
+
end
|
314
320
|
|
315
321
|
def perform(record:)
|
316
|
-
|
317
|
-
|
318
|
-
# the idempotency key will be based on whatever the values of `@record.id` and `@record.status` are
|
319
|
-
with_acidic_workflow do |workflow|
|
320
|
-
workflow.step :do_something
|
321
|
-
end
|
322
|
+
# ...
|
322
323
|
end
|
323
324
|
end
|
324
325
|
```
|
325
326
|
|
326
|
-
> **Note:** The `acidic_by` proc _will be executed in the context of the job instance_ at the moment the
|
327
|
+
> **Note:** The `acidic_by` proc/block _will be executed in the context of the job instance_ at the moment the job is initialized. This means it will **not** have access to any instance variables defined in your `perform` method.
|
327
328
|
|
328
329
|
|
329
330
|
### Sidekiq Callbacks
|
@@ -13,11 +13,29 @@ module AcidicJob
|
|
13
13
|
define_callbacks :perform
|
14
14
|
include Mixin
|
15
15
|
|
16
|
+
concerning :Configuring do
|
17
|
+
# Configures the job with the given options.
|
18
|
+
def set(options = {}) # :nodoc:
|
19
|
+
self.scheduled_at = options[:wait].seconds.from_now.to_f if options[:wait]
|
20
|
+
self.scheduled_at = options[:wait_until].to_f if options[:wait_until]
|
21
|
+
self.queue_name = self.class.queue_name_from_part(options[:queue]) if options[:queue]
|
22
|
+
|
23
|
+
self
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
16
27
|
concerning :Initializing do
|
28
|
+
class_methods do
|
29
|
+
def job_or_instantiate(*args)
|
30
|
+
args.first.is_a?(self) ? args.first : new(*args)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
17
34
|
included do
|
18
35
|
attr_accessor :arguments
|
19
36
|
attr_accessor :job_id
|
20
37
|
attr_accessor :queue_name
|
38
|
+
attr_accessor :scheduled_at
|
21
39
|
attr_accessor :sidekiq_options
|
22
40
|
end
|
23
41
|
##
|
@@ -25,8 +43,8 @@ module AcidicJob
|
|
25
43
|
# +args+ are the arguments, if any, that will be passed to the perform method
|
26
44
|
# +opts+ are any options to configure the job
|
27
45
|
def initialize(*arguments)
|
28
|
-
@arguments
|
29
|
-
@job_id
|
46
|
+
@arguments = arguments
|
47
|
+
@job_id = ::SecureRandom.uuid
|
30
48
|
@sidekiq_options = sidekiq_options_hash || ::Sidekiq.default_job_options
|
31
49
|
@queue_name = @sidekiq_options["queue"]
|
32
50
|
end
|
@@ -43,22 +61,33 @@ module AcidicJob
|
|
43
61
|
|
44
62
|
concerning :Performing do
|
45
63
|
class_methods do
|
46
|
-
def
|
47
|
-
|
64
|
+
def perform_later(*args)
|
65
|
+
perform_async(*args)
|
66
|
+
end
|
67
|
+
|
68
|
+
def perform_now(*args)
|
69
|
+
perform_inline(*args)
|
48
70
|
end
|
49
71
|
end
|
50
72
|
|
51
|
-
def
|
52
|
-
|
73
|
+
def perform_later(*args)
|
74
|
+
Setter.new(self.class, {}).perform_async(*args)
|
75
|
+
end
|
76
|
+
|
77
|
+
def perform_now(*args)
|
78
|
+
Setter.new(self.class, {}).perform_inline(*args)
|
53
79
|
end
|
54
80
|
|
55
81
|
def enqueue
|
56
|
-
|
82
|
+
item = {
|
57
83
|
"class" => self.class,
|
58
84
|
"args" => @arguments,
|
59
85
|
"jid" => @job_id,
|
60
86
|
"queue" => @queue_name
|
61
|
-
|
87
|
+
}
|
88
|
+
item["at"] = @scheduled_at if @scheduled_at
|
89
|
+
|
90
|
+
::Sidekiq::Client.push(item)
|
62
91
|
end
|
63
92
|
end
|
64
93
|
|
data/lib/acidic_job/mixin.rb
CHANGED
@@ -10,6 +10,8 @@ module AcidicJob
|
|
10
10
|
# Ensure our `perform` method always runs first to gather parameters
|
11
11
|
# and run perform callbacks for Sidekiq workers
|
12
12
|
other.prepend PerformWrapper
|
13
|
+
# Ensure both configured and base jobs can be performed acidicly
|
14
|
+
other.include PerformAcidicly
|
13
15
|
|
14
16
|
# By default, we unique job runs by the `job_id`
|
15
17
|
other.instance_variable_set(:@acidic_identifier, :job_id)
|
@@ -18,7 +20,7 @@ module AcidicJob
|
|
18
20
|
# You could unique job runs by the arguments passed to the job (e.g. memoization)
|
19
21
|
other.define_singleton_method(:acidic_by_job_arguments) { @acidic_identifier = :job_arguments }
|
20
22
|
# Or, you could unique jobs run by any logic you'd like using a block
|
21
|
-
other.define_singleton_method(:acidic_by) {
|
23
|
+
other.define_singleton_method(:acidic_by) { |proc = nil, &block| @acidic_identifier = proc || block }
|
22
24
|
|
23
25
|
# We add a callback to ensure that staged, non-workflow jobs are "finished" after they are "performed".
|
24
26
|
# This allows us to ensure that we can always inspect whether a run is finished and get correct data
|
@@ -40,19 +42,6 @@ module AcidicJob
|
|
40
42
|
super
|
41
43
|
end
|
42
44
|
|
43
|
-
# `perform_now` runs a job synchronously and immediately
|
44
|
-
# `perform_later` runs a job asynchronously and queues it immediately
|
45
|
-
# `perform_acidicly` run a job asynchronously and queues it after a successful database commit
|
46
|
-
def perform_acidicly(*args, **kwargs)
|
47
|
-
job = if kwargs.empty?
|
48
|
-
new(*args)
|
49
|
-
else
|
50
|
-
new(*args, **kwargs)
|
51
|
-
end
|
52
|
-
|
53
|
-
Run.stage!(job)
|
54
|
-
end
|
55
|
-
|
56
45
|
# Instantiate an instance of a job ready for serialization
|
57
46
|
def with(...)
|
58
47
|
# New delegation syntax (...) was introduced in Ruby 2.7.
|
@@ -62,12 +51,26 @@ module AcidicJob
|
|
62
51
|
job.queue_name
|
63
52
|
job
|
64
53
|
end
|
54
|
+
|
55
|
+
def set(options = {})
|
56
|
+
::AcidicJob::ConfiguredJob.new(self, options)
|
57
|
+
end
|
65
58
|
end
|
66
59
|
|
67
60
|
def idempotency_key
|
68
61
|
IdempotencyKey.new(self).value(acidic_by: acidic_identifier)
|
69
62
|
end
|
70
63
|
|
64
|
+
# Configures the job with the given options.
|
65
|
+
def set(options = {}) # :nodoc:
|
66
|
+
self.scheduled_at = options[:wait].seconds.from_now.to_f if options[:wait]
|
67
|
+
self.scheduled_at = options[:wait_until].to_f if options[:wait_until]
|
68
|
+
self.queue_name = self.class.queue_name_from_part(options[:queue]) if options[:queue]
|
69
|
+
self.priority = options[:priority].to_i if options[:priority]
|
70
|
+
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
71
74
|
protected
|
72
75
|
|
73
76
|
# Short circuits execution by sending execution right to 'finished'.
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/concern"
|
4
|
+
|
5
|
+
module AcidicJob
|
6
|
+
module PerformAcidicly
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
# `perform_now` runs a job synchronously and immediately
|
10
|
+
# `perform_later` runs a job asynchronously and queues it immediately
|
11
|
+
# `perform_acidicly` run a job asynchronously and queues it after a successful database commit
|
12
|
+
|
13
|
+
class_methods do
|
14
|
+
def perform_acidicly(...)
|
15
|
+
job_or_instantiate(...).perform_acidicly
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def perform_acidicly
|
20
|
+
Run.stage!(self)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/acidic_job/rails.rb
CHANGED
@@ -20,14 +20,17 @@ module AcidicJob
|
|
20
20
|
Serializers::JobSerializer,
|
21
21
|
Serializers::RangeSerializer,
|
22
22
|
Serializers::RecoveryPointSerializer,
|
23
|
-
Serializers::WorkerSerializer
|
23
|
+
Serializers::WorkerSerializer,
|
24
|
+
Serializers::ActiveKiqSerializer
|
24
25
|
)
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
29
|
+
# :nocov:
|
28
30
|
generators do
|
29
31
|
require "generators/acidic_job/install_generator"
|
30
32
|
end
|
33
|
+
# :nocov:
|
31
34
|
|
32
35
|
# This hook happens after all initializers are run, just before returning
|
33
36
|
config.after_initialize do
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_job/serializers/object_serializer"
|
4
|
+
|
5
|
+
module AcidicJob
|
6
|
+
module Serializers
|
7
|
+
class ActiveKiqSerializer < ::ActiveJob::Serializers::ObjectSerializer
|
8
|
+
def serialize(worker)
|
9
|
+
super(
|
10
|
+
"job_class" => worker.class.name,
|
11
|
+
"arguments" => Arguments.serialize(worker.arguments),
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def deserialize(hash)
|
16
|
+
worker_class = hash["job_class"].constantize
|
17
|
+
worker_class.new(*hash["arguments"])
|
18
|
+
end
|
19
|
+
|
20
|
+
def serialize?(argument)
|
21
|
+
defined?(::AcidicJob::ActiveKiq) && argument.class < ::AcidicJob::ActiveKiq
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -10,7 +10,7 @@ module AcidicJob
|
|
10
10
|
# by comparing the deserialized database value with a temporary in-memory generated value.
|
11
11
|
# That temporary in-memory generated value can sometimes have an `enqueued_at` value that is 1 second off
|
12
12
|
# from the original. In this case, ActiveRecord will think the record has unsaved changes and block the lock.
|
13
|
-
super(job.
|
13
|
+
super(job.serialize.except("enqueued_at"))
|
14
14
|
end
|
15
15
|
|
16
16
|
def deserialize(hash)
|
@@ -2,26 +2,24 @@
|
|
2
2
|
|
3
3
|
require "active_job/serializers/object_serializer"
|
4
4
|
|
5
|
-
# :nocov:
|
6
5
|
module AcidicJob
|
7
6
|
module Serializers
|
8
7
|
class WorkerSerializer < ::ActiveJob::Serializers::ObjectSerializer
|
9
8
|
def serialize(worker)
|
10
9
|
super(
|
11
|
-
"job_class" => worker.class.name
|
12
|
-
"arguments" => worker.arguments,
|
10
|
+
"job_class" => worker.class.name
|
13
11
|
)
|
14
12
|
end
|
15
13
|
|
16
14
|
def deserialize(hash)
|
17
15
|
worker_class = hash["job_class"].constantize
|
18
|
-
worker_class.new
|
16
|
+
worker_class.new
|
19
17
|
end
|
20
18
|
|
21
19
|
def serialize?(argument)
|
22
|
-
defined?(::Sidekiq) && argument.class.include?(::Sidekiq::Worker)
|
20
|
+
defined?(::Sidekiq) && argument.class.include?(::Sidekiq::Worker) &&
|
21
|
+
!(defined?(::AcidicJob::ActiveKiq) && argument.class < ::AcidicJob::ActiveKiq)
|
23
22
|
end
|
24
23
|
end
|
25
24
|
end
|
26
25
|
end
|
27
|
-
# :nocov:
|
data/lib/acidic_job/version.rb
CHANGED
data/lib/acidic_job.rb
CHANGED
@@ -5,6 +5,7 @@ require_relative "acidic_job/errors"
|
|
5
5
|
require_relative "acidic_job/logger"
|
6
6
|
require_relative "acidic_job/arguments"
|
7
7
|
require_relative "acidic_job/serializer"
|
8
|
+
require_relative "acidic_job/configured_job"
|
8
9
|
require_relative "acidic_job/workflow_builder"
|
9
10
|
require_relative "acidic_job/idempotency_key"
|
10
11
|
require_relative "acidic_job/recovery_point"
|
@@ -14,6 +15,7 @@ require_relative "acidic_job/workflow_step"
|
|
14
15
|
require_relative "acidic_job/workflow"
|
15
16
|
require_relative "acidic_job/processor"
|
16
17
|
require_relative "acidic_job/perform_wrapper"
|
18
|
+
require_relative "acidic_job/perform_acidicly"
|
17
19
|
require_relative "acidic_job/extensions/action_mailer"
|
18
20
|
require_relative "acidic_job/extensions/noticed"
|
19
21
|
require_relative "acidic_job/mixin"
|
@@ -27,6 +29,7 @@ require_relative "acidic_job/serializers/job_serializer"
|
|
27
29
|
require_relative "acidic_job/serializers/range_serializer"
|
28
30
|
require_relative "acidic_job/serializers/recovery_point_serializer"
|
29
31
|
require_relative "acidic_job/serializers/worker_serializer"
|
32
|
+
require_relative "acidic_job/serializers/active_kiq_serializer"
|
30
33
|
|
31
34
|
require_relative "acidic_job/rails"
|
32
35
|
|
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: 0.8.
|
4
|
+
version: 0.8.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- fractaledmind
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-08-
|
11
|
+
date: 2022-08-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -253,6 +253,7 @@ files:
|
|
253
253
|
- lib/acidic_job/active_kiq.rb
|
254
254
|
- lib/acidic_job/arguments.rb
|
255
255
|
- lib/acidic_job/base.rb
|
256
|
+
- lib/acidic_job/configured_job.rb
|
256
257
|
- lib/acidic_job/errors.rb
|
257
258
|
- lib/acidic_job/extensions/action_mailer.rb
|
258
259
|
- lib/acidic_job/extensions/noticed.rb
|
@@ -260,12 +261,14 @@ files:
|
|
260
261
|
- lib/acidic_job/idempotency_key.rb
|
261
262
|
- lib/acidic_job/logger.rb
|
262
263
|
- lib/acidic_job/mixin.rb
|
264
|
+
- lib/acidic_job/perform_acidicly.rb
|
263
265
|
- lib/acidic_job/perform_wrapper.rb
|
264
266
|
- lib/acidic_job/processor.rb
|
265
267
|
- lib/acidic_job/rails.rb
|
266
268
|
- lib/acidic_job/recovery_point.rb
|
267
269
|
- lib/acidic_job/run.rb
|
268
270
|
- lib/acidic_job/serializer.rb
|
271
|
+
- lib/acidic_job/serializers/active_kiq_serializer.rb
|
269
272
|
- lib/acidic_job/serializers/exception_serializer.rb
|
270
273
|
- lib/acidic_job/serializers/finished_point_serializer.rb
|
271
274
|
- lib/acidic_job/serializers/job_serializer.rb
|