good_job 0.1.0 → 0.2.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 +52 -14
- data/lib/good_job/adapter.rb +8 -1
- data/lib/good_job/logging.rb +60 -0
- data/lib/good_job/railtie.rb +3 -0
- data/lib/good_job/scheduler.rb +27 -31
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +5 -1
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c0fc31238c6d66b5b9ed3ddc0d4b9393a0a5a2cca85c7fcd0f2f24650892675
|
4
|
+
data.tar.gz: d6484d63b86eb7bc748b308cbfebe1ca7798fa1fa77d9542754dc99504f12244
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e4d9fc5ae59750231906be49188aba712f1bdfbe2d66ed441f82c69ad267b0a056fed12998fe594c33c862f81d36d912a56d598c8e436e1e0ea225634abe66a
|
7
|
+
data.tar.gz: b02e2c08035b4a90713a0378e2b83173232baec7bf6b22ecbd56033d8fd38e78be0e3d992a7ca4e3339485014bc97fac62efd3d3fd94df195fceb9248703f004
|
data/README.md
CHANGED
@@ -2,14 +2,34 @@
|
|
2
2
|
|
3
3
|
GoodJob is a multithreaded, Postgres-based ActiveJob backend for Ruby on Rails.
|
4
4
|
|
5
|
+
Inspired by [Delayed::Job](https://github.com/collectiveidea/delayed_job) and [Que](https://github.com/que-rb/que), GoodJob’s design principles are:
|
6
|
+
|
7
|
+
- Stand on the shoulders of ActiveJob. For example, [exception](https://edgeguides.rubyonrails.org/active_job_basics.html#exceptions) and [retry](https://edgeguides.rubyonrails.org/active_job_basics.html#retrying-or-discarding-failed-jobs) behavior.
|
8
|
+
- Stand on the shoulders of Ruby on Rails. For example, ActiveRecord ORM, connection pools, and [multithreaded support](https://guides.rubyonrails.org/threading_and_code_execution.html) with [Concurrent-Ruby](https://github.com/ruby-concurrency/concurrent-ruby).
|
9
|
+
- Stand on the shoulders of Postgres. For example, Advisory Locks.
|
10
|
+
- Convention over simplicity over performance.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'good_job', github: 'bensheldon/good_job'
|
18
|
+
```
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
```bash
|
22
|
+
$ bundle
|
23
|
+
```
|
24
|
+
|
5
25
|
## Usage
|
6
26
|
|
7
27
|
1. Create a database migration:
|
8
28
|
```bash
|
9
|
-
bin/rails g migration CreateGoodJobs
|
29
|
+
$ bin/rails g migration CreateGoodJobs
|
10
30
|
```
|
11
31
|
|
12
|
-
|
32
|
+
Add to the newly created migration file:
|
13
33
|
|
14
34
|
```ruby
|
15
35
|
class CreateGoodJobs < ActiveRecord::Migration[6.0]
|
@@ -27,6 +47,13 @@ GoodJob is a multithreaded, Postgres-based ActiveJob backend for Ruby on Rails.
|
|
27
47
|
end
|
28
48
|
end
|
29
49
|
```
|
50
|
+
|
51
|
+
Run the migration:
|
52
|
+
|
53
|
+
```bash
|
54
|
+
$ bin/rails db:migrate
|
55
|
+
```
|
56
|
+
|
30
57
|
1. Configure the ActiveJob adapter:
|
31
58
|
```ruby
|
32
59
|
# config/environments/production.rb
|
@@ -41,18 +68,6 @@ GoodJob is a multithreaded, Postgres-based ActiveJob backend for Ruby on Rails.
|
|
41
68
|
$ bundle exec good_job
|
42
69
|
```
|
43
70
|
|
44
|
-
## Installation
|
45
|
-
Add this line to your application's Gemfile:
|
46
|
-
|
47
|
-
```ruby
|
48
|
-
gem 'good_job', github: 'bensheldon/good_job'
|
49
|
-
```
|
50
|
-
|
51
|
-
And then execute:
|
52
|
-
```bash
|
53
|
-
$ bundle
|
54
|
-
```
|
55
|
-
|
56
71
|
## Development
|
57
72
|
|
58
73
|
To run tests:
|
@@ -80,8 +95,31 @@ $ bundle install
|
|
80
95
|
# => Using good_job 0.1.0 from https://github.com/bensheldon/good_job.git (at /Users/You/Projects/good_job@dc57fb0)
|
81
96
|
```
|
82
97
|
|
98
|
+
## Releasing
|
99
|
+
|
100
|
+
Package maintainers can release this gem with the following [gem-release](https://github.com/svenfuchs/gem-release) command:
|
101
|
+
|
102
|
+
```bash
|
103
|
+
# Sign into rubygems
|
104
|
+
$ gem signin
|
105
|
+
|
106
|
+
# Increase the version number
|
107
|
+
$ gem bump -v minor --no-commit
|
108
|
+
|
109
|
+
# Update the changelog
|
110
|
+
$ bundle exec changelog
|
111
|
+
|
112
|
+
# Commit the version and changelog to git
|
113
|
+
$ bundle commit_version
|
114
|
+
|
115
|
+
# Push to rubygems.org
|
116
|
+
$ gem release
|
117
|
+
```
|
118
|
+
|
83
119
|
## Contributing
|
120
|
+
|
84
121
|
Contribution directions go here.
|
85
122
|
|
86
123
|
## License
|
124
|
+
|
87
125
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/good_job/adapter.rb
CHANGED
@@ -22,7 +22,14 @@ module GoodJob
|
|
22
22
|
params[:scheduled_at] = Time.at(timestamp) if timestamp
|
23
23
|
|
24
24
|
good_job = GoodJob::Job.create(params)
|
25
|
-
|
25
|
+
job.provider_job_id = good_job.id
|
26
|
+
|
27
|
+
GoodJob.tag_logger do
|
28
|
+
ActiveSupport::Notifications.instrument("create.good_job", { good_job: good_job, job: job })
|
29
|
+
@scheduler.enqueue(good_job) if inline?
|
30
|
+
end
|
31
|
+
|
32
|
+
good_job
|
26
33
|
end
|
27
34
|
|
28
35
|
def shutdown(wait: true)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module GoodJob::Logging
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
cattr_accessor :logger, default: ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
|
6
|
+
|
7
|
+
def self.tag_logger(*tags)
|
8
|
+
if logger.respond_to?(:tagged)
|
9
|
+
tags.unshift "GoodJob" unless logger.formatter.current_tags.include?("GoodJob")
|
10
|
+
logger.tagged(*tags) { yield }
|
11
|
+
else
|
12
|
+
yield
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
18
|
+
def create(event)
|
19
|
+
good_job = event.payload[:good_job]
|
20
|
+
|
21
|
+
info do
|
22
|
+
"Created GoodJob resource with id #{good_job.id}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def timer_task_finished(event)
|
27
|
+
result = event.payload[:result]
|
28
|
+
exception = event.payload[:error]
|
29
|
+
|
30
|
+
if exception
|
31
|
+
error do
|
32
|
+
"ERROR: #{exception}\n #{exception.backtrace}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def job_finished(event)
|
38
|
+
result = event.payload[:result]
|
39
|
+
exception = event.payload[:error]
|
40
|
+
|
41
|
+
if exception
|
42
|
+
error do
|
43
|
+
"ERROR: #{exception}\n #{exception.backtrace}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def logger
|
51
|
+
GoodJob.logger
|
52
|
+
end
|
53
|
+
|
54
|
+
def thread_name
|
55
|
+
Thread.current.name || Thread.current.object_id
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
GoodJob::Logging::LogSubscriber.attach_to :good_job
|
data/lib/good_job/railtie.rb
CHANGED
data/lib/good_job/scheduler.rb
CHANGED
@@ -4,7 +4,7 @@ require "concurrent/utility/processor_counter"
|
|
4
4
|
|
5
5
|
module GoodJob
|
6
6
|
class Scheduler
|
7
|
-
|
7
|
+
MAX_THREADS = Concurrent.processor_count
|
8
8
|
|
9
9
|
DEFAULT_TIMER_OPTIONS = {
|
10
10
|
execution_interval: 1,
|
@@ -12,16 +12,14 @@ module GoodJob
|
|
12
12
|
run_now: true
|
13
13
|
}.freeze
|
14
14
|
|
15
|
-
MAX_THREADS = Concurrent.processor_count
|
16
|
-
|
17
15
|
DEFAULT_POOL_OPTIONS = {
|
18
|
-
name:
|
19
|
-
min_threads:
|
20
|
-
max_threads:
|
21
|
-
auto_terminate:
|
22
|
-
idletime:
|
23
|
-
max_queue:
|
24
|
-
fallback_policy:
|
16
|
+
name: 'good_job',
|
17
|
+
min_threads: 0,
|
18
|
+
max_threads: MAX_THREADS,
|
19
|
+
auto_terminate: true,
|
20
|
+
idletime: 0,
|
21
|
+
max_queue: 0,
|
22
|
+
fallback_policy: :abort # shouldn't matter -- 0 max queue
|
25
23
|
}.freeze
|
26
24
|
|
27
25
|
def initialize(query = GoodJob::Job.all, **options)
|
@@ -30,13 +28,16 @@ module GoodJob
|
|
30
28
|
@pool = Concurrent::ThreadPoolExecutor.new(DEFAULT_POOL_OPTIONS)
|
31
29
|
@timer = Concurrent::TimerTask.new(DEFAULT_TIMER_OPTIONS) do
|
32
30
|
idle_threads = @pool.max_length - @pool.length
|
33
|
-
puts "There are idle_threads: #{idle_threads}"
|
34
31
|
create_thread if idle_threads.positive?
|
35
|
-
true
|
36
32
|
end
|
33
|
+
@timer.add_observer(TimerObserver.new)
|
37
34
|
@timer.execute
|
38
35
|
end
|
39
36
|
|
37
|
+
def ordered_query
|
38
|
+
@query.where("scheduled_at < ?", Time.current).or(@query.where(scheduled_at: nil)).order(priority: :desc)
|
39
|
+
end
|
40
|
+
|
40
41
|
def execute
|
41
42
|
end
|
42
43
|
|
@@ -53,36 +54,31 @@ module GoodJob
|
|
53
54
|
end
|
54
55
|
|
55
56
|
def create_thread
|
56
|
-
future = Concurrent::Future.new(args: [
|
57
|
+
future = Concurrent::Future.new(args: [ordered_query], executor: @pool) do |query|
|
57
58
|
Rails.application.executor.wrap do
|
58
|
-
|
59
|
-
|
60
|
-
puts "Executing job #{job.id} in thread #{thread_name}"
|
59
|
+
while good_job = query.with_advisory_lock.first
|
60
|
+
ActiveSupport::Notifications.instrument("job_started.good_job", { good_job: good_job })
|
61
61
|
|
62
|
-
JobWrapper.new(
|
62
|
+
JobWrapper.new(good_job).perform
|
63
63
|
|
64
|
-
|
64
|
+
good_job.advisory_unlock
|
65
65
|
end
|
66
|
-
true
|
67
66
|
end
|
67
|
+
true
|
68
68
|
end
|
69
|
-
future.add_observer(TaskObserver.new
|
69
|
+
future.add_observer(TaskObserver.new)
|
70
70
|
future.execute
|
71
71
|
end
|
72
72
|
|
73
|
-
class
|
74
|
-
def
|
75
|
-
|
73
|
+
class TimerObserver
|
74
|
+
def update(time, result, error)
|
75
|
+
ActiveSupport::Notifications.instrument("timer_task_finished.good_job", { result: result, error: error, time: time })
|
76
76
|
end
|
77
|
+
end
|
77
78
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
elsif ex.is_a?(Concurrent::TimeoutError)
|
82
|
-
puts "(#{time}) Execution timed out\n"
|
83
|
-
else
|
84
|
-
puts "(#{time}) Execution failed with error #{result} #{ex}\n"
|
85
|
-
end
|
79
|
+
class TaskObserver
|
80
|
+
def update(time, result, error)
|
81
|
+
ActiveSupport::Notifications.instrument("job_finished.good_job", { result: result, error: error, time: time })
|
86
82
|
end
|
87
83
|
end
|
88
84
|
end
|
data/lib/good_job/version.rb
CHANGED
data/lib/good_job.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require "rails"
|
2
|
+
require 'good_job/railtie'
|
2
3
|
|
4
|
+
require 'good_job/logging'
|
3
5
|
require 'good_job/lockable'
|
4
6
|
require 'good_job/job'
|
5
7
|
require 'good_job/inline_scheduler'
|
@@ -8,5 +10,7 @@ require "good_job/job_wrapper"
|
|
8
10
|
require 'good_job/adapter'
|
9
11
|
|
10
12
|
module GoodJob
|
11
|
-
|
13
|
+
include Logging
|
14
|
+
|
15
|
+
ActiveSupport.run_load_hooks(:good_job, self)
|
12
16
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: good_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Sheldon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-03-
|
11
|
+
date: 2020-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: github_changelog_generator
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: pg
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -126,6 +140,7 @@ files:
|
|
126
140
|
- lib/good_job/job.rb
|
127
141
|
- lib/good_job/job_wrapper.rb
|
128
142
|
- lib/good_job/lockable.rb
|
143
|
+
- lib/good_job/logging.rb
|
129
144
|
- lib/good_job/railtie.rb
|
130
145
|
- lib/good_job/scheduler.rb
|
131
146
|
- lib/good_job/version.rb
|