good_job 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|