good_job 2.11.1 → 2.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c72b452a727d8f42ffb1a261669eb55a684ae5b9643d0f5660db7585d69416af
4
- data.tar.gz: fe50c11e3b2c5c5aca4f86dadbfc8ab8c8bf3bd3f8898fccd25e4fb8ca47a151
3
+ metadata.gz: 398864eebf8f617924967bf2a73679bd0584493299570c20ea6dbc9598d23ff3
4
+ data.tar.gz: d781885d188ec4b3dab51f9609ba7a3935027426edf6cbf8147ae2b11a51ac72
5
5
  SHA512:
6
- metadata.gz: ead055bb27d8a25f3c199d731d6ad56390ac934a85dae640fb4aa2d35baae8aec0b0cb0e30354b2972591b69269caf30574df60d8ecbd3dca445170dd0e1598a
7
- data.tar.gz: 396f2a76423df8033811186c993fad03007e25a1aaa85b3565b375d1a89b9934bcc4c42ebb7eda1fb1f5b00d89452a4315e642e155df4328a13fc25ec044f60e
6
+ metadata.gz: e9830078953d473bb48ec24cfcd4841029614be85c910b493d670eb1cfbbad287e95ce854a65aec609dd97954d6bb19f3a30c6a95cfd5d18c6913d777bab4d0f
7
+ data.tar.gz: ba0beb2c46d7f7044d1a7f2d5b1cb006ee67433432fb18e7bb04c7d4452df2e10ad45eff884c66bd7c1069ec47fee4fa18d776814dde4adf5b8a5bb4fb722904
data/CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # Changelog
2
2
 
3
+ ## [v2.12.0](https://github.com/bensheldon/good_job/tree/v2.12.0) (2022-04-05)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.11.3...v2.12.0)
6
+
7
+ **Closed issues:**
8
+
9
+ - TimeTask timeouts are now ignored as these were not able to be implemented correctly [\#555](https://github.com/bensheldon/good_job/issues/555)
10
+ - undefined method `relative\_time' when include\_all\_helpers is false [\#550](https://github.com/bensheldon/good_job/issues/550)
11
+ - ArgumentError: wrong number of arguments \(given 1, expected 0; required keyword: schedule\) - cron [\#546](https://github.com/bensheldon/good_job/issues/546)
12
+
13
+ **Merged pull requests:**
14
+
15
+ - Deprecate Adapter configuration of job execution/cron [\#558](https://github.com/bensheldon/good_job/pull/558) ([bensheldon](https://github.com/bensheldon))
16
+ - Remove usage of Concurrent::TimerTask's timeout\_interval [\#557](https://github.com/bensheldon/good_job/pull/557) ([bensheldon](https://github.com/bensheldon))
17
+ - Include locale in html lang attribute [\#556](https://github.com/bensheldon/good_job/pull/556) ([bensheldon](https://github.com/bensheldon))
18
+ - Rename `GoodJob::BaseController` to `GoodJob::ApplicationController` [\#553](https://github.com/bensheldon/good_job/pull/553) ([shouichi](https://github.com/shouichi))
19
+ - Internationalize/I18n the Dashboard Engine [\#497](https://github.com/bensheldon/good_job/pull/497) ([JuanVqz](https://github.com/JuanVqz))
20
+
21
+ ## [v2.11.3](https://github.com/bensheldon/good_job/tree/v2.11.3) (2022-03-30)
22
+
23
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.11.2...v2.11.3)
24
+
25
+ **Fixed bugs:**
26
+
27
+ - Add explicit `kwargs:` key to cron configuration [\#548](https://github.com/bensheldon/good_job/pull/548) ([bensheldon](https://github.com/bensheldon))
28
+
29
+ **Closed issues:**
30
+
31
+ - How to run clean up preserved jobs in cron? [\#541](https://github.com/bensheldon/good_job/issues/541)
32
+ - Erroring with "Too many open files" when good\_job tries reconnecting to database [\#530](https://github.com/bensheldon/good_job/issues/530)
33
+ - Can't cast Array [\#529](https://github.com/bensheldon/good_job/issues/529)
34
+
35
+ **Merged pull requests:**
36
+
37
+ - Use bundle add instead [\#542](https://github.com/bensheldon/good_job/pull/542) ([glaucocustodio](https://github.com/glaucocustodio))
38
+ - Update Readme to better explain queues, pools, threads, and database connections; update CLI to frontload queue option [\#539](https://github.com/bensheldon/good_job/pull/539) ([bensheldon](https://github.com/bensheldon))
39
+
40
+ ## [v2.11.2](https://github.com/bensheldon/good_job/tree/v2.11.2) (2022-03-03)
41
+
42
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.11.1...v2.11.2)
43
+
44
+ **Closed issues:**
45
+
46
+ - Best practices in deploying and monitoring a queue [\#523](https://github.com/bensheldon/good_job/issues/523)
47
+
48
+ **Merged pull requests:**
49
+
50
+ - Wrap Rspec before and example blocks with a mutex for JRuby [\#537](https://github.com/bensheldon/good_job/pull/537) ([bensheldon](https://github.com/bensheldon))
51
+ - Delegate `ActiveJobJob.table_name` to `Execution` and prevent it from being directly assignable [\#536](https://github.com/bensheldon/good_job/pull/536) ([bensheldon](https://github.com/bensheldon))
52
+ - Enable DB table names customization [\#535](https://github.com/bensheldon/good_job/pull/535) ([dimvic](https://github.com/dimvic))
53
+ - Added a chapter on how to prepare for production. [\#525](https://github.com/bensheldon/good_job/pull/525) ([stas](https://github.com/stas))
54
+
3
55
  ## [v2.11.1](https://github.com/bensheldon/good_job/tree/v2.11.1) (2022-03-01)
4
56
 
5
57
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v2.11.0...v2.11.1)
data/README.md CHANGED
@@ -52,6 +52,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
52
52
  - [Timeouts](#timeouts)
53
53
  - [Optimize queues, threads, and processes](#optimize-queues-threads-and-processes)
54
54
  - [Database connections](#database-connections)
55
+ - [Production setup](#production-setup)
55
56
  - [Execute jobs async / in-process](#execute-jobs-async--in-process)
56
57
  - [Migrate to GoodJob from a different ActiveJob backend](#migrate-to-goodjob-from-a-different-activejob-backend)
57
58
  - [Monitor and preserve worked jobs](#monitor-and-preserve-worked-jobs)
@@ -64,16 +65,10 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
64
65
 
65
66
  ## Set up
66
67
 
67
- 1. Add `good_job` to your application's Gemfile:
68
+ 1. Add `good_job` to your application's Gemfile and install the gem:
68
69
 
69
- ```ruby
70
- gem 'good_job'
71
- ```
72
-
73
- 1. Install the gem:
74
-
75
- ```bash
76
- bundle install
70
+ ```sh
71
+ bundle add good_job
77
72
  ```
78
73
 
79
74
  1. Run the GoodJob install generator. This will generate a database migration to create a table for GoodJob's job records:
@@ -164,8 +159,8 @@ Usage:
164
159
  good_job start
165
160
 
166
161
  Options:
167
- [--max-threads=COUNT] # Maximum number of threads to use for working jobs. (env var: GOOD_JOB_MAX_THREADS, default: 5)
168
- [--queues=QUEUE_LIST] # Queues to work from. (env var: GOOD_JOB_QUEUES, default: *)
162
+ [--queues=QUEUE_LIST] # Queues or pools to work from. (env var: GOOD_JOB_QUEUES, default: *)
163
+ [--max-threads=COUNT] # Default number of threads per pool to use for working jobs. (env var: GOOD_JOB_MAX_THREADS, default: 5)
169
164
  [--poll-interval=SECONDS] # Interval between polls for available jobs in seconds (env var: GOOD_JOB_POLL_INTERVAL, default: 1)
170
165
  [--max-cache=COUNT] # Maximum number of scheduled jobs to cache in memory (env var: GOOD_JOB_MAX_CACHE, default: 10000)
171
166
  [--shutdown-timeout=SECONDS] # Number of seconds to wait for jobs to finish when shutting down before stopping the thread. (env var: GOOD_JOB_SHUTDOWN_TIMEOUT, default: -1 (forever))
@@ -229,12 +224,12 @@ Rails.application.configure do
229
224
  config.good_job.retry_on_unhandled_error = false
230
225
  config.good_job.on_thread_error = -> (exception) { Raven.capture_exception(exception) }
231
226
  config.good_job.execution_mode = :async
227
+ config.good_job.queues = '*'
232
228
  config.good_job.max_threads = 5
233
229
  config.good_job.poll_interval = 30 # seconds
234
230
  config.good_job.shutdown_timeout = 25 # seconds
235
231
  config.good_job.enable_cron = true
236
232
  config.good_job.cron = { example: { cron: '0 * * * *', class: 'ExampleJob' } }
237
- config.good_job.queues = '*'
238
233
 
239
234
  # ...or all at once.
240
235
  config.good_job = {
@@ -242,6 +237,7 @@ Rails.application.configure do
242
237
  retry_on_unhandled_error: false,
243
238
  on_thread_error: -> (exception) { Raven.capture_exception(exception) },
244
239
  execution_mode: :async,
240
+ queues: '*',
245
241
  max_threads: 5,
246
242
  poll_interval: 30,
247
243
  shutdown_timeout: 25,
@@ -252,7 +248,6 @@ Rails.application.configure do
252
248
  class: 'ExampleJob'
253
249
  },
254
250
  },
255
- queues: '*',
256
251
  }
257
252
  end
258
253
  ```
@@ -264,8 +259,8 @@ Available configuration options are:
264
259
  - `:external` causes the adapter to enqueue jobs, but not execute them. When using this option (the default for production environments), you’ll need to use the command-line tool to actually execute your jobs.
265
260
  - `:async` (or `:async_server`) executes jobs in separate threads within the Rails web server process (`bundle exec rails server`). It can be more economical for small workloads because you don’t need a separate machine or environment for running your jobs, but if your web server is under heavy load or your jobs require a lot of resources, you should choose `:external` instead. When not in the Rails web server, jobs will execute in `:external` mode to ensure jobs are not executed within `rails console`, `rails db:migrate`, `rails assets:prepare`, etc.
266
261
  - `:async_all` executes jobs in separate threads in _any_ Rails process.
267
- - `max_threads` (integer) sets the maximum number of threads to use when `execution_mode` is set to `:async`. You can also set this with the environment variable `GOOD_JOB_MAX_THREADS`.
268
- - `queues` (string) determines which queues to execute jobs from when `execution_mode` is set to `:async`. See the description of `good_job start` for more details on the format of this string. You can also set this with the environment variable `GOOD_JOB_QUEUES`.
262
+ - `queues` (string) sets queues or pools to execute jobs. You can also set this with the environment variable `GOOD_JOB_QUEUES`.
263
+ - `max_threads` (integer) sets the default number of threads per pool to use for working jobs. You can also set this with the environment variable `GOOD_JOB_MAX_THREADS`.
269
264
  - `poll_interval` (integer) sets the number of seconds between polls for jobs when `execution_mode` is set to `:async`. You can also set this with the environment variable `GOOD_JOB_POLL_INTERVAL`. A poll interval of `-1` disables polling completely.
270
265
  - `max_cache` (integer) sets the maximum number of scheduled jobs that will be stored in memory to reduce execution latency when also polling for scheduled jobs. Caching 10,000 scheduled jobs uses approximately 20MB of memory. You can also set this with the environment variable `GOOD_JOB_MAX_CACHE`.
271
266
  - `shutdown_timeout` (float) number of seconds to wait for jobs to finish when shutting down before stopping the thread. Defaults to forever: `-1`. You can also set this with the environment variable `GOOD_JOB_SHUTDOWN_TIMEOUT`.
@@ -441,7 +436,8 @@ config.good_job.cron = {
441
436
  frequent_task: { # each recurring job must have a unique key
442
437
  cron: "*/15 * * * *", # cron-style scheduling format by fugit gem
443
438
  class: "ExampleJob", # reference the Job class with a string
444
- args: [42, { name: "Alice" }], # arguments to pass; can also be a proc e.g. `-> { { when: Time.now } }`
439
+ args: [42, "life"], # positional arguments to pass; can also be a proc e.g. `-> { [Time.now] }`
440
+ kwargs: { name: "Alice" }, # keyword arguments to pass; can also be a proc e.g. `-> { { name: NAMES.sample } }`
445
441
  set: { priority: -10 }, # additional ActiveJob properties; can also be a lambda/proc e.g. `-> { { priority: [1,2].sample } }`
446
442
  description: "Something helpful", # optional description that appears in Dashboard (coming soon!)
447
443
  },
@@ -674,13 +670,38 @@ Keep in mind, queue operations and management is an advanced discipline. This st
674
670
 
675
671
  ### Database connections
676
672
 
677
- Each GoodJob execution thread requires its own database connection that is automatically checked out from Rails’ connection pool. _Allowing GoodJob to create more threads than available database connections can lead to timeouts and is not recommended._ For example:
673
+ Each GoodJob execution thread requires its own database connection that is automatically checked out from Rails’ connection pool. For example:
678
674
 
679
675
  ```yaml
680
676
  # config/database.yml
681
- pool: <%= [ENV.fetch("RAILS_MAX_THREADS", 5).to_i, ENV.fetch("GOOD_JOB_MAX_THREADS", 4).to_i].max %>
677
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", 5).to_i + 3 + (ENV.fetch("GOOD_JOB_MAX_THREADS", 5).to_i %>
682
678
  ```
683
679
 
680
+ To calculate the total number of the database connections you'll need:
681
+
682
+ - 1 connection dedicated to the scheduler aka `LISTEN/NOTIFY`
683
+ - 1 connection per query pool thread e.g. `--queues=mice:2;elephants:1` is 3 threads. Pool thread size defaults to `--max-threads`
684
+ - (optional) 2 connections for Cron scheduler if you're running it
685
+ - (optional) 1 connection per subthread, if your application makes multithreaded database queries within a job
686
+ - When running `:async`, you must also add the number of threads by the webserver
687
+
688
+ The queue process will not crash if the connections pool is exhausted, instead it will report an exception (eg. `ActiveRecord::ConnectionTimeoutError`).
689
+
690
+ #### Production setup
691
+
692
+ When running GoodJob in a production environment, you should be mindful of:
693
+
694
+ - [Execution mode](execute-jobs-async--in-process)
695
+ - [Database connection pool size](#database-connections)
696
+ - [Health check probes](#cli-http-health-check-probes) and potentially the [instrumentation support](#monitor-and-preserve-worked-jobs)
697
+
698
+ The recommended way to monitor the queue in production is:
699
+
700
+ - have an exception notifier callback (see `on_thread_error`)
701
+ - if possible, run the queue as a dedicated instance and use available HTTP health check probes instead of pid-based monitoring
702
+ - keep an eye on the number of jobs in the queue (abnormal high number of unscheduled jobs means the queue could be underperforming)
703
+ - consider performance monitoring services which support the built-in Rails instrumentation (eg. Sentry, Skylight, etc.)
704
+
684
705
  ### Execute jobs async / in-process
685
706
 
686
707
  GoodJob can execute jobs "async" in the same process as the web server (e.g. `bin/rails s`). GoodJob's async execution mode offers benefits of economy by not requiring a separate job worker process, but with the tradeoff of increased complexity. Async mode can be configured in two ways:
@@ -9,6 +9,7 @@ module GoodJob
9
9
  def data
10
10
  end_time = Time.current
11
11
  start_time = end_time - 1.day
12
+ table_name = GoodJob::ActiveJobJob.table_name
12
13
 
13
14
  count_query = Arel.sql(GoodJob::Execution.pg_or_jdbc_query(<<~SQL.squish))
14
15
  SELECT *
@@ -23,7 +24,7 @@ module GoodJob
23
24
  queue_name,
24
25
  count(*) AS count
25
26
  FROM (
26
- #{@filter.filtered_query.except(:select, :order).select('queue_name', 'COALESCE(good_jobs.scheduled_at, good_jobs.created_at)::timestamp AS scheduled_at').to_sql}
27
+ #{@filter.filtered_query.except(:select, :order).select('queue_name', "COALESCE(#{table_name}.scheduled_at, #{table_name}.created_at)::timestamp AS scheduled_at").to_sql}
27
28
  ) sources
28
29
  GROUP BY date_trunc('hour', scheduled_at), queue_name
29
30
  ) sources ON sources.scheduled_at = timestamp
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
- class BaseController < ActionController::Base # rubocop:disable Rails/ApplicationController
3
+ class ApplicationController < ActionController::Base
4
4
  protect_from_forgery with: :exception
5
5
 
6
6
  around_action :switch_locale
@@ -24,10 +24,28 @@ module GoodJob
24
24
  request.content_security_policy_nonce_generator = ->(_request) { SecureRandom.base64(16) }
25
25
  end
26
26
 
27
+ def default_url_options(options = {})
28
+ { locale: I18n.locale }.merge(options)
29
+ end
30
+
27
31
  private
28
32
 
29
33
  def switch_locale(&action)
30
- I18n.with_locale(:en, &action)
34
+ I18n.with_locale(current_locale, &action)
35
+ end
36
+
37
+ def current_locale
38
+ if params[:locale]
39
+ params[:locale]
40
+ elsif good_job_available_locales.exclude?(I18n.default_locale) && I18n.available_locales.include?(:en)
41
+ :en
42
+ else
43
+ I18n.default_locale
44
+ end
45
+ end
46
+
47
+ def good_job_available_locales
48
+ @_good_job_available_locales ||= GoodJob::Engine.root.join("config/locales").glob("*.yml").map { |path| File.basename(path, ".yml").to_sym }.uniq
31
49
  end
32
50
  end
33
51
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
- class CronEntriesController < GoodJob::BaseController
3
+ class CronEntriesController < GoodJob::ApplicationController
4
4
  def index
5
5
  @cron_entries = CronEntry.all
6
6
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
- class ExecutionsController < GoodJob::BaseController
3
+ class ExecutionsController < GoodJob::ApplicationController
4
4
  def index
5
5
  @filter = ExecutionsFilter.new(params)
6
6
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
- class JobsController < GoodJob::BaseController
3
+ class JobsController < GoodJob::ApplicationController
4
4
  rescue_from GoodJob::ActiveJobJob::AdapterNotGoodJobError,
5
5
  GoodJob::ActiveJobJob::ActionForStateMismatchError,
6
6
  with: :redirect_on_error
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
- class ProcessesController < GoodJob::BaseController
3
+ class ProcessesController < GoodJob::ApplicationController
4
4
  def index
5
5
  @processes = GoodJob::Process.active.order(created_at: :desc) if GoodJob::Process.migrated?
6
6
  end
@@ -14,7 +14,7 @@ module GoodJob
14
14
 
15
15
  def filtered_query
16
16
  query = base_query.includes(:executions)
17
- .joins_advisory_locks.select('good_jobs.*', 'pg_locks.locktype AS locktype')
17
+ .joins_advisory_locks.select("#{GoodJob::ActiveJobJob.table_name}.*", 'pg_locks.locktype AS locktype')
18
18
 
19
19
  query = query.job_class(params[:job_class]) if params[:job_class].present?
20
20
  query = query.where(queue_name: params[:queue_name]) if params[:queue_name].present?
@@ -31,7 +31,7 @@ module GoodJob
31
31
  when 'scheduled'
32
32
  query = query.scheduled
33
33
  when 'running'
34
- query = query.running.select('good_jobs.*', 'pg_locks.locktype')
34
+ query = query.running.select("#{GoodJob::ActiveJobJob.table_name}.*", 'pg_locks.locktype')
35
35
  when 'queued'
36
36
  query = query.queued
37
37
  end
@@ -0,0 +1,13 @@
1
+ <% if notice %>
2
+ <div class="alert alert-success alert-dismissible fade show d-flex align-items-center offset-md-3 col-6" role="alert">
3
+ <%= render "good_job/shared/icons/check", class: "flex-shrink-0 me-2" %>
4
+ <div><%= notice %></div>
5
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
6
+ </div>
7
+ <% elsif alert %>
8
+ <div class="alert alert-warning alert-dismissible fade show d-flex align-items-center offset-md-3 col-6" role="alert">
9
+ <%= render "good_job/shared/icons/exclamation", class: "flex-shrink-0 me-2" %>
10
+ <div><%= alert %></div>
11
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
12
+ </div>
13
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <div class="card border-warning text-dark my-3">
2
+ <div class="card-body">
3
+ <p class="card-text">
4
+ <%= t(".work_in_progress_html") %>
5
+ </p>
6
+ </div>
7
+ </div>
@@ -0,0 +1,15 @@
1
+ <footer class="footer mt-auto py-3 bg-light fixed-bottom" id="footer" data-gj-poll-replace>
2
+ <div class="container-fluid">
3
+ <div class="row">
4
+ <div class="col-6">
5
+ <span class="text-muted">
6
+ <%= t(".last_update_html", time: Time.current.utc.iso8601) %>
7
+ </span>
8
+ </div>
9
+
10
+ <div class="col-6 text-end">
11
+ <%= t(".wording") %>
12
+ </div>
13
+ </div>
14
+ </div>
15
+ </footer>
@@ -0,0 +1,49 @@
1
+ <nav class="navbar navbar-expand-lg navbar-light bg-light">
2
+ <div class="container-fluid">
3
+ <%= link_to t(".name"), root_path, class: "navbar-brand mb-0 h1" %>
4
+ <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
5
+ <span class="navbar-toggler-icon"></span>
6
+ </button>
7
+
8
+ <div class="collapse navbar-collapse" id="navbarSupportedContent">
9
+ <ul class="navbar-nav me-auto">
10
+ <li class="nav-item">
11
+ <%= link_to t(".executions"), root_path, class: ["nav-link", ("active" if current_page?(root_path))] %>
12
+ </li>
13
+ <li class="nav-item">
14
+ <%= link_to t(".jobs"), jobs_path, class: ["nav-link", ("active" if current_page?(jobs_path))] %>
15
+ </li>
16
+ <li class="nav-item">
17
+ <%= link_to t(".cron_schedules"), cron_entries_path, class: ["nav-link", ("active" if current_page?(cron_entries_path))] %>
18
+ </li>
19
+ <li class="nav-item">
20
+ <%= link_to t(".processes"), processes_path, class: ["nav-link", ("active" if current_page?(processes_path))] %>
21
+ </li>
22
+ <li class="nav-item">
23
+ <div class="nav-link">
24
+ <span class="badge bg-secondary"><%= t(".coming_soon") %></span>
25
+ </div>
26
+ </li>
27
+ </ul>
28
+ <div class="nav-item pe-2">
29
+ <div class="form-check">
30
+ <input type="checkbox" id="toggle-poll" name="toggle-poll" data-gj-action='change#togglePoll' <%= 'checked' if params[:poll].present? %>>
31
+ <label for="toggle-poll"><%= t(".live_poll") %></label>
32
+ </div>
33
+ </div>
34
+ <ul class="navbar-nav">
35
+ <li class="nav-item dropdown">
36
+ <a href="#" class="nav-link dropdown-toggle" type="button" id="localeOptions" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
37
+ <%= I18n.locale %>
38
+ </a>
39
+
40
+ <ul class="dropdown-menu" aria-labelledby="localeOptions">
41
+ <% I18n.available_locales.reject { |locale| locale == I18n.locale }.each do |locale| %>
42
+ <li><%= link_to locale, url_for(locale: locale), class: "dropdown-item" %></li>
43
+ <% end %>
44
+ </ul>
45
+ </li>
46
+ </ul>
47
+ </div>
48
+ </div>
49
+ </nav>
@@ -0,0 +1,32 @@
1
+ <!DOCTYPE html>
2
+ <html lang="<%= I18n.locale %>">
3
+ <head>
4
+ <title>Good Job Dashboard</title>
5
+ <meta charset="utf-8">
6
+ <meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
7
+ <%= csrf_meta_tags %>
8
+ <%= csp_meta_tag %>
9
+
10
+ <%# Assets must use *_url route helpers to avoid being overriden by config.asset_host %>
11
+ <%= stylesheet_link_tag bootstrap_url(format: :css, v: GoodJob::VERSION), skip_pipeline: true %>
12
+ <%= stylesheet_link_tag style_url(format: :css, v: GoodJob::VERSION) %>
13
+
14
+ <%= javascript_include_tag bootstrap_url(format: :js, v: GoodJob::VERSION), nonce: true %>
15
+ <%= javascript_include_tag chartjs_url(format: :js, v: GoodJob::VERSION), nonce: true %>
16
+ <%= javascript_include_tag scripts_url(format: :js, v: GoodJob::VERSION), nonce: true %>
17
+
18
+ <%= javascript_include_tag rails_ujs_url(format: :js, v: GoodJob::VERSION), nonce: true %>
19
+ </head>
20
+ <body>
21
+ <%= render "good_job/shared/navbar" %>
22
+
23
+ <div class="container-fluid">
24
+ <%= render "good_job/shared/announcement" %>
25
+ <%= render "good_job/shared/alert" %>
26
+
27
+ <%= yield %>
28
+ </div>
29
+
30
+ <%= render "good_job/shared/footer" %>
31
+ </body>
32
+ </html>
@@ -0,0 +1,56 @@
1
+ ---
2
+ en:
3
+ datetime:
4
+ distance_in_words:
5
+ about_x_hours:
6
+ one: about 1 hour
7
+ other: about %{count} hours
8
+ about_x_months:
9
+ one: about 1 month
10
+ other: about %{count} months
11
+ about_x_years:
12
+ one: about 1 year
13
+ other: about %{count} years
14
+ almost_x_years:
15
+ one: almost 1 year
16
+ other: almost %{count} years
17
+ half_a_minute: half a minute
18
+ less_than_x_minutes:
19
+ one: less than a minute
20
+ other: less than %{count} minutes
21
+ less_than_x_seconds:
22
+ one: less than 1 second
23
+ other: less than %{count} seconds
24
+ over_x_years:
25
+ one: over 1 year
26
+ other: over %{count} years
27
+ x_days:
28
+ one: 1 day
29
+ other: "%{count} days"
30
+ x_minutes:
31
+ one: 1 minute
32
+ other: "%{count} minutes"
33
+ x_months:
34
+ one: 1 month
35
+ other: "%{count} months"
36
+ x_seconds:
37
+ one: 1 second
38
+ other: "%{count} seconds"
39
+ x_years:
40
+ one: 1 year
41
+ other: "%{count} years"
42
+ good_job:
43
+ shared:
44
+ announcement:
45
+ work_in_progress_html: "🚧 GoodJob's dashboard is a work in progress. Please contribute ideas and code on <a href=\"https://github.com/bensheldon/good_job/issues\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Github</a>."
46
+ footer:
47
+ last_update_html: Last updated <time id="page-updated-at" datetime="%{time}">%{time}</time>
48
+ wording: Remember, you're doing a Good Job too!
49
+ navbar:
50
+ coming_soon: More views coming soon
51
+ cron_schedules: Cron Schedules
52
+ executions: All Executions
53
+ jobs: All Jobs
54
+ live_poll: Live Poll
55
+ name: "GoodJob 👍"
56
+ processes: Processes
@@ -0,0 +1,56 @@
1
+ ---
2
+ es:
3
+ datetime:
4
+ distance_in_words:
5
+ about_x_hours:
6
+ one: alrededor de 1 hora
7
+ other: alrededor de %{count} horas
8
+ about_x_months:
9
+ one: alrededor de 1 mes
10
+ other: alrededor de %{count} meses
11
+ about_x_years:
12
+ one: alrededor de 1 año
13
+ other: alrededor de %{count} años
14
+ almost_x_years:
15
+ one: casi 1 año
16
+ other: casi %{count} años
17
+ half_a_minute: medio minuto
18
+ less_than_x_minutes:
19
+ one: menos de un minuto
20
+ other: menos de %{count} minutos
21
+ less_than_x_seconds:
22
+ one: menos de 1 segundo
23
+ other: menos de %{count} segundos
24
+ over_x_years:
25
+ one: más de 1 año
26
+ other: durante %{count} años
27
+ x_days:
28
+ one: 1 día
29
+ other: "%{count} días"
30
+ x_minutes:
31
+ one: 1 minuto
32
+ other: "%{count} minutos"
33
+ x_months:
34
+ one: 1 mes
35
+ other: "%{count} meses"
36
+ x_seconds:
37
+ one: 1 segundo
38
+ other: "%{count} segundos"
39
+ x_years:
40
+ one: 1 año
41
+ other: "%{count} años"
42
+ good_job:
43
+ shared:
44
+ announcement:
45
+ work_in_progress_html: "🚧 GoodJob se encuentra en desarrollo. Por favor contribuya en <a href=\"https://github.com/bensheldon/good_job/issues\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">GoodJob</a> con ideas y código."
46
+ footer:
47
+ last_update_html: Última actualización <time id="page-updated-at" datetime="%{time}">%{time}</time>
48
+ wording: "¡Recuerda, también tú estás haciendo un buen trabajo!"
49
+ navbar:
50
+ coming_soon: Próximamente más vistas
51
+ cron_schedules: Tareas Programadas
52
+ executions: Ejecuciones
53
+ jobs: Tareas
54
+ live_poll: En vivo
55
+ name: "GoodJob 👍"
56
+ processes: Procesos
@@ -15,7 +15,14 @@ module GoodJob
15
15
  # Attached to a Job's Execution when the Job is discarded.
16
16
  DiscardJobError = Class.new(StandardError)
17
17
 
18
- self.table_name = 'good_jobs'
18
+ class << self
19
+ delegate :table_name, to: Execution
20
+
21
+ def table_name=(_value)
22
+ raise NotImplementedError, 'Assign GoodJob::Execution.table_name directly'
23
+ end
24
+ end
25
+
19
26
  self.primary_key = 'active_job_id'
20
27
  self.advisory_lockable_column = 'active_job_id'
21
28
 
@@ -27,7 +27,13 @@ module GoodJob
27
27
  # @param queues [String, nil] determines which queues to execute jobs from when +execution_mode+ is set to +:async+. See {file:README.md#optimize-queues-threads-and-processes} for more details on the format of this string. You can also set this with the environment variable +GOOD_JOB_QUEUES+. Defaults to +"*"+.
28
28
  # @param poll_interval [Integer, nil] sets the number of seconds between polls for jobs when +execution_mode+ is set to +:async+. You can also set this with the environment variable +GOOD_JOB_POLL_INTERVAL+. Defaults to +1+.
29
29
  # @param start_async_on_initialize [Boolean] whether to start the async scheduler when the adapter is initialized.
30
- def initialize(execution_mode: nil, queues: nil, max_threads: nil, poll_interval: nil, start_async_on_initialize: GoodJob.async_ready?)
30
+ def initialize(execution_mode: nil, queues: nil, max_threads: nil, poll_interval: nil, start_async_on_initialize: nil)
31
+ if execution_mode || queues || max_threads || poll_interval || start_async_on_initialize
32
+ ActiveSupport::Deprecation.warn(
33
+ "The GoodJob::Adapter's initialization parameters have been deprecated and will be removed in GoodJob v3. These options should be configured through GoodJob global configuration instead."
34
+ )
35
+ end
36
+
31
37
  @configuration = GoodJob::Configuration.new(
32
38
  {
33
39
  execution_mode: execution_mode,
@@ -39,7 +45,7 @@ module GoodJob
39
45
  @configuration.validate!
40
46
  self.class.instances << self
41
47
 
42
- start_async if start_async_on_initialize
48
+ start_async if start_async_on_initialize || GoodJob.async_ready?
43
49
  end
44
50
 
45
51
  # Enqueues the ActiveJob job to be performed.
data/lib/good_job/cli.rb CHANGED
@@ -50,14 +50,14 @@ module GoodJob
50
50
  separate isolated execution pools with semicolons and threads with colons.
51
51
 
52
52
  DESCRIPTION
53
- method_option :max_threads,
54
- type: :numeric,
55
- banner: 'COUNT',
56
- desc: "Maximum number of threads to use for working jobs. (env var: GOOD_JOB_MAX_THREADS, default: 5)"
57
53
  method_option :queues,
58
54
  type: :string,
59
55
  banner: "QUEUE_LIST",
60
- desc: "Queues to work from. (env var: GOOD_JOB_QUEUES, default: *)"
56
+ desc: "Queues or queue pools to work from. (env var: GOOD_JOB_QUEUES, default: *)"
57
+ method_option :max_threads,
58
+ type: :numeric,
59
+ banner: 'COUNT',
60
+ desc: "Default number of threads per pool to use for working jobs. (env var: GOOD_JOB_MAX_THREADS, default: 5)"
61
61
  method_option :poll_interval,
62
62
  type: :numeric,
63
63
  banner: 'SECONDS',
@@ -52,6 +52,10 @@ module GoodJob # :nodoc:
52
52
  params[:args]
53
53
  end
54
54
 
55
+ def kwargs
56
+ params[:kwargs]
57
+ end
58
+
55
59
  def description
56
60
  params[:description]
57
61
  end
@@ -87,7 +91,8 @@ module GoodJob # :nodoc:
87
91
  current_thread.cron_key = key
88
92
  current_thread.cron_at = cron_at
89
93
 
90
- job_class.constantize.set(set_value).perform_later(*args_value)
94
+ configured_job = job_class.constantize.set(set_value)
95
+ kwargs_value.present? ? configured_job.perform_later(*args_value, **kwargs_value) : configured_job.perform_later(*args_value)
91
96
  end
92
97
  rescue ActiveRecord::RecordNotUnique
93
98
  false
@@ -124,6 +129,11 @@ module GoodJob # :nodoc:
124
129
  value.respond_to?(:call) ? value.call : value
125
130
  end
126
131
 
132
+ def kwargs_value
133
+ value = kwargs || nil
134
+ value.respond_to?(:call) ? value.call : value
135
+ end
136
+
127
137
  def display_property(value)
128
138
  case value
129
139
  when NilClass
@@ -6,13 +6,10 @@ module GoodJob # :nodoc:
6
6
  # Pollers regularly wake up execution threads to check for new work.
7
7
  #
8
8
  class Poller
9
- TIMEOUT_INTERVAL = 5
10
-
11
9
  # Defaults for instance of Concurrent::TimerTask.
12
10
  # The timer controls how and when sleeping threads check for new work.
13
11
  DEFAULT_TIMER_OPTIONS = {
14
12
  execution_interval: Configuration::DEFAULT_POLL_INTERVAL,
15
- timeout_interval: TIMEOUT_INTERVAL,
16
13
  run_now: true,
17
14
  }.freeze
18
15
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  # GoodJob gem version.
4
- VERSION = '2.11.1'
4
+ VERSION = '2.12.0'
5
5
  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: 2.11.1
4
+ version: 2.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Sheldon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-01 00:00:00.000000000 Z
11
+ date: 2022-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -367,8 +367,8 @@ files:
367
367
  - engine/app/assets/vendor/chartjs/chart.min.js
368
368
  - engine/app/assets/vendor/rails_ujs.js
369
369
  - engine/app/charts/good_job/scheduled_by_queue_chart.rb
370
+ - engine/app/controllers/good_job/application_controller.rb
370
371
  - engine/app/controllers/good_job/assets_controller.rb
371
- - engine/app/controllers/good_job/base_controller.rb
372
372
  - engine/app/controllers/good_job/cron_entries_controller.rb
373
373
  - engine/app/controllers/good_job/executions_controller.rb
374
374
  - engine/app/controllers/good_job/jobs_controller.rb
@@ -385,8 +385,12 @@ files:
385
385
  - engine/app/views/good_job/jobs/index.html.erb
386
386
  - engine/app/views/good_job/jobs/show.html.erb
387
387
  - engine/app/views/good_job/processes/index.html.erb
388
+ - engine/app/views/good_job/shared/_alert.erb
389
+ - engine/app/views/good_job/shared/_announcement.erb
388
390
  - engine/app/views/good_job/shared/_chart.erb
389
391
  - engine/app/views/good_job/shared/_filter.erb
392
+ - engine/app/views/good_job/shared/_footer.erb
393
+ - engine/app/views/good_job/shared/_navbar.erb
390
394
  - engine/app/views/good_job/shared/icons/_arrow_clockwise.html.erb
391
395
  - engine/app/views/good_job/shared/icons/_check.html.erb
392
396
  - engine/app/views/good_job/shared/icons/_exclamation.html.erb
@@ -394,7 +398,9 @@ files:
394
398
  - engine/app/views/good_job/shared/icons/_skip_forward.html.erb
395
399
  - engine/app/views/good_job/shared/icons/_stop.html.erb
396
400
  - engine/app/views/good_job/shared/icons/_trash.html.erb
397
- - engine/app/views/layouts/good_job/base.html.erb
401
+ - engine/app/views/layouts/good_job/application.html.erb
402
+ - engine/config/locales/en.yml
403
+ - engine/config/locales/es.yml
398
404
  - engine/config/routes.rb
399
405
  - engine/lib/good_job/engine.rb
400
406
  - exe/good_job
@@ -1,96 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <title>Good Job Dashboard</title>
5
- <meta charset="utf-8">
6
- <meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
7
- <%= csrf_meta_tags %>
8
- <%= csp_meta_tag %>
9
-
10
- <%# Assets must use *_url route helpers to avoid being overriden by config.asset_host %>
11
- <%= stylesheet_link_tag bootstrap_url(format: :css, v: GoodJob::VERSION), skip_pipeline: true %>
12
- <%= stylesheet_link_tag style_url(format: :css, v: GoodJob::VERSION) %>
13
-
14
- <%= javascript_include_tag bootstrap_url(format: :js, v: GoodJob::VERSION), nonce: true %>
15
- <%= javascript_include_tag chartjs_url(format: :js, v: GoodJob::VERSION), nonce: true %>
16
- <%= javascript_include_tag scripts_url(format: :js, v: GoodJob::VERSION), nonce: true %>
17
-
18
- <%= javascript_include_tag rails_ujs_url(format: :js, v: GoodJob::VERSION), nonce: true %>
19
- </head>
20
- <body>
21
- <nav class="navbar navbar-expand-lg navbar-light bg-light">
22
- <div class="container-fluid">
23
- <%= link_to "GoodJob 👍", root_path, class: 'navbar-brand mb-0 h1' %>
24
- <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
25
- <span class="navbar-toggler-icon"></span>
26
- </button>
27
-
28
- <div class="collapse navbar-collapse" id="navbarSupportedContent">
29
- <ul class="navbar-nav me-auto">
30
- <li class="nav-item">
31
- <%= link_to "All Executions", root_path, class: ["nav-link", ("active" if current_page?(root_path))] %>
32
- </li>
33
- <li class="nav-item">
34
- <%= link_to "All Jobs", jobs_path, class: ["nav-link", ("active" if current_page?(jobs_path))] %>
35
- </li>
36
- <li class="nav-item">
37
- <%= link_to "Cron Schedules", cron_entries_path, class: ["nav-link", ("active" if current_page?(cron_entries_path))] %>
38
- </li>
39
- <li class="nav-item">
40
- <%= link_to "Processes", processes_path, class: ["nav-link", ("active" if current_page?(processes_path))] %>
41
- </li>
42
- <li class="nav-item">
43
- <div class="nav-link">
44
- <span class="badge bg-secondary">More views coming soon</span>
45
- </div>
46
- </li>
47
- </ul>
48
- <div>
49
- <input type="checkbox" id="toggle-poll" name="toggle-poll" data-gj-action='change#togglePoll' <%= 'checked' if params[:poll].present? %>>
50
- <label for="toggle-poll">Live Poll</label>
51
- </div>
52
- </div>
53
- </div>
54
- </nav>
55
-
56
- <div class="container-fluid">
57
- <div class="card border-warning text-dark my-3">
58
- <div class="card-body">
59
- <p class="card-text">🚧 GoodJob's dashboard is a work in progress. Please contribute ideas and code on <a href="https://github.com/bensheldon/good_job/issues" target="_blank" rel="nofollow noopener noreferrer">Github</a>.</p>
60
- </div>
61
- </div>
62
-
63
- <% if notice %>
64
- <div class="alert alert-success alert-dismissible fade show d-flex align-items-center offset-md-3 col-6" role="alert">
65
- <%= render "good_job/shared/icons/check", class: "flex-shrink-0 me-2" %>
66
- <div><%= notice %></div>
67
- <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
68
- </div>
69
- <% elsif alert %>
70
- <div class="alert alert-warning alert-dismissible fade show d-flex align-items-center offset-md-3 col-6" role="alert">
71
- <%= render "good_job/shared/icons/exclamation", class: "flex-shrink-0 me-2" %>
72
- <div><%= alert %></div>
73
- <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
74
- </div>
75
- <% end %>
76
-
77
- <%= yield %>
78
- </div>
79
-
80
- <footer class="footer mt-auto py-3 bg-light fixed-bottom" id="footer" data-gj-poll-replace>
81
- <div class="container-fluid">
82
- <div class="row">
83
- <div class="col-6">
84
- <span class="text-muted">
85
- Last updated: <time id="page-updated-at" datetime="<%= Time.current.utc.iso8601 %>"><%= Time.current %></time>
86
- </span>
87
- </div>
88
-
89
- <div class="col-6 text-end">
90
- Remember, you're doing a Good Job too!
91
- </div>
92
- </div>
93
- </div>
94
- </footer>
95
- </body>
96
- </html>