good_job 2.6.0 → 2.6.1

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