acidic_job 0.1.5 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/Gemfile.lock +50 -1
- data/README.md +5 -5
- data/acidic_job.gemspec +12 -10
- data/lib/acidic_job/no_op.rb +5 -3
- data/lib/acidic_job/recovery_point.rb +9 -7
- data/lib/acidic_job/response.rb +8 -6
- data/lib/acidic_job/version.rb +1 -1
- data/lib/acidic_job.rb +57 -36
- data/lib/generators/acidic_job_generator.rb +38 -0
- data/lib/generators/templates/migration.rb +18 -0
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 528d362023c8d2547054566f13d65556e057285124ffb05f8bc4c6007848b5cb
|
4
|
+
data.tar.gz: 05d08959ff8b674b48966ce4b879fd8c290d658a20c24b6af27e0c1fa8150452
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68bdc77c5e23e4fa680ca9ec72e00cf62cc1bcb2fc133b539d4c17f68b8926da9edd19f895ff92a0a379db567aee9ee949f0ade58eee0ef8b38edd61d28b5c19
|
7
|
+
data.tar.gz: b352b70b51cf7f3244a057dc5d6b2a89ede6e9b317346dd0fd0d54e9115ffff84a86670ac27c3bd45dc8b8f28d535de9af308fbd1b6be8dbd6eec76a7d740485
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,12 +1,26 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
acidic_job (0.
|
4
|
+
acidic_job (0.3.0)
|
5
|
+
activerecord (>= 4.0.0)
|
5
6
|
activesupport
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: https://rubygems.org/
|
9
10
|
specs:
|
11
|
+
actionpack (6.1.3.2)
|
12
|
+
actionview (= 6.1.3.2)
|
13
|
+
activesupport (= 6.1.3.2)
|
14
|
+
rack (~> 2.0, >= 2.0.9)
|
15
|
+
rack-test (>= 0.6.3)
|
16
|
+
rails-dom-testing (~> 2.0)
|
17
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
18
|
+
actionview (6.1.3.2)
|
19
|
+
activesupport (= 6.1.3.2)
|
20
|
+
builder (~> 3.1)
|
21
|
+
erubi (~> 1.4)
|
22
|
+
rails-dom-testing (~> 2.0)
|
23
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
10
24
|
activejob (6.1.3.2)
|
11
25
|
activesupport (= 6.1.3.2)
|
12
26
|
globalid (>= 0.3.6)
|
@@ -22,7 +36,10 @@ GEM
|
|
22
36
|
tzinfo (~> 2.0)
|
23
37
|
zeitwerk (~> 2.3)
|
24
38
|
ast (2.4.2)
|
39
|
+
builder (3.2.4)
|
40
|
+
coderay (1.1.3)
|
25
41
|
concurrent-ruby (1.1.9)
|
42
|
+
crass (1.0.6)
|
26
43
|
database_cleaner (2.0.1)
|
27
44
|
database_cleaner-active_record (~> 2.0.0)
|
28
45
|
database_cleaner-active_record (2.0.1)
|
@@ -30,14 +47,43 @@ GEM
|
|
30
47
|
database_cleaner-core (~> 2.0.0)
|
31
48
|
database_cleaner-core (2.0.1)
|
32
49
|
docile (1.4.0)
|
50
|
+
erubi (1.10.0)
|
33
51
|
globalid (0.4.2)
|
34
52
|
activesupport (>= 4.2.0)
|
35
53
|
i18n (1.8.10)
|
36
54
|
concurrent-ruby (~> 1.0)
|
55
|
+
loofah (2.12.0)
|
56
|
+
crass (~> 1.0.2)
|
57
|
+
nokogiri (>= 1.5.9)
|
58
|
+
method_source (1.0.0)
|
59
|
+
mini_portile2 (2.6.1)
|
37
60
|
minitest (5.14.4)
|
61
|
+
nokogiri (1.12.3)
|
62
|
+
mini_portile2 (~> 2.6.1)
|
63
|
+
racc (~> 1.4)
|
64
|
+
nokogiri (1.12.3-x86_64-darwin)
|
65
|
+
racc (~> 1.4)
|
38
66
|
parallel (1.20.1)
|
39
67
|
parser (3.0.1.1)
|
40
68
|
ast (~> 2.4.1)
|
69
|
+
pry (0.14.1)
|
70
|
+
coderay (~> 1.1)
|
71
|
+
method_source (~> 1.0)
|
72
|
+
racc (1.5.2)
|
73
|
+
rack (2.2.3)
|
74
|
+
rack-test (1.1.0)
|
75
|
+
rack (>= 1.0, < 3)
|
76
|
+
rails-dom-testing (2.0.3)
|
77
|
+
activesupport (>= 4.2.0)
|
78
|
+
nokogiri (>= 1.6)
|
79
|
+
rails-html-sanitizer (1.4.1)
|
80
|
+
loofah (~> 2.3)
|
81
|
+
railties (6.1.3.2)
|
82
|
+
actionpack (= 6.1.3.2)
|
83
|
+
activesupport (= 6.1.3.2)
|
84
|
+
method_source
|
85
|
+
rake (>= 0.8.7)
|
86
|
+
thor (~> 1.0)
|
41
87
|
rainbow (3.0.0)
|
42
88
|
rake (13.0.4)
|
43
89
|
regexp_parser (2.1.1)
|
@@ -65,6 +111,7 @@ GEM
|
|
65
111
|
simplecov-html (0.12.3)
|
66
112
|
simplecov_json_formatter (0.1.3)
|
67
113
|
sqlite3 (1.4.2)
|
114
|
+
thor (1.1.0)
|
68
115
|
tzinfo (2.0.4)
|
69
116
|
concurrent-ruby (~> 1.0)
|
70
117
|
unicode-display_width (2.0.0)
|
@@ -80,6 +127,8 @@ DEPENDENCIES
|
|
80
127
|
activerecord (~> 6.1.3.2)
|
81
128
|
database_cleaner
|
82
129
|
minitest (~> 5.0)
|
130
|
+
pry
|
131
|
+
railties (>= 4.0)
|
83
132
|
rake (~> 13.0)
|
84
133
|
rubocop (~> 1.7)
|
85
134
|
rubocop-minitest
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
### Idempotent operations for Rails apps, built on top of ActiveJob.
|
4
4
|
|
5
|
-
At the conceptual heart of basically any software are "operations"—the discrete actions the software performs. Rails provides a powerful abstraction layer for building operations in the form of `ActiveJob`. With `ActiveJob`, we can easily trigger from other Ruby code throughout our Rails application (controller actions, model methods, model callbacks, etc.); we can run operations both synchronously (blocking execution and then returning its response to the caller) and asychronously (non-blocking and the caller doesn't know its response); and we can also retry a specific operation if needed
|
5
|
+
At the conceptual heart of basically any software are "operations"—the discrete actions the software performs. Rails provides a powerful abstraction layer for building operations in the form of `ActiveJob`. With `ActiveJob`, we can easily trigger from other Ruby code throughout our Rails application (controller actions, model methods, model callbacks, etc.); we can run operations both synchronously (blocking execution and then returning its response to the caller) and asychronously (non-blocking and the caller doesn't know its response); and we can also retry a specific operation if needed seamlessly.
|
6
6
|
|
7
7
|
However, in order to ensure that our operational jobs are _robust_, we need to ensure that they are properly [idempotent and transactional](https://github.com/mperham/sidekiq/wiki/Best-Practices#2-make-your-job-idempotent-and-transactional). As stated in the [GitLab Sidekiq Style Guide](https://docs.gitlab.com/ee/development/sidekiq_style_guide.html#idempotent-jobs):
|
8
8
|
|
@@ -19,7 +19,7 @@ This is, of course, far easier said than done. Thus, `AcidicJob`.
|
|
19
19
|
3. https://brandur.org/job-drain
|
20
20
|
4. https://brandur.org/idempotency-keys
|
21
21
|
|
22
|
-
`
|
22
|
+
`AcidicJob` brings these techniques and principles into the world of a standard Rails application.
|
23
23
|
|
24
24
|
## Installation
|
25
25
|
|
@@ -33,14 +33,14 @@ And then execute:
|
|
33
33
|
|
34
34
|
$ bundle install
|
35
35
|
|
36
|
-
Or install
|
36
|
+
Or simply execute to install the gem yourself:
|
37
37
|
|
38
|
-
$
|
38
|
+
$ bundle add acidic_job
|
39
39
|
|
40
40
|
Then, use the following command to copy over the AcidicJobKey migration.
|
41
41
|
|
42
42
|
```
|
43
|
-
rails generate acidic_job
|
43
|
+
rails generate acidic_job
|
44
44
|
```
|
45
45
|
|
46
46
|
## Usage
|
data/acidic_job.gemspec
CHANGED
@@ -3,15 +3,15 @@
|
|
3
3
|
require_relative "lib/acidic_job/version"
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name
|
7
|
-
spec.version
|
8
|
-
spec.authors
|
9
|
-
spec.email
|
6
|
+
spec.name = "acidic_job"
|
7
|
+
spec.version = AcidicJob::VERSION
|
8
|
+
spec.authors = ["fractaledmind"]
|
9
|
+
spec.email = ["stephen.margheim@gmail.com"]
|
10
10
|
|
11
|
-
spec.summary
|
12
|
-
spec.description
|
13
|
-
spec.homepage
|
14
|
-
spec.license
|
11
|
+
spec.summary = "Idempotent operations for Rails apps, built on top of ActiveJob."
|
12
|
+
spec.description = "Idempotent operations for Rails apps, built on top of ActiveJob."
|
13
|
+
spec.homepage = "https://github.com/fractaledmind/acidic_job"
|
14
|
+
spec.license = "MIT"
|
15
15
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
|
16
16
|
|
17
17
|
spec.metadata["homepage_uri"] = spec.homepage
|
@@ -23,11 +23,13 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
24
24
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
25
25
|
end
|
26
|
-
spec.bindir
|
27
|
-
spec.executables
|
26
|
+
spec.bindir = "exe"
|
27
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
28
28
|
spec.require_paths = ["lib"]
|
29
29
|
|
30
30
|
spec.add_dependency "activesupport"
|
31
|
+
spec.add_dependency "activerecord", ">= 4.0.0"
|
32
|
+
spec.add_development_dependency "railties", ">= 4.0"
|
31
33
|
|
32
34
|
# For more information and examples about making a new gem, checkout our
|
33
35
|
# guide at: https://bundler.io/guides/creating_gem.html
|
data/lib/acidic_job/no_op.rb
CHANGED
@@ -2,14 +2,16 @@
|
|
2
2
|
|
3
3
|
# Represents an action to set a new recovery point. One possible option for a
|
4
4
|
# return from an #atomic_phase block.
|
5
|
-
|
6
|
-
|
5
|
+
module AcidicJob
|
6
|
+
class RecoveryPoint
|
7
|
+
attr_accessor :name
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
def initialize(name)
|
10
|
+
self.name = name
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
13
|
+
def call(key:)
|
14
|
+
key.update_column(:recovery_point, name)
|
15
|
+
end
|
14
16
|
end
|
15
17
|
end
|
data/lib/acidic_job/response.rb
CHANGED
@@ -3,11 +3,13 @@
|
|
3
3
|
# Represents an action to set a new API response (which will be stored onto an
|
4
4
|
# idempotency key). One possible option for a return from an #atomic_phase
|
5
5
|
# block.
|
6
|
-
|
7
|
-
|
8
|
-
key
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
module AcidicJob
|
7
|
+
class Response
|
8
|
+
def call(key:)
|
9
|
+
key.update!(
|
10
|
+
locked_at: nil,
|
11
|
+
recovery_point: Key::RECOVERY_POINT_FINISHED
|
12
|
+
)
|
13
|
+
end
|
12
14
|
end
|
13
15
|
end
|
data/lib/acidic_job/version.rb
CHANGED
data/lib/acidic_job.rb
CHANGED
@@ -8,12 +8,6 @@ require "active_support/concern"
|
|
8
8
|
|
9
9
|
# rubocop:disable Metrics/ModuleLength, Style/Documentation, Metrics/AbcSize, Metrics/MethodLength
|
10
10
|
module AcidicJob
|
11
|
-
class IdempotencyKeyRequired < StandardError; end
|
12
|
-
|
13
|
-
class MissingRequiredAttribute < StandardError; end
|
14
|
-
|
15
|
-
class IdempotencyKeyTooShort < StandardError; end
|
16
|
-
|
17
11
|
class MismatchedIdempotencyKeyAndJobArguments < StandardError; end
|
18
12
|
|
19
13
|
class LockedIdempotencyKey < StandardError; end
|
@@ -24,6 +18,33 @@ module AcidicJob
|
|
24
18
|
|
25
19
|
class SerializedTransactionConflict < StandardError; end
|
26
20
|
|
21
|
+
class Key < ActiveRecord::Base
|
22
|
+
RECOVERY_POINT_FINISHED = "FINISHED"
|
23
|
+
|
24
|
+
self.table_name = "acidic_job_keys"
|
25
|
+
|
26
|
+
serialize :error_object
|
27
|
+
serialize :job_args
|
28
|
+
|
29
|
+
validates :idempotency_key, presence: true, uniqueness: {scope: [:job_name, :job_args]}
|
30
|
+
validates :job_name, presence: true
|
31
|
+
validates :job_args, presence: true
|
32
|
+
validates :last_run_at, presence: true
|
33
|
+
validates :recovery_point, presence: true
|
34
|
+
|
35
|
+
def finished?
|
36
|
+
recovery_point == RECOVERY_POINT_FINISHED
|
37
|
+
end
|
38
|
+
|
39
|
+
def succeeded?
|
40
|
+
finished? && !failed?
|
41
|
+
end
|
42
|
+
|
43
|
+
def failed?
|
44
|
+
error_object.present?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
27
48
|
extend ActiveSupport::Concern
|
28
49
|
|
29
50
|
included do
|
@@ -38,32 +59,33 @@ module AcidicJob
|
|
38
59
|
end
|
39
60
|
|
40
61
|
# Number of seconds passed which we consider a held idempotency key lock to be
|
41
|
-
# defunct and eligible to be locked again by a different
|
62
|
+
# defunct and eligible to be locked again by a different job run. We try to
|
42
63
|
# unlock keys on our various failure conditions, but software is buggy, and
|
43
64
|
# this might not happen 100% of the time, so this is a hedge against it.
|
44
65
|
IDEMPOTENCY_KEY_LOCK_TIMEOUT = 90
|
45
66
|
|
46
|
-
# To try and enforce some level of required randomness in an idempotency key,
|
47
|
-
# we require a minimum length. This of course is a poor approximate, and in
|
48
|
-
# real life you might want to consider trying to measure actual entropy with
|
49
|
-
# something like the Shannon entropy equation.
|
50
|
-
IDEMPOTENCY_KEY_MIN_LENGTH = 20
|
51
|
-
|
52
67
|
# &block
|
53
|
-
def idempotently(with:)
|
68
|
+
def idempotently(with:) # &block
|
54
69
|
# set accessors for each argument passed in to ensure they are available
|
55
70
|
# to the step methods the job will have written
|
56
71
|
define_accessors_for_passed_arguments(with)
|
57
72
|
|
58
73
|
# execute the block to gather the info on what phases are defined for this job
|
59
74
|
defined_steps = yield
|
75
|
+
# [:create_ride_and_audit_record, :create_stripe_charge, :send_receipt]
|
60
76
|
|
61
77
|
# convert the array of steps into a hash of recovery_points and callable actions
|
62
78
|
phases = define_atomic_phases(defined_steps)
|
79
|
+
# { create_ride_and_audit_record: <#Method >, ... }
|
63
80
|
|
64
|
-
# find or create an
|
81
|
+
# find or create an Key record (our idempotency key) to store all information about this job
|
65
82
|
# side-effect: will set the @key instance variable
|
66
|
-
|
83
|
+
#
|
84
|
+
# A key concept here is that if two requests try to insert or update within
|
85
|
+
# close proximity, one of the two will be aborted by Postgres because we're
|
86
|
+
# using a transaction with SERIALIZABLE isolation level. It may not look
|
87
|
+
# it, but this code is safe from races.
|
88
|
+
ensure_idempotency_key_record(job_id, defined_steps.first)
|
67
89
|
|
68
90
|
# if the key record is already marked as finished, immediately return its result
|
69
91
|
return @key.succeeded? if @key.finished?
|
@@ -74,7 +96,7 @@ module AcidicJob
|
|
74
96
|
recovery_point = @key.recovery_point.to_sym
|
75
97
|
|
76
98
|
case recovery_point
|
77
|
-
when
|
99
|
+
when Key::RECOVERY_POINT_FINISHED.to_sym
|
78
100
|
break
|
79
101
|
else
|
80
102
|
raise UnknownRecoveryPoint unless phases.key? recovery_point
|
@@ -105,7 +127,7 @@ module AcidicJob
|
|
105
127
|
|
106
128
|
phase_result.call(key: key)
|
107
129
|
end
|
108
|
-
rescue
|
130
|
+
rescue => e
|
109
131
|
error = e
|
110
132
|
raise e
|
111
133
|
ensure
|
@@ -113,7 +135,7 @@ module AcidicJob
|
|
113
135
|
# key right away so that another request can try again.
|
114
136
|
begin
|
115
137
|
key.update_columns(locked_at: nil, error_object: error) if error.present?
|
116
|
-
rescue
|
138
|
+
rescue => e
|
117
139
|
# We're already inside an error condition, so swallow any additional
|
118
140
|
# errors from here and just send them to logs.
|
119
141
|
puts "Failed to unlock key #{key.id} because of #{e}."
|
@@ -121,38 +143,37 @@ module AcidicJob
|
|
121
143
|
end
|
122
144
|
end
|
123
145
|
|
124
|
-
def ensure_idempotency_key_record(key_val,
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
146
|
+
def ensure_idempotency_key_record(key_val, first_step)
|
147
|
+
isolation_level = case ActiveRecord::Base.connection.adapter_name.downcase.to_sym
|
148
|
+
when :sqlite
|
149
|
+
:read_uncommitted
|
150
|
+
else
|
151
|
+
:serializable
|
152
|
+
end
|
153
|
+
serialized_job_info = serialize
|
132
154
|
|
133
155
|
ActiveRecord::Base.transaction(isolation: isolation_level) do
|
134
|
-
@key =
|
156
|
+
@key = Key.find_by(idempotency_key: key_val)
|
135
157
|
|
136
158
|
if @key
|
137
159
|
# Programs enqueuing multiple jobs with different parameters but the
|
138
160
|
# same idempotency key is a bug.
|
139
|
-
raise MismatchedIdempotencyKeyAndJobArguments if @key.job_args !=
|
161
|
+
raise MismatchedIdempotencyKeyAndJobArguments if @key.job_args != serialized_job_info["arguments"]
|
140
162
|
|
141
163
|
# Only acquire a lock if the key is unlocked or its lock has expired
|
142
164
|
# because the original job was long enough ago.
|
143
165
|
raise LockedIdempotencyKey if @key.locked_at && @key.locked_at > Time.current - IDEMPOTENCY_KEY_LOCK_TIMEOUT
|
144
166
|
|
145
|
-
# Lock the key and update latest run unless the job is already
|
146
|
-
# finished.
|
167
|
+
# Lock the key and update latest run unless the job is already finished.
|
147
168
|
@key.update!(last_run_at: Time.current, locked_at: Time.current) unless @key.finished?
|
148
169
|
else
|
149
|
-
@key =
|
170
|
+
@key = Key.create!(
|
150
171
|
idempotency_key: key_val,
|
151
172
|
locked_at: Time.current,
|
152
173
|
last_run_at: Time.current,
|
153
174
|
recovery_point: first_step,
|
154
|
-
job_name:
|
155
|
-
job_args:
|
175
|
+
job_name: serialized_job_info["job_class"],
|
176
|
+
job_args: serialized_job_info["arguments"]
|
156
177
|
)
|
157
178
|
end
|
158
179
|
end
|
@@ -170,14 +191,14 @@ module AcidicJob
|
|
170
191
|
end
|
171
192
|
|
172
193
|
def define_atomic_phases(defined_steps)
|
173
|
-
defined_steps <<
|
194
|
+
defined_steps << Key::RECOVERY_POINT_FINISHED
|
174
195
|
|
175
196
|
{}.tap do |phases|
|
176
197
|
defined_steps.each_cons(2).map do |enter_method, exit_method|
|
177
198
|
phases[enter_method] = lambda do
|
178
199
|
method(enter_method).call
|
179
200
|
|
180
|
-
if exit_method ==
|
201
|
+
if exit_method.to_s == Key::RECOVERY_POINT_FINISHED
|
181
202
|
Response.new
|
182
203
|
else
|
183
204
|
RecoveryPoint.new(exit_method)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
require "rails/generators/active_record"
|
3
|
+
|
4
|
+
# This generator adds a migration for the {FriendlyId::History
|
5
|
+
# FriendlyId::History} addon.
|
6
|
+
class AcidicJobGenerator < ActiveRecord::Generators::Base
|
7
|
+
# ActiveRecord::Generators::Base inherits from Rails::Generators::NamedBase which requires a NAME parameter for the
|
8
|
+
# new table name. Our generator always uses 'acidic_job_keys', so we just set a random name here.
|
9
|
+
argument :name, type: :string, default: "random_name"
|
10
|
+
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
12
|
+
|
13
|
+
def self.next_migration_number(_path)
|
14
|
+
if instance_variable_defined?("@prev_migration_nr")
|
15
|
+
@prev_migration_nr += 1
|
16
|
+
else
|
17
|
+
@prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
|
18
|
+
end
|
19
|
+
|
20
|
+
@prev_migration_nr.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
# Copies the migration template to db/migrate.
|
24
|
+
def copy_files
|
25
|
+
migration_template "migration.rb",
|
26
|
+
"db/migrate/create_acidic_job_keys.rb"
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def migration_class
|
32
|
+
if ActiveRecord::VERSION::MAJOR >= 5
|
33
|
+
ActiveRecord::Migration["#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"]
|
34
|
+
else
|
35
|
+
ActiveRecord::Migration
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateAcidicJobKeys < <%= migration_class %>
|
2
|
+
def change
|
3
|
+
create_table :acidic_job_keys do |t|
|
4
|
+
t.string :idempotency_key, null: false
|
5
|
+
t.string :job_name, null: false
|
6
|
+
t.text :job_args, null: false
|
7
|
+
t.datetime :last_run_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
8
|
+
t.datetime :locked_at, null: true
|
9
|
+
t.string :recovery_point, null: false
|
10
|
+
t.text :error_object
|
11
|
+
t.timestamps
|
12
|
+
|
13
|
+
t.index %i[idempotency_key job_name job_args],
|
14
|
+
unique: true,
|
15
|
+
name: "idx_acidic_job_keys_on_idempotency_key_n_job_name_n_job_args"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
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: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- fractaledmind
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -24,6 +24,34 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 4.0.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 4.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: railties
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.0'
|
27
55
|
description: Idempotent operations for Rails apps, built on top of ActiveJob.
|
28
56
|
email:
|
29
57
|
- stephen.margheim@gmail.com
|
@@ -48,6 +76,8 @@ files:
|
|
48
76
|
- lib/acidic_job/recovery_point.rb
|
49
77
|
- lib/acidic_job/response.rb
|
50
78
|
- lib/acidic_job/version.rb
|
79
|
+
- lib/generators/acidic_job_generator.rb
|
80
|
+
- lib/generators/templates/migration.rb
|
51
81
|
homepage: https://github.com/fractaledmind/acidic_job
|
52
82
|
licenses:
|
53
83
|
- MIT
|