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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9604c74cf4073b178401ec0cb606e55732f087464058bebe968b666c31c242d4
4
- data.tar.gz: f4af6118d55f231d15c3ffa2a3aac9f625911889a4f1adce5c14dcb2626af4d5
3
+ metadata.gz: 2c0fc31238c6d66b5b9ed3ddc0d4b9393a0a5a2cca85c7fcd0f2f24650892675
4
+ data.tar.gz: d6484d63b86eb7bc748b308cbfebe1ca7798fa1fa77d9542754dc99504f12244
5
5
  SHA512:
6
- metadata.gz: 46e92a0efba3937d274942ea55371f65cc3aa0451bc625bff6856be09525446960d192473548820bf1c289713151ee82e5cf1bdef21252da6a0927baafe5cc9c
7
- data.tar.gz: 9e1d108190398d5d698713e12ba981d63ee121f095185990f1f0963bee36e4f4760a22c9f64f8148624ba6be931a89943e48a601c4395ad825254d5d23c86c58
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
- And then add to the newly created file:
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).
@@ -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
- @scheduler.enqueue(good_job) if inline?
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
@@ -1,4 +1,7 @@
1
1
  module GoodJob
2
2
  class Railtie < ::Rails::Railtie
3
+ initializer "good_job.logger" do
4
+ ActiveSupport.on_load(:good_job) { self.logger = ::Rails.logger }
5
+ end
3
6
  end
4
7
  end
@@ -4,7 +4,7 @@ require "concurrent/utility/processor_counter"
4
4
 
5
5
  module GoodJob
6
6
  class Scheduler
7
- MINIMUM_EXECUTION_INTERVAL = 0.1
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: 'good_job',
19
- min_threads: 0,
20
- max_threads: MAX_THREADS,
21
- auto_terminate: true,
22
- idletime: 0,
23
- max_queue: 0,
24
- fallback_policy: :abort # shouldn't matter -- 0 max queue
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: [@query], executor: @pool) do |query|
57
+ future = Concurrent::Future.new(args: [ordered_query], executor: @pool) do |query|
57
58
  Rails.application.executor.wrap do
58
- thread_name = Thread.current.name || Thread.current.object_id
59
- while job = query.with_advisory_lock.first
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(job).perform
62
+ JobWrapper.new(good_job).perform
63
63
 
64
- job.advisory_unlock
64
+ good_job.advisory_unlock
65
65
  end
66
- true
67
66
  end
67
+ true
68
68
  end
69
- future.add_observer(TaskObserver.new(self))
69
+ future.add_observer(TaskObserver.new)
70
70
  future.execute
71
71
  end
72
72
 
73
- class TaskObserver
74
- def initialize(scheduler)
75
- @scheduler = scheduler
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
- def update(time, result, ex)
79
- if result
80
- puts "(#{time}) Execution successfully returned #{result}\n"
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
@@ -1,3 +1,3 @@
1
1
  module GoodJob
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
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
- # Your code goes here...
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.1.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-03 00:00:00.000000000 Z
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