marj 2.0.0 → 2.1.0
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 +4 -4
- data/README.md +71 -32
- data/lib/marj.rb +3 -1
- data/lib/marj_adapter.rb +2 -0
- data/lib/marj_config.rb +2 -0
- data/{app/models/marj.rb → lib/marj_record.rb} +26 -16
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '078c3d48b27784e09425ba8eda2207038d6ce5c7a711953e8567c76aac689435'
|
4
|
+
data.tar.gz: c129b4848de1e2fe55de0de3a4893586a052cd814df798879dc196a81c1a94d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4cef48fb7c862123f4a5ad7ed5bf7a9d95eb3d7036831db79ad3dc9cf94f074a112ce58f0189484f991a52185cdf8e8ceed48dd9237bf4a04d5e3bbe30b2d274
|
7
|
+
data.tar.gz: 7631b1a0fef1ece1edcf15541dc85d25b2d5d3ba1cb8ca716133975126e5ce9bb4f28d627a4c2a5d751029ce8f67ba4db47c6fa9d197467fade4c09d20a04716
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
# Marj
|
1
|
+
# Marj - Minimal ActiveRecord Jobs
|
2
2
|
|
3
|
-
|
3
|
+
The simplest database-backed ActiveJob queueing backend.
|
4
|
+
|
5
|
+
## Quick Links
|
4
6
|
|
5
7
|
API docs: https://www.rubydoc.info/github/nicholasdower/marj <br>
|
6
8
|
RubyGems: https://rubygems.org/gems/marj <br>
|
@@ -8,27 +10,52 @@ Changelog: https://github.com/nicholasdower/marj/releases <br>
|
|
8
10
|
Issues: https://github.com/nicholasdower/marj/issues <br>
|
9
11
|
Development: https://github.com/nicholasdower/marj/blob/master/CONTRIBUTING.md
|
10
12
|
|
11
|
-
|
13
|
+
## Features
|
12
14
|
|
13
|
-
-
|
14
|
-
-
|
15
|
-
-
|
15
|
+
- Enqueued jobs are written to the database.
|
16
|
+
- Successfully executed jobs are deleted from the database.
|
17
|
+
- Failed jobs which should be retried are updated in the database.
|
18
|
+
- Failed jobs which should not be retried are deleted from the database.
|
19
|
+
|
20
|
+
## Features Not Provided
|
21
|
+
|
22
|
+
- Workers
|
23
|
+
- Timeouts
|
24
|
+
- Concurrency Controls
|
25
|
+
- Observability
|
26
|
+
- A User Interace
|
27
|
+
- Anything else you might dream up.
|
28
|
+
|
29
|
+
## Interface
|
30
|
+
|
31
|
+
- `Marj` - An ActiveRecord model class
|
32
|
+
- `Marj.ready` - Used to retrieve jobs ready to be executed
|
33
|
+
- `Marj#execute` - Used to execute jobs retrieved from the database
|
34
|
+
- `MarjConfig.table_name=` - Used to override the default table name
|
16
35
|
|
17
36
|
## Setup
|
18
37
|
|
19
38
|
### 1. Install
|
20
39
|
|
21
|
-
|
40
|
+
```shell
|
41
|
+
bundle add marj
|
22
42
|
|
23
|
-
|
24
|
-
|
43
|
+
# or
|
44
|
+
|
45
|
+
gem install marj
|
25
46
|
```
|
26
47
|
|
27
|
-
|
28
|
-
|
48
|
+
### 2. Configure
|
49
|
+
|
50
|
+
By default, the database table is named "jobs". To use a different table name:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
require 'marj'
|
54
|
+
|
55
|
+
MarjConfig.table_name = 'some_name'
|
29
56
|
```
|
30
57
|
|
31
|
-
###
|
58
|
+
### 3. Create the database table
|
32
59
|
|
33
60
|
```ruby
|
34
61
|
class CreateJobs < ActiveRecord::Migration[7.1]
|
@@ -57,15 +84,7 @@ class CreateJobs < ActiveRecord::Migration[7.1]
|
|
57
84
|
end
|
58
85
|
```
|
59
86
|
|
60
|
-
|
61
|
-
|
62
|
-
```ruby
|
63
|
-
require 'marj'
|
64
|
-
|
65
|
-
MarjConfig.table_name = 'some_name'
|
66
|
-
```
|
67
|
-
|
68
|
-
### 3. Configure the queue adapter
|
87
|
+
### 4. Configure the queue adapter
|
69
88
|
|
70
89
|
```ruby
|
71
90
|
require 'marj'
|
@@ -93,28 +112,40 @@ job.perform_now
|
|
93
112
|
|
94
113
|
# Enqueue, retrieve and manually run a job:
|
95
114
|
SomeJob.perform_later('foo')
|
96
|
-
|
97
|
-
record.execute
|
115
|
+
Marj.first.execute
|
98
116
|
|
99
117
|
# Run all ready jobs:
|
100
|
-
|
101
|
-
|
102
|
-
|
118
|
+
Marj.ready.each(&:execute)
|
119
|
+
|
120
|
+
# Run all ready jobs, querying each time:
|
121
|
+
loop { Marj.ready.first&.tap(&:execute) || break }
|
103
122
|
|
104
123
|
# Run all ready jobs in a specific queue:
|
105
|
-
|
106
|
-
record.execute
|
107
|
-
end
|
124
|
+
loop { Marj.where(queue_name: 'foo').ready.first&.tap(&:execute) || break }
|
108
125
|
|
109
126
|
# Run jobs as they become ready:
|
110
127
|
loop do
|
111
|
-
|
112
|
-
|
113
|
-
|
128
|
+
loop { Marj.ready.first&.tap(&:execute) || break }
|
129
|
+
rescue Exception => e
|
130
|
+
logger.error(e)
|
131
|
+
ensure
|
114
132
|
sleep 5.seconds
|
115
133
|
end
|
116
134
|
```
|
117
135
|
|
136
|
+
## Testing
|
137
|
+
|
138
|
+
By default, jobs enqeued during tests will be written to the database. Enqueued jobs can be executed via:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
Marj.ready.each(&:execute)
|
142
|
+
```
|
143
|
+
|
144
|
+
Alternatively, to use [ActiveJob::QueueAdapters::TestAdapter](https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/TestAdapter.html):
|
145
|
+
```ruby
|
146
|
+
ActiveJob::Base.queue_adapter = :test
|
147
|
+
```
|
148
|
+
|
118
149
|
## Extension Examples
|
119
150
|
|
120
151
|
### Timeouts
|
@@ -176,6 +207,12 @@ end
|
|
176
207
|
|
177
208
|
## ActiveJob Cheatsheet
|
178
209
|
|
210
|
+
For more information on ActiveJob, see:
|
211
|
+
|
212
|
+
- https://edgeguides.rubyonrails.org/active_job_basics.html
|
213
|
+
- https://www.rubydoc.info/gems/activejob
|
214
|
+
- https://github.com/nicholasdower/marj/blob/master/README.md#activejob-cheatsheet
|
215
|
+
|
179
216
|
### Configuring a Queue Adapter
|
180
217
|
|
181
218
|
```ruby
|
@@ -253,6 +290,8 @@ SomeJob.perform_now(args)
|
|
253
290
|
SomeJob.new(args).enqueue
|
254
291
|
SomeJob.new(args).enqueue(options)
|
255
292
|
|
293
|
+
SomeJob.perform_later(SomeJob.new(args))
|
294
|
+
|
256
295
|
SomeJob.perform_later(args)
|
257
296
|
SomeJob.set(options).perform_later(args)
|
258
297
|
|
data/lib/marj.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# See https://github.com/nicholasdower/marj
|
4
|
+
|
3
5
|
require_relative 'marj_adapter'
|
4
6
|
require_relative 'marj_config'
|
5
7
|
|
6
|
-
Kernel.autoload(:Marj, File.expand_path('
|
8
|
+
Kernel.autoload(:Marj, File.expand_path('marj_record.rb', __dir__))
|
data/lib/marj_adapter.rb
CHANGED
data/lib/marj_config.rb
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
|
3
3
|
require 'active_job'
|
4
4
|
require 'active_record'
|
5
|
-
require_relative '
|
5
|
+
require_relative 'marj_config'
|
6
6
|
|
7
|
-
# Marj
|
7
|
+
# The Marj ActiveRecord model class.
|
8
8
|
#
|
9
9
|
# See https://github.com/nicholasdower/marj
|
10
10
|
class Marj < ActiveRecord::Base
|
11
11
|
# The Marj version.
|
12
|
-
VERSION = '2.
|
12
|
+
VERSION = '2.1.0'
|
13
13
|
|
14
14
|
# Executes the job associated with this record and returns the result.
|
15
15
|
def execute
|
@@ -29,13 +29,9 @@ class Marj < ActiveRecord::Base
|
|
29
29
|
|
30
30
|
# ActiveJob::Base#deserialize expects dates to be strings rather than Time objects.
|
31
31
|
job_data = job_data.to_h { |k, v| [k, %w[enqueued_at scheduled_at].include?(k) ? v&.iso8601 : v] }
|
32
|
-
job.deserialize(job_data)
|
33
32
|
|
34
|
-
|
35
|
-
job.perform_now
|
36
|
-
# If no error was raised, the job should either be destroyed (success) or updated (retryable failure).
|
37
|
-
raise "job #{job_id} not destroyed or updated" unless destroyed? || (executions == new_executions && !changed?)
|
38
|
-
end
|
33
|
+
job.deserialize(job_data)
|
34
|
+
job.perform_now
|
39
35
|
end
|
40
36
|
end
|
41
37
|
|
@@ -97,15 +93,20 @@ class Marj < ActiveRecord::Base
|
|
97
93
|
# @param job [ActiveJob::Base]
|
98
94
|
# @return [ActiveJob::Base]
|
99
95
|
def self.register_callbacks(job, record)
|
100
|
-
|
96
|
+
if job.singleton_class.instance_variable_get(:@__marj)
|
97
|
+
# Callbacks already registered. We just need to update the record.
|
98
|
+
job.singleton_class.instance_variable_set(:@__marj, record)
|
99
|
+
return
|
100
|
+
end
|
101
101
|
|
102
102
|
# We need to detect three cases:
|
103
103
|
# - If a job succeeds, after_perform will be called.
|
104
104
|
# - If a job fails and should be retried, enqueue will be called. This is handled by the enqueue method.
|
105
105
|
# - If a job exceeds its max attempts, after_discard will be called.
|
106
|
-
job.singleton_class.after_perform { |_j|
|
107
|
-
job.singleton_class.after_discard { |_j, _exception|
|
106
|
+
job.singleton_class.after_perform { |_j| job.singleton_class.instance_variable_get(:@__marj).destroy! }
|
107
|
+
job.singleton_class.after_discard { |_j, _exception| job.singleton_class.instance_variable_get(:@__marj).destroy! }
|
108
108
|
job.singleton_class.instance_variable_set(:@__marj, record)
|
109
|
+
|
109
110
|
job
|
110
111
|
end
|
111
112
|
private_class_method :register_callbacks
|
@@ -124,19 +125,28 @@ class Marj < ActiveRecord::Base
|
|
124
125
|
# registered on the job instance so that when the job is executed, the database record is deleted or updated
|
125
126
|
# (depending on the result).
|
126
127
|
#
|
127
|
-
# There are
|
128
|
+
# There are two normal cases:
|
128
129
|
# - The first time a job is enqueued, we need to create the record and register callbacks.
|
129
130
|
# - If a previously enqueued job instance is re-enqueued, for instance after execution fails, callbacks have
|
130
131
|
# already been registered. In this case we only need to update the record.
|
131
|
-
# - It is also possible for new job instance to be created for a job that is already in the database. In this case
|
132
|
-
# we need to update the record and register callbacks.
|
133
132
|
#
|
134
133
|
# We keep track of whether callbacks have been registered by setting the @__marj instance variable on the job's
|
135
134
|
# singleton class. This holds a reference to the record. This allows us to update the record without re-fetching it
|
136
135
|
# and also ensures that if execute is called on a record any updates to the database are reflected on that record
|
137
136
|
# instance.
|
137
|
+
#
|
138
|
+
# There are also two edge cases:
|
139
|
+
# - It is possible for new job instance to be created for a job that is already in the database. In this case
|
140
|
+
# we need to update the record and register callbacks.
|
141
|
+
# - It is possible for the underlying row corresponding to an existing job to have been deleted. In this case we
|
142
|
+
# need to create a new record and update the reference on the job's singleton class.
|
138
143
|
if (record = job.singleton_class.instance_variable_get(:@__marj))
|
139
|
-
|
144
|
+
if Marj.exists?(job_id: job.job_id)
|
145
|
+
record.update!(serialized)
|
146
|
+
else
|
147
|
+
record = Marj.create!(serialized)
|
148
|
+
register_callbacks(job, record)
|
149
|
+
end
|
140
150
|
else
|
141
151
|
record = Marj.find_or_create_by!(job_id: job.job_id) { _1.assign_attributes(serialized) }
|
142
152
|
register_callbacks(job, record)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: marj
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Dower
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-01-
|
11
|
+
date: 2024-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -38,7 +38,8 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '7.1'
|
41
|
-
description: Minimal ActiveRecord
|
41
|
+
description: Marj (Minimal ActiveRecord Jobs) is the simplest database-backed ActiveJob
|
42
|
+
queueing backend.
|
42
43
|
email: nicholasdower@gmail.com
|
43
44
|
executables: []
|
44
45
|
extensions: []
|
@@ -46,17 +47,17 @@ extra_rdoc_files: []
|
|
46
47
|
files:
|
47
48
|
- LICENSE.txt
|
48
49
|
- README.md
|
49
|
-
- app/models/marj.rb
|
50
50
|
- lib/marj.rb
|
51
51
|
- lib/marj_adapter.rb
|
52
52
|
- lib/marj_config.rb
|
53
|
+
- lib/marj_record.rb
|
53
54
|
homepage: https://github.com/nicholasdower/marj
|
54
55
|
licenses:
|
55
56
|
- MIT
|
56
57
|
metadata:
|
57
58
|
bug_tracker_uri: https://github.com/nicholasdower/marj/issues
|
58
|
-
changelog_uri: https://github.com/nicholasdower/marj/releases/tag/v2.
|
59
|
-
documentation_uri: https://www.rubydoc.info/github/nicholasdower/marj/v2.
|
59
|
+
changelog_uri: https://github.com/nicholasdower/marj/releases/tag/v2.1.0
|
60
|
+
documentation_uri: https://www.rubydoc.info/github/nicholasdower/marj/v2.1.0
|
60
61
|
homepage_uri: https://github.com/nicholasdower/marj
|
61
62
|
rubygems_mfa_required: 'true'
|
62
63
|
source_code_uri: https://github.com/nicholasdower/marj
|
@@ -78,5 +79,5 @@ requirements: []
|
|
78
79
|
rubygems_version: 3.5.3
|
79
80
|
signing_key:
|
80
81
|
specification_version: 4
|
81
|
-
summary:
|
82
|
+
summary: Minimal ActiveRecord Jobs
|
82
83
|
test_files: []
|