good_job 2.6.0 → 2.6.1

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.
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GoodJob
4
+ class ScheduledByQueueChart
5
+ def initialize(filter)
6
+ @filter = filter
7
+ end
8
+
9
+ def data
10
+ end_time = Time.current
11
+ start_time = end_time - 1.day
12
+
13
+ count_query = Arel.sql(GoodJob::Execution.pg_or_jdbc_query(<<~SQL.squish))
14
+ SELECT *
15
+ FROM generate_series(
16
+ date_trunc('hour', $1::timestamp),
17
+ date_trunc('hour', $2::timestamp),
18
+ '1 hour'
19
+ ) timestamp
20
+ LEFT JOIN (
21
+ SELECT
22
+ date_trunc('hour', scheduled_at) AS scheduled_at,
23
+ queue_name,
24
+ count(*) AS count
25
+ 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
+ ) sources
28
+ GROUP BY date_trunc('hour', scheduled_at), queue_name
29
+ ) sources ON sources.scheduled_at = timestamp
30
+ ORDER BY timestamp ASC
31
+ SQL
32
+
33
+ binds = [[nil, start_time], [nil, end_time]]
34
+ executions_data = GoodJob::Execution.connection.exec_query(GoodJob::Execution.pg_or_jdbc_query(count_query), "GoodJob Dashboard Chart", binds)
35
+
36
+ queue_names = executions_data.reject { |d| d['count'].nil? }.map { |d| d['queue_name'] || BaseFilter::EMPTY }.uniq
37
+ labels = []
38
+ queues_data = executions_data.to_a.group_by { |d| d['timestamp'] }.each_with_object({}) do |(timestamp, values), hash|
39
+ labels << timestamp.in_time_zone.strftime('%H:%M')
40
+ queue_names.each do |queue_name|
41
+ (hash[queue_name] ||= []) << values.find { |d| d['queue_name'] == queue_name }&.[]('count')
42
+ end
43
+ end
44
+
45
+ {
46
+ labels: labels,
47
+ datasets: queues_data.map do |queue, data|
48
+ label = queue || '(none)'
49
+ {
50
+ label: label,
51
+ data: data,
52
+ backgroundColor: string_to_hsl(label),
53
+ borderColor: string_to_hsl(label),
54
+ }
55
+ end,
56
+ }
57
+ end
58
+
59
+ def string_to_hsl(string)
60
+ hash_value = string.sum
61
+
62
+ hue = hash_value % 360
63
+ saturation = (hash_value % 50) + 50
64
+ lightness = '50'
65
+
66
+ "hsl(#{hue}, #{saturation}%, #{lightness}%)"
67
+ end
68
+ end
69
+ end
@@ -15,18 +15,18 @@ module GoodJob
15
15
  render file: GoodJob::Engine.root.join("app", "assets", "vendor", "bootstrap", "bootstrap.bundle.min.js")
16
16
  end
17
17
 
18
- def chartist_css
19
- render file: GoodJob::Engine.root.join("app", "assets", "vendor", "chartist", "chartist.css")
20
- end
21
-
22
- def chartist_js
23
- render file: GoodJob::Engine.root.join("app", "assets", "vendor", "chartist", "chartist.js")
18
+ def chartjs_js
19
+ render file: GoodJob::Engine.root.join("app", "assets", "vendor", "chartjs", "chart.min.js")
24
20
  end
25
21
 
26
22
  def rails_ujs_js
27
23
  render file: GoodJob::Engine.root.join("app", "assets", "vendor", "rails_ujs.js")
28
24
  end
29
25
 
26
+ def scripts_js
27
+ render file: GoodJob::Engine.root.join("app", "assets", "scripts.js")
28
+ end
29
+
30
30
  def style_css
31
31
  render file: GoodJob::Engine.root.join("app", "assets", "style.css")
32
32
  end
@@ -2,6 +2,7 @@
2
2
  module GoodJob
3
3
  class BaseFilter
4
4
  DEFAULT_LIMIT = 25
5
+ EMPTY = '[none]'
5
6
 
6
7
  attr_accessor :params, :base_query
7
8
 
@@ -31,7 +32,7 @@ module GoodJob
31
32
 
32
33
  def queues
33
34
  base_query.group(:queue_name).count
34
- .sort_by { |name, _count| name.to_s }
35
+ .sort_by { |name, _count| name.to_s || EMPTY }
35
36
  .to_h
36
37
  end
37
38
 
@@ -39,58 +40,18 @@ module GoodJob
39
40
  raise NotImplementedError
40
41
  end
41
42
 
42
- def to_params(override)
43
+ def to_params(override = {})
43
44
  {
44
45
  job_class: params[:job_class],
45
46
  limit: params[:limit],
46
47
  queue_name: params[:queue_name],
48
+ query: params[:query],
47
49
  state: params[:state],
48
- }.merge(override).delete_if { |_, v| v.nil? }
50
+ }.merge(override).delete_if { |_, v| v.blank? }
49
51
  end
50
52
 
51
- def chart_data
52
- count_query = Arel.sql(GoodJob::Execution.pg_or_jdbc_query(<<~SQL.squish))
53
- SELECT *
54
- FROM generate_series(
55
- date_trunc('hour', $1::timestamp),
56
- date_trunc('hour', $2::timestamp),
57
- '1 hour'
58
- ) timestamp
59
- LEFT JOIN (
60
- SELECT
61
- date_trunc('hour', scheduled_at) AS scheduled_at,
62
- queue_name,
63
- count(*) AS count
64
- FROM (
65
- #{filtered_query.except(:select).select('queue_name', 'COALESCE(good_jobs.scheduled_at, good_jobs.created_at)::timestamp AS scheduled_at').to_sql}
66
- ) sources
67
- GROUP BY date_trunc('hour', scheduled_at), queue_name
68
- ) sources ON sources.scheduled_at = timestamp
69
- ORDER BY timestamp ASC
70
- SQL
71
-
72
- current_time = Time.current
73
- binds = [[nil, current_time - 1.day], [nil, current_time]]
74
- executions_data = GoodJob::Execution.connection.exec_query(count_query, "GoodJob Dashboard Chart", binds)
75
-
76
- queue_names = executions_data.map { |d| d['queue_name'] }.uniq
77
- labels = []
78
- queues_data = executions_data.to_a.group_by { |d| d['timestamp'] }.each_with_object({}) do |(timestamp, values), hash|
79
- labels << timestamp.in_time_zone.strftime('%H:%M %z')
80
- queue_names.each do |queue_name|
81
- (hash[queue_name] ||= []) << values.find { |d| d['queue_name'] == queue_name }&.[]('count')
82
- end
83
- end
84
-
85
- {
86
- labels: labels,
87
- series: queues_data.map do |queue, data|
88
- {
89
- name: queue,
90
- data: data,
91
- }
92
- end,
93
- }
53
+ def filtered_query
54
+ raise NotImplementedError
94
55
  end
95
56
 
96
57
  private
@@ -98,9 +59,5 @@ module GoodJob
98
59
  def default_base_query
99
60
  raise NotImplementedError
100
61
  end
101
-
102
- def filtered_query
103
- raise NotImplementedError
104
- end
105
62
  end
106
63
  end
@@ -10,16 +10,11 @@ module GoodJob
10
10
  }
11
11
  end
12
12
 
13
- private
14
-
15
- def default_base_query
16
- GoodJob::Execution.all
17
- end
18
-
19
13
  def filtered_query
20
14
  query = base_query
21
- query = query.job_class(params[:job_class]) if params[:job_class]
22
- query = query.where(queue_name: params[:queue_name]) if params[:queue_name]
15
+ query = query.job_class(params[:job_class]) if params[:job_class].present?
16
+ query = query.where(queue_name: params[:queue_name]) if params[:queue_name].present?
17
+ query = query.search(params['query']) if params[:query].present?
23
18
 
24
19
  if params[:state]
25
20
  case params[:state]
@@ -36,5 +31,11 @@ module GoodJob
36
31
 
37
32
  query
38
33
  end
34
+
35
+ private
36
+
37
+ def default_base_query
38
+ GoodJob::Execution.all
39
+ end
39
40
  end
40
41
  end
@@ -12,18 +12,13 @@ module GoodJob
12
12
  }
13
13
  end
14
14
 
15
- private
16
-
17
- def default_base_query
18
- GoodJob::ActiveJobJob.all
19
- end
20
-
21
15
  def filtered_query
22
16
  query = base_query.includes(:executions)
23
17
  .joins_advisory_locks.select('good_jobs.*', 'pg_locks.locktype AS locktype')
24
18
 
25
- query = query.job_class(params[:job_class]) if params[:job_class]
26
- query = query.where(queue_name: params[:queue_name]) if params[:queue_name]
19
+ query = query.job_class(params[:job_class]) if params[:job_class].present?
20
+ query = query.where(queue_name: params[:queue_name]) if params[:queue_name].present?
21
+ query = query.search(params['query']) if params[:query].present?
27
22
 
28
23
  if params[:state]
29
24
  case params[:state]
@@ -44,5 +39,11 @@ module GoodJob
44
39
 
45
40
  query
46
41
  end
42
+
43
+ private
44
+
45
+ def default_base_query
46
+ GoodJob::ActiveJobJob.all
47
+ end
47
48
  end
48
49
  end
@@ -1,5 +1,5 @@
1
1
  <div class="card my-3 p-6">
2
- <%= render 'good_job/shared/chart', chart_data: @filter.chart_data %>
2
+ <%= render 'good_job/shared/chart', chart_data: GoodJob::ScheduledByQueueChart.new(@filter).data %>
3
3
  </div>
4
4
 
5
5
  <%= render 'good_job/shared/filter', filter: @filter %>
@@ -22,7 +22,7 @@
22
22
  </thead>
23
23
  <tbody>
24
24
  <% jobs.each do |job| %>
25
- <tr id="<%= dom_id(job) %>">
25
+ <tr class="<%= dom_class(job) %>" id="<%= dom_id(job) %>">
26
26
  <td>
27
27
  <%= link_to job_path(job.id) do %>
28
28
  <code><%= job.id %></code>
@@ -1,5 +1,5 @@
1
1
  <div class="card my-3 p-6">
2
- <%= render 'good_job/shared/chart', chart_data: @filter.chart_data %>
2
+ <%= render 'good_job/shared/chart', chart_data: GoodJob::ScheduledByQueueChart.new(@filter).data %>
3
3
  </div>
4
4
 
5
5
  <%= render 'good_job/shared/filter', filter: @filter %>
@@ -1,52 +1,25 @@
1
- <div id="chart"></div>
1
+ <div class="chart-wrapper">
2
+ <canvas id="chart"></canvas>
3
+ </div>
2
4
 
3
5
  <%= javascript_tag nonce: true do %>
4
- new Chartist.Line('#chart', <%== chart_data.to_json %>, {
5
- height: '300px',
6
- fullWidth: true,
7
- chartPadding: {
8
- right: 40,
9
- top: 20,
10
- bottom: 20
6
+ const chartData = <%== chart_data.to_json %>;
7
+
8
+ const ctx = document.getElementById('chart').getContext('2d');
9
+ const chart = new Chart(ctx, {
10
+ type: 'line',
11
+ data: {
12
+ labels: chartData.labels,
13
+ datasets: chartData.datasets
11
14
  },
12
- axisX: {
13
- labelInterpolationFnc: function(value, index) {
14
- return index % 3 === 0 ? value : null;
15
+ options: {
16
+ responsive: true,
17
+ maintainAspectRatio: false,
18
+ scales: {
19
+ y: {
20
+ beginAtZero: true
21
+ }
15
22
  }
16
- },
17
- axisY: {
18
- low: 0,
19
- onlyInteger: true
20
23
  }
21
- })
22
-
23
- // https://www.smashingmagazine.com/2014/12/chartist-js-open-source-library-responsive-charts/
24
- const chartEl = document.getElementById('chart');
25
- const tooltipEl = document.createElement('div')
26
-
27
- tooltipEl.classList.add('tooltip', 'tooltip-hidden');
28
- chartEl.appendChild(tooltipEl);
29
-
30
- document.body.addEventListener('mouseenter', function (event) {
31
- if (!(event.target.matches && event.target.matches('.ct-point'))) return;
32
-
33
- const seriesName = event.target.closest('.ct-series').getAttribute('ct:series-name');
34
- const value = event.target.getAttribute('ct:value');
35
-
36
- tooltipEl.innerText = seriesName + ': ' + value;
37
- tooltipEl.classList.remove('tooltip-hidden');
38
- }, true);
39
-
40
- document.body.addEventListener('mouseleave', function (event) {
41
- if (!(event.target.matches && event.target.matches('.ct-point'))) return;
42
-
43
- tooltipEl.classList.add('tooltip-hidden');
44
- }, true);
45
-
46
- document.body.addEventListener('mousemove', function(event) {
47
- if (!(event.target.matches && event.target.matches('.ct-point'))) return;
48
-
49
- tooltipEl.style.left = (event.offsetX || event.originalEvent.layerX) + tooltipEl.offsetWidth + 10 + 'px';
50
- tooltipEl.style.top = (event.offsetY || event.originalEvent.layerY) + tooltipEl.offsetHeight - 20 + 'px';
51
- }, true);
24
+ });
52
25
  <% end %>
@@ -1,6 +1,6 @@
1
1
  <div class='card mb-2'>
2
2
  <div class='card-body d-flex flex-wrap'>
3
- <div class='me-4'>
3
+ <div class='mb-2 me-4'>
4
4
  <small>Filter by job class</small>
5
5
  <br>
6
6
  <% filter.job_classes.each do |name, count| %>
@@ -16,7 +16,7 @@
16
16
  <% end %>
17
17
  </div>
18
18
 
19
- <div class='me-4'>
19
+ <div class='mb-2 me-4'>
20
20
  <small>Filter by state</small>
21
21
  <br>
22
22
  <% filter.states.each do |name, count| %>
@@ -32,7 +32,7 @@
32
32
  <% end %>
33
33
  </div>
34
34
 
35
- <div>
35
+ <div class='mb-2 me-4'>
36
36
  <small>Filter by queue</small>
37
37
  <br>
38
38
  <% filter.queues.each do |name, count| %>
@@ -47,5 +47,20 @@
47
47
  <% end %>
48
48
  <% end %>
49
49
  </div>
50
+
51
+ <div class="mb-2">
52
+ <%= form_with(url: "", method: :get, local: true) do |form| %>
53
+ <% filter.to_params(query: nil).each do |key, value| %>
54
+ <%= form.hidden_field(key.to_sym, value: value) %>
55
+ <% end %>
56
+
57
+ <small><%= form.label :query, "Search" %></small>
58
+ <div class="input-group input-group-sm">
59
+ <%= form.search_field :query, value: params[:query], class: "form-control" %>
60
+ <%= form.button "Search", type: "submit", name: nil, class: "btn btn-sm btn-outline-secondary" %>
61
+ <%= link_to "Clear", filter.to_params(query: nil), class: "btn btn-sm btn-outline-secondary" %>
62
+ </div>
63
+ <% end %>
64
+ </div>
50
65
  </div>
51
66
  </div>
@@ -1,16 +1,17 @@
1
1
  <!DOCTYPE html>
2
- <html>
2
+ <html lang="en">
3
3
  <head>
4
4
  <title>Good Job Dashboard</title>
5
5
  <%= csrf_meta_tags %>
6
6
  <%= csp_meta_tag %>
7
7
 
8
8
  <%= stylesheet_link_tag bootstrap_path(format: :css, v: GoodJob::VERSION) %>
9
- <%= stylesheet_link_tag chartist_path(format: :css, v: GoodJob::VERSION) %>
10
9
  <%= stylesheet_link_tag style_path(format: :css, v: GoodJob::VERSION) %>
11
10
 
12
11
  <%= javascript_include_tag bootstrap_path(format: :js, v: GoodJob::VERSION) %>
13
- <%= javascript_include_tag chartist_path(format: :js, v: GoodJob::VERSION) %>
12
+ <%= javascript_include_tag chartjs_path(format: :js, v: GoodJob::VERSION) %>
13
+ <%= javascript_include_tag scripts_path(format: :js, v: GoodJob::VERSION) %>
14
+
14
15
  <%= javascript_include_tag rails_ujs_path(format: :js, v: GoodJob::VERSION) %>
15
16
  </head>
16
17
  <body>
@@ -20,14 +20,14 @@ GoodJob::Engine.routes.draw do
20
20
  scope controller: :assets do
21
21
  constraints(format: :css) do
22
22
  get :bootstrap, action: :bootstrap_css
23
- get :chartist, action: :chartist_css
24
23
  get :style, action: :style_css
25
24
  end
26
25
 
27
26
  constraints(format: :js) do
28
27
  get :bootstrap, action: :bootstrap_js
29
28
  get :rails_ujs, action: :rails_ujs_js
30
- get :chartist, action: :chartist_js
29
+ get :chartjs, action: :chartjs_js
30
+ get :scripts, action: :scripts_js
31
31
  end
32
32
  end
33
33
  end
@@ -8,7 +8,8 @@ module GoodJob
8
8
  # @!parse
9
9
  # class ActiveJob < ActiveRecord::Base; end
10
10
  class ActiveJobJob < Object.const_get(GoodJob.active_record_parent_class)
11
- include GoodJob::Lockable
11
+ include Filterable
12
+ include Lockable
12
13
 
13
14
  # Raised when an inappropriate action is applied to a Job based on its state.
14
15
  ActionForStateMismatchError = Class.new(StandardError)
@@ -47,24 +48,6 @@ module GoodJob
47
48
  # Errored but will not be retried
48
49
  scope :discarded, -> { where.not(finished_at: nil).where.not(error: nil) }
49
50
 
50
- # Get Jobs in display order with optional keyset pagination.
51
- # @!method display_all(after_scheduled_at: nil, after_id: nil)
52
- # @!scope class
53
- # @param after_scheduled_at [DateTime, String, nil]
54
- # Display records scheduled after this time for keyset pagination
55
- # @param after_id [Numeric, String, nil]
56
- # Display records after this ID for keyset pagination
57
- # @return [ActiveRecord::Relation]
58
- scope :display_all, (lambda do |after_scheduled_at: nil, after_id: nil|
59
- query = order(Arel.sql('COALESCE(scheduled_at, created_at) DESC, id DESC'))
60
- if after_scheduled_at.present? && after_id.present?
61
- query = query.where(Arel.sql('(COALESCE(scheduled_at, created_at), id) < (:after_scheduled_at, :after_id)'), after_scheduled_at: after_scheduled_at, after_id: after_id)
62
- elsif after_scheduled_at.present?
63
- query = query.where(Arel.sql('(COALESCE(scheduled_at, created_at)) < (:after_scheduled_at)'), after_scheduled_at: after_scheduled_at)
64
- end
65
- query
66
- end)
67
-
68
51
  # The job's ActiveJob UUID
69
52
  # @return [String]
70
53
  def id
@@ -6,6 +6,7 @@ module GoodJob
6
6
  # class Execution < ActiveRecord::Base; end
7
7
  class Execution < Object.const_get(GoodJob.active_record_parent_class)
8
8
  include Lockable
9
+ include Filterable
9
10
 
10
11
  # Raised if something attempts to execute a previously completed Execution again.
11
12
  PreviouslyPerformedError = Class.new(StandardError)
@@ -156,24 +157,6 @@ module GoodJob
156
157
  end
157
158
  end)
158
159
 
159
- # Get Jobs in display order with optional keyset pagination.
160
- # @!method display_all(after_scheduled_at: nil, after_id: nil)
161
- # @!scope class
162
- # @param after_scheduled_at [DateTime, String, nil]
163
- # Display records scheduled after this time for keyset pagination
164
- # @param after_id [Numeric, String, nil]
165
- # Display records after this ID for keyset pagination
166
- # @return [ActiveRecord::Relation]
167
- scope :display_all, (lambda do |after_scheduled_at: nil, after_id: nil|
168
- query = order(Arel.sql('COALESCE(scheduled_at, created_at) DESC, id DESC'))
169
- if after_scheduled_at.present? && after_id.present?
170
- query = query.where(Arel.sql('(COALESCE(scheduled_at, created_at), id) < (:after_scheduled_at, :after_id)'), after_scheduled_at: after_scheduled_at, after_id: after_id)
171
- elsif after_scheduled_at.present?
172
- query = query.where(Arel.sql('(COALESCE(scheduled_at, created_at)) < (:after_scheduled_at)'), after_scheduled_at: after_scheduled_at)
173
- end
174
- query
175
- end)
176
-
177
160
  # Finds the next eligible Execution, acquire an advisory lock related to it, and
178
161
  # executes the job.
179
162
  # @return [ExecutionResult, nil]
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ module GoodJob
3
+ # Shared methods for filtering Execution/Job records from the +good_jobs+ table.
4
+ module Filterable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ # Get records in display order with optional keyset pagination.
9
+ # @!method display_all(after_scheduled_at: nil, after_id: nil)
10
+ # @!scope class
11
+ # @param after_scheduled_at [DateTime, String, nil]
12
+ # Display records scheduled after this time for keyset pagination
13
+ # @param after_id [Numeric, String, nil]
14
+ # Display records after this ID for keyset pagination
15
+ # @return [ActiveRecord::Relation]
16
+ scope :display_all, (lambda do |after_scheduled_at: nil, after_id: nil|
17
+ query = order(Arel.sql('COALESCE(scheduled_at, created_at) DESC, id DESC'))
18
+ if after_scheduled_at.present? && after_id.present?
19
+ query = query.where(Arel.sql('(COALESCE(scheduled_at, created_at), id) < (:after_scheduled_at, :after_id)'), after_scheduled_at: after_scheduled_at, after_id: after_id)
20
+ elsif after_scheduled_at.present?
21
+ query = query.where(Arel.sql('(COALESCE(scheduled_at, created_at)) < (:after_scheduled_at)'), after_scheduled_at: after_scheduled_at)
22
+ end
23
+ query
24
+ end)
25
+
26
+ # Search records by text query.
27
+ # @!method search(query)
28
+ # @!scope class
29
+ # @param query [String]
30
+ # Search Query
31
+ # @return [ActiveRecord::Relation]
32
+ scope :search, (lambda do |query|
33
+ query = query.to_s.strip
34
+ next if query.blank?
35
+
36
+ tsvector = "(to_tsvector('english', serialized_params) || to_tsvector('english', id::text) || to_tsvector('english', COALESCE(error, '')::text))"
37
+ where("#{tsvector} @@ to_tsquery(?)", query)
38
+ .order(sanitize_sql_for_order([Arel.sql("ts_rank(#{tsvector}, to_tsquery(?))"), query]) => 'DESC')
39
+ end)
40
+ end
41
+ end
42
+ end
@@ -25,10 +25,17 @@ module GoodJob # :nodoc:
25
25
  max_queue: 1,
26
26
  fallback_policy: :discard,
27
27
  }.freeze
28
- # Seconds to wait if database cannot be connected to
29
- RECONNECT_INTERVAL = 5
30
28
  # Seconds to block while LISTENing for a message
31
29
  WAIT_INTERVAL = 1
30
+ # Seconds to wait if database cannot be connected to
31
+ RECONNECT_INTERVAL = 5
32
+ # Connection errors that will wait {RECONNECT_INTERVAL} before reconnecting
33
+ CONNECTION_ERRORS = %w[
34
+ ActiveRecord::ConnectionNotEstablished
35
+ ActiveRecord::StatementInvalid
36
+ PG::UnableToSend
37
+ PG::Error
38
+ ].freeze
32
39
 
33
40
  # @!attribute [r] instances
34
41
  # @!scope class
@@ -115,15 +122,18 @@ module GoodJob # :nodoc:
115
122
  if thread_error
116
123
  GoodJob.on_thread_error.call(thread_error) if GoodJob.on_thread_error.respond_to?(:call)
117
124
  ActiveSupport::Notifications.instrument("notifier_notify_error.good_job", { error: thread_error })
125
+
126
+ connection_error = CONNECTION_ERRORS.any? do |error_string|
127
+ error_class = error_string.safe_constantize
128
+ next unless error_class
129
+
130
+ thread_error.is_a? error_class
131
+ end
118
132
  end
119
133
 
120
134
  return if shutdown?
121
135
 
122
- if thread_error.is_a?(ActiveRecord::ConnectionNotEstablished) || thread_error.is_a?(ActiveRecord::StatementInvalid)
123
- listen(delay: RECONNECT_INTERVAL)
124
- else
125
- listen
126
- end
136
+ listen(delay: connection_error ? RECONNECT_INTERVAL : 0)
127
137
  end
128
138
 
129
139
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  # GoodJob gem version.
4
- VERSION = '2.6.0'
4
+ VERSION = '2.6.1'
5
5
  end