que 1.0.0.beta → 1.0.0.beta2

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
- SHA1:
3
- metadata.gz: 9bbd70fe386be02949156458e095b91fc7af2001
4
- data.tar.gz: d7029485d2eb220cad37d8b41ca8021a6089e934
2
+ SHA256:
3
+ metadata.gz: 8ad84e5065bf9a35a8ed97f1b905b62935a67bf3713d537c4601c1bb6bc48e7c
4
+ data.tar.gz: 6ced1d272b59a81854f6794147856a1f21bfe048316d767b27ee984935fa8300
5
5
  SHA512:
6
- metadata.gz: 94f36a7474bf38f0a06839f4ce7afc904bb855e828aff90891ccdfbf87f77e017bd823f3b92ed0bbce3ade75b931d4158c70ee1df3fd4d52d48554a8f7e2c74e
7
- data.tar.gz: e90625eb0de12115e779cd2f497db6b4348e26736959f6c4d7b8bd40b58b51c9b086c6f8acaf4569f31b120733648e6bb84cfe6a52dc1628167efead9a016250
6
+ metadata.gz: a103cc5cd43608bc66023a16b32fe71f87b5adbaae7ba8d544f388d704d12d18fddbf2528f98d5aaddaf4e93d4c112d27f2921c3f33c4fd9dedb23410e8ff24e
7
+ data.tar.gz: befd6ca6e2ecd9f7f454e8a7c1789d5effb1638b4b9fb74ed5c83b07e413e7f5d54ea76338621e6a696464194a6118fe0ffcd46acc0c094b0fd57a4e8a244b85
@@ -0,0 +1,127 @@
1
+ ### 1.0.0.beta2 (2018-04-13)
2
+
3
+ * Fixed an incompatibility that caused the new locker to hang when using Rails in development mode (#213).
4
+
5
+ * Fixed a bug with setting the log level via the CLI when the configured logger was based on a callable (#210).
6
+
7
+ * Renamed Que.middleware to Que.job_middleware.
8
+
9
+ * Added Que.sql_middleware.
10
+
11
+ * Officially added support for Ruby 2.5.
12
+
13
+ * Internal cleanup and renamings.
14
+
15
+ ### 1.0.0.beta (2017-10-25)
16
+
17
+ * **A schema upgrade to version 4 will be required for this release.** See [the migration doc](https://github.com/chanks/que/blob/master/docs/migrating.md) for information if you're upgrading from a previous release.
18
+
19
+ * Please note that this migration requires a rewrite of the jobs table, which makes it O(n) with the size of the table. If you have a very large backlog of jobs you may want to schedule downtime for this migration.
20
+
21
+ * Que's implementation has been changed from one in which worker threads hold their own PG connections and lock their own jobs to one in which a single thread (and PG connection) locks jobs through LISTEN/NOTIFY and batch polling, and passes jobs along to worker threads. This has many benefits, including:
22
+
23
+ * Jobs queued for immediate processing can be actively distributed to workers with LISTEN/NOTIFY, which is more efficient than having workers repeatedly poll for new jobs.
24
+
25
+ * When polling is necessary (to pick up jobs that are scheduled for the future or that need to be retried due to errors), jobs can be locked and fetched in batches, rather than one at a time.
26
+
27
+ * Individual workers no longer need to monopolize their own (usually idle) connections while working jobs, so Ruby processes will require fewer Postgres connections.
28
+
29
+ * PgBouncer or another external connection pool can be used for workers' connections (though not for the connection used to lock and listen for jobs).
30
+
31
+ * Other features introduced in this version include:
32
+
33
+ * Much better support for all versions of ActiveJob.
34
+
35
+ * In particular, you may (optionally) include `Que::ActiveJob::JobExtensions` into `ApplicationJob` to get support for all of Que's job helper methods.
36
+
37
+ * Custom middleware that wrap running jobs are now supported.
38
+
39
+ * Support for categorizing jobs with tags.
40
+
41
+ * Support for configuring a `maximum_retry_count` on individual job classes.
42
+
43
+ * Job configuration options are now inheritable, so job class hierarchies are more useful.
44
+
45
+ * There are now built-in models for ActiveRecord and Sequel to allow inspecting the queue easily.
46
+
47
+ * Jobs that have finished working may optionally be retained in the database indefinitely.
48
+
49
+ * To keep a job record, replace the `destroy` calls in your jobs with `finish`. `destroy` will still delete records entirely, for jobs that you don't want to keep.
50
+
51
+ * If you don't resolve a job yourself one way or another, Que will still `destroy` the job for you by default.
52
+
53
+ * Finished jobs have a timestamp set in the finished_at column.
54
+
55
+ * Jobs that have errored too many times will now be marked as expired, and won't be retried again.
56
+
57
+ * You can configure a maximum_retry_count in your job classes, to set the threshold at which a job will be marked expired. The default is 15.
58
+
59
+ * To manually mark a job as expired (and keep it in the database but not try to run it again) you can call `expire` helper in your job.
60
+
61
+ * You can now set job priority thresholds for individual workers, to ensure that there will always be space available for high-priority jobs.
62
+
63
+ * `Que.job_states` returns a list of locked jobs and the hostname/pid of the Ruby processes that have locked them.
64
+
65
+ * `Que.connection_proc=` has been added, to allow for the easy integration of custom connection pools.
66
+
67
+ * In keeping with semantic versioning, the major version is being bumped since the new implementation requires some backwards-incompatible changes. These changes include:
68
+
69
+ * Support for MRI Rubies before 2.2 has been dropped.
70
+
71
+ * Support for Postgres versions before 9.5 has been dropped (JSONB and upsert support is required).
72
+
73
+ * JRuby support has been dropped. It will be reintroduced whenever the jruby-pg gem is production-ready.
74
+
75
+ * The `que:work` rake task has been removed. Use the `que` executable instead.
76
+
77
+ * Therefore, configuring workers using QUE_* environment variables is no longer supported. Please pass the appropriate options to the `que` executable instead.
78
+
79
+ * The `mode` setter has been removed.
80
+
81
+ * To run jobs synchronously when they are enqueued (the old `:sync` behavior) you can set `Que.run_synchronously = true`.
82
+
83
+ * To start up the worker pool (the old :async behavior) you should use the `que` executable to start up a worker process. There's no longer a supported API for running workers outside of the `que` executable.
84
+
85
+ * The following methods are not meaningful under the new implementation and have been removed:
86
+
87
+ * The `Que.wake_interval` getter and setter.
88
+
89
+ * The `Que.worker_count` getter and setter.
90
+
91
+ * `Que.wake!`
92
+
93
+ * `Que.wake_all!`
94
+
95
+ * Since Que needs a dedicated Postgres connection to manage job locks, running Que through a single PG connection is no longer supported.
96
+
97
+ * It's not clear that anyone ever actually did this.
98
+
99
+ * `Que.worker_states` has been removed, as the connection that locks a job is no longer the one that the job is using to run. Its functionality has been partially replaced with `Que.job_states`.
100
+
101
+ * When using Rails, for simplicity, job attributes and keys in argument hashes are now converted to symbols when retrieved from the database, rather than being converted to instances of HashWithIndifferentAccess.
102
+
103
+ * Arguments passed to jobs are now deep-frozen, to prevent unexpected behavior when the args are mutated and the job is reenqueued.
104
+
105
+ * Since JSONB is now used to store arguments, the order of argument hashes is no longer maintained.
106
+
107
+ * It wouldn't have been a good idea to rely on this anyway.
108
+
109
+ * Calling Que.log() directly is no longer supported/recommended.
110
+
111
+ * Features marked as deprecated in the final 0.x releases have been removed.
112
+
113
+ * Finally, if you've built up your own tooling and customizations around Que, you may need to be aware of some DB schema changes made in the migration to schema version #4.
114
+
115
+ * The `job_id` column has been renamed `id` and is now the primary key. This makes it easier to manage the queue using an ActiveRecord model.
116
+
117
+ * Finished jobs are now kept in the DB, unless you explicitly call `destroy`. If you want to query the DB for only jobs that haven't finished yet, add a `WHERE finished_at IS NULL` condition to your query, or use the not_finished scope on one of the provided ORM models.
118
+
119
+ * There is now an `expired_at` timestamp column, which is set when a job reaches its maximum number of retries and will not be attempted again.
120
+
121
+ * Due to popular demand, the default queue name is now "default" rather than an empty string. The migration will move pending jobs under the "" queue to the "default" queue.
122
+
123
+ * The `last_error` column has been split in two, to `last_error_message` and `last_error_backtrace`. These two columns are now limited to 500 and 10,000 characters, respectively. The migration will split old error data correctly, and truncate it if necessary.
124
+
125
+ * Names for queues and job classes are now limited to 500 characters, which is still far longer than either of these values should reasonably be.
126
+
127
+ * There is now a `data` JSONB column which is used to support various ways of organizing jobs (setting tags on them, etc).
@@ -1,4 +1,4 @@
1
- ### 1.0.0.beta (2017-10-25)
1
+ ### 1.0.0.beta2 (2018-04-13)
2
2
 
3
3
  * **A schema upgrade to version 4 will be required for this release.** See [the migration doc](https://github.com/chanks/que/blob/master/docs/migrating.md) for information if you're upgrading from a previous release.
4
4
 
@@ -20,7 +20,7 @@
20
20
 
21
21
  * In particular, you may (optionally) include `Que::ActiveJob::JobExtensions` into `ApplicationJob` to get support for all of Que's job helper methods.
22
22
 
23
- * Custom middleware that wrap running jobs are now supported.
23
+ * Custom middleware that wrap running jobs and executing SQL statements are now supported.
24
24
 
25
25
  * Support for categorizing jobs with tags.
26
26
 
@@ -68,11 +68,7 @@
68
68
 
69
69
  * To start up the worker pool (the old :async behavior) you should use the `que` executable to start up a worker process. There's no longer a supported API for running workers outside of the `que` executable.
70
70
 
71
- * The way Que uses prepared statements internally has changed. This shouldn't affect anyone's use of Que, except that the `disable_prepared_statements` configuration option is no longer necessary and has been removed.
72
-
73
- * Specifically, while Que previously used prepared statements for most of its built-in queries, now only the polling query uses it, due to its complexity. Since the polling query is only run through a dedicated connection, it's no longer possible for prepared statements to conflict with external connection pools, which was the reason that `disable_prepared_statements` was supported in the first place.
74
-
75
- * In addition to `Que.disable_prepared_statements=`, the following methods are not meaningful under the new implementation and have been removed:
71
+ * The following methods are not meaningful under the new implementation and have been removed:
76
72
 
77
73
  * The `Que.wake_interval` getter and setter.
78
74
 
@@ -116,6 +112,32 @@
116
112
 
117
113
  * There is now a `data` JSONB column which is used to support various ways of organizing jobs (setting tags on them, etc).
118
114
 
115
+ For a detailed list of the changes between each beta release of 1.0.0, see [the beta Changelog](CHANGELOG.1.0.beta.md).
116
+
117
+ ### 0.14.3 (2018-03-02)
118
+
119
+ * Recorded errors now always include the error class, so that empty error messages can still be helpful. ( joehorsnell)
120
+
121
+ * Recorded error messages are now truncated to the first 500 characters.
122
+
123
+ ### 0.14.2 (2018-01-05)
124
+
125
+ * Deprecate the Que.disable_prepared_statements= accessors.
126
+
127
+ * Add Que.use_prepared_statements= configuration accessors.
128
+
129
+ * Update the generated Rails migration to declare a version. (NARKOZ)
130
+
131
+ ### 0.14.1 (2017-12-14)
132
+
133
+ * Fix a bug with typecasting boolean values on Rails 5+.
134
+
135
+ ### 0.14.0 (2017-08-11)
136
+
137
+ * Fix incompatibility with Rails 5.1.
138
+
139
+ * Drop support for waking an in-process worker when an ActiveRecord transaction commits.
140
+
119
141
  ### 0.13.1 (2017-07-05)
120
142
 
121
143
  * Fix issue that caused error stacktraces to not be persisted in most cases.
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Que
2
2
 
3
- **TL;DR: Que is a high-performance job queue that improves the reliability of your application by protecting your jobs with the same [ACID guarantees](https://en.wikipedia.org/wiki/ACID) as the rest of your data.**
3
+ **This README and the rest of the docs on the master branch all refer to Que 1.0, which is currently in beta. If you're using version 0.x, please refer to the docs on [the 0.x branch](https://github.com/chanks/que/tree/0.x).**
4
+
5
+ *TL;DR: Que is a high-performance job queue that improves the reliability of your application by protecting your jobs with the same [ACID guarantees](https://en.wikipedia.org/wiki/ACID) as the rest of your data.*
4
6
 
5
7
  Que ("keɪ", or "kay") is a queue for Ruby and PostgreSQL that manages jobs using [advisory locks](http://www.postgresql.org/docs/current/static/explicit-locking.html#ADVISORY-LOCKS), which gives it several advantages over other RDBMS-backed queues:
6
8
 
@@ -113,6 +115,17 @@ You can also add options to run the job after a specific time, or with a specifi
113
115
  ChargeCreditCard.enqueue card.id, user_id: current_user.id, run_at: 1.day.from_now, priority: 5
114
116
  ```
115
117
 
118
+ Finally, you can work jobs using the included `que` CLI. Try running `que -h` to get a list of runtime options:
119
+ ```
120
+ $ que -h
121
+ usage: que [options] [file/to/require] ...
122
+ -h, --help Show this help text.
123
+ -i, --poll-interval [INTERVAL] Set maximum interval between polls for available jobs, in seconds (default: 5)
124
+ ...
125
+ ```
126
+
127
+ You may need to pass que a file path to require so that it can load your app. Que will automatically load `config/environment.rb` if it exists, so you shouldn't need an argument if you're using Rails.
128
+
116
129
  ## Testing
117
130
 
118
131
  There are a couple ways to do testing. You may want to set `Que::Job.run_synchronously = true`, which will cause JobClass.enqueue to simply execute the job's logic synchronously, as if you'd run JobClass.run(*your_args). Or, you may want to leave it disabled so you can assert on the job state once they are stored in the database.
@@ -93,7 +93,7 @@ module Que
93
93
  String,
94
94
  "Set a custom database url to connect to for locking purposes.",
95
95
  ) do |url|
96
- connection_url = url
96
+ options[:connection_url] = url
97
97
  end
98
98
 
99
99
  opts.on(
@@ -107,19 +107,19 @@ module Que
107
107
  opts.on(
108
108
  '--maximum-buffer-size [SIZE]',
109
109
  Integer,
110
- "Set maximum number of jobs to be cached in this process " \
111
- "awaiting a worker (default: 8)",
110
+ "Set maximum number of jobs to be locked and held in this " \
111
+ "process awaiting a worker (default: 8)",
112
112
  ) do |s|
113
- options[:maximum_queue_size] = s
113
+ options[:maximum_buffer_size] = s
114
114
  end
115
115
 
116
116
  opts.on(
117
117
  '--minimum-buffer-size [SIZE]',
118
118
  Integer,
119
- "Set minimum number of jobs to be cached in this process " \
120
- "awaiting a worker (default: 2)",
119
+ "Set minimum number of jobs to be locked and held in this " \
120
+ "process awaiting a worker (default: 2)",
121
121
  ) do |s|
122
- options[:minimum_queue_size] = s
122
+ options[:minimum_buffer_size] = s
123
123
  end
124
124
 
125
125
  opts.on(
@@ -172,7 +172,7 @@ OUTPUT
172
172
  end
173
173
 
174
174
  begin
175
- Que.logger.level = Logger.const_get(log_level.upcase)
175
+ Que.get_logger.level = Logger.const_get(log_level.upcase)
176
176
  rescue NameError
177
177
  output.puts "Unsupported logging level: #{log_level} (try debug, info, warn, error, or fatal)"
178
178
  return 1
@@ -195,19 +195,6 @@ OUTPUT
195
195
 
196
196
  options[:poll_interval] = poll_interval
197
197
 
198
- if connection_url
199
- uri = URI.parse(connection_url)
200
-
201
- options[:connection] =
202
- PG::Connection.open(
203
- host: uri.host,
204
- user: uri.user,
205
- password: uri.password,
206
- port: uri.port || 5432,
207
- dbname: uri.path[1..-1],
208
- )
209
- end
210
-
211
198
  locker =
212
199
  begin
213
200
  Que::Locker.new(options)
@@ -221,13 +208,14 @@ OUTPUT
221
208
  $stop_que_executable = false
222
209
  %w[INT TERM].each { |signal| trap(signal) { $stop_que_executable = true } }
223
210
 
211
+ output.puts "Que waiting for jobs..."
212
+
224
213
  loop do
225
214
  sleep 0.01
226
215
  break if $stop_que_executable || locker.stopping?
227
216
  end
228
217
 
229
- output.puts ''
230
- output.puts "Finishing Que's current jobs before exiting..."
218
+ output.puts "\nFinishing Que's current jobs before exiting..."
231
219
 
232
220
  locker.stop!
233
221
 
@@ -1,6 +1,6 @@
1
1
  ## Using Que With ActiveJob
2
2
 
3
3
  You can include `Que::ActiveJob::JobExtensions` into your `ApplicationJob` subclass to get support for all of Que's
4
- [helper methods](/job_helper_methods.md). These methods will become no-ops if you use a queue adapter that isn't Que, so if you like to use a different adapter in development they shouldn't interfere.
4
+ [helper methods](/docs/job_helper_methods.md). These methods will become no-ops if you use a queue adapter that isn't Que, so if you like to use a different adapter in development they shouldn't interfere.
5
5
 
6
6
  Additionally, including `Que::ActiveJob::JobExtensions` lets you define a run() method that supports keyword arguments.
@@ -10,8 +10,8 @@ usage: que [options] [file/to/require] ...
10
10
  -w, --worker-count [COUNT] Set number of workers in process (default: 6)
11
11
  --connection-url [URL] Set a custom database url to connect to for locking purposes.
12
12
  --log-internals Log verbosely about Que's internal state. Only recommended for debugging issues
13
- --maximum-buffer-size [SIZE] Set maximum number of jobs to be cached in this process awaiting a worker (default: 8)
14
- --minimum-buffer-size [SIZE] Set minimum number of jobs to be cached in this process awaiting a worker (default: 2)
13
+ --maximum-buffer-size [SIZE] Set maximum number of jobs to be locked and held in this process awaiting a worker (default: 8)
14
+ --minimum-buffer-size [SIZE] Set minimum number of jobs to be locked and held in this process awaiting a worker (default: 2)
15
15
  --wait-period [PERIOD] Set maximum interval between checks of the in-memory job queue, in milliseconds (default: 50)
16
16
  --worker-priorities [LIST] List of priorities to assign to workers, unspecified workers take jobs of any priority (default: 10,30,50)
17
17
  ```
@@ -30,7 +30,7 @@ This option sets the number of seconds the process will wait between polls of th
30
30
 
31
31
  ### minimum-buffer-size and maximum-buffer-size
32
32
 
33
- These options set the size of the internal buffer that Que uses to cache job information until it's ready for workers. The default minimum is 2 and the maximum is 8, meaning that the process won't buffer more than 8 jobs that aren't yet ready to be worked, and will only resort to polling if the buffer dips below 2. If you don't want jobs to be buffered at all, you can set both of these values to zero.
33
+ These options set the size of the internal buffer that Que uses to hold jobs until they're ready for workers. The default minimum is 2 and the maximum is 8, meaning that the process won't buffer more than 8 jobs that aren't yet ready to be worked, and will only resort to polling if the buffer dips below 2. If you don't want jobs to be buffered at all, you can set both of these values to zero.
34
34
 
35
35
  ### connection-url
36
36
 
@@ -1,9 +1,15 @@
1
- ## Defining Middleware For Jobs
1
+ ## Middleware
2
2
 
3
- You can define middleware to wrap jobs. For example:
3
+ A new feature in 1.0 is support for custom middleware around various actions.
4
+
5
+ This API is experimental for the 1.0 beta and may change.
6
+
7
+ ### Defining Middleware For Jobs
8
+
9
+ You can define middleware to wrap worked jobs. You can use this to add custom instrumentation around jobs, log how long they take to complete, etc.
4
10
 
5
11
  ``` ruby
6
- Que.middleware.push(
12
+ Que.job_middleware.push(
7
13
  -> (job, &block) {
8
14
  # Do stuff with the job object - report on it, count time elapsed, etc.
9
15
  block.call
@@ -12,4 +18,19 @@ Que.middleware.push(
12
18
  )
13
19
  ```
14
20
 
15
- This API is experimental for the 1.0 beta and may change.
21
+ ### Defining Middleware For SQL statements
22
+
23
+ SQL middleware wraps queries that Que executes, or which you might decide to execute via Que.execute(). You can use hook this into NewRelic or a similar service to instrument how long SQL queries take, for example.
24
+
25
+ ``` ruby
26
+ Que.sql_middleware.push(
27
+ -> (sql, params, &block) {
28
+ Service.instrument(sql: sql, params: params) do
29
+ block.call
30
+ end
31
+ nil # Still doesn't matter what's returned.
32
+ }
33
+ )
34
+ ```
35
+
36
+ Please be careful with what you do inside an SQL middleware - this code will execute inside Que's locking thread, which runs in a fairly tight loop that is optimized for performance. If you do something inside this block that incurs blocking I/O (like synchronously touching an external service) you may find Que being less able to pick up jobs quickly.
data/lib/que.rb CHANGED
@@ -35,7 +35,7 @@ module Que
35
35
  require_relative 'que/connection_pool'
36
36
  require_relative 'que/job_methods'
37
37
  require_relative 'que/job'
38
- require_relative 'que/job_cache'
38
+ require_relative 'que/job_buffer'
39
39
  require_relative 'que/locker'
40
40
  require_relative 'que/metajob'
41
41
  require_relative 'que/migrations'
@@ -44,6 +44,12 @@ module Que
44
44
  require_relative 'que/version'
45
45
  require_relative 'que/worker'
46
46
 
47
+ class << self
48
+ attr_writer :default_queue
49
+ end
50
+
51
+ self.default_queue = nil
52
+
47
53
  class << self
48
54
  include Utils::Assertions
49
55
  include Utils::Constantization
@@ -65,7 +71,6 @@ module Que
65
71
 
66
72
  # Global configuration logic.
67
73
  attr_accessor :use_prepared_statements
68
- attr_writer :default_queue
69
74
 
70
75
  def default_queue
71
76
  @default_queue || DEFAULT_QUEUE
@@ -77,8 +82,8 @@ module Que
77
82
  if conn.to_s == 'ActiveRecord'
78
83
  # Load and setup AR compatibility.
79
84
  require_relative 'que/active_record/connection'
80
- m = Que::ActiveRecord::Connection::Middleware
81
- middleware << m unless middleware.include?(m)
85
+ m = Que::ActiveRecord::Connection::JobMiddleware
86
+ job_middleware << m unless job_middleware.include?(m)
82
87
  Que::ActiveRecord::Connection.method(:checkout)
83
88
  else
84
89
  case conn.class.to_s
@@ -18,16 +18,16 @@ module Que
18
18
  # Use Rails' executor (if present) to make sure that the connection
19
19
  # we're using isn't taken from us while the block runs. See
20
20
  # https://github.com/chanks/que/issues/166#issuecomment-274218910
21
- def wrap_in_rails_executor
21
+ def wrap_in_rails_executor(&block)
22
22
  if defined?(::Rails.application.executor)
23
- ::Rails.application.executor.wrap { yield }
23
+ ::Rails.application.executor.wrap(&block)
24
24
  else
25
25
  yield
26
26
  end
27
27
  end
28
28
  end
29
29
 
30
- module Middleware
30
+ module JobMiddleware
31
31
  class << self
32
32
  def call(job)
33
33
  yield
@@ -16,11 +16,11 @@ module Que
16
16
  scope :finished, -> { where(t[:finished_at].not_eq(nil)) }
17
17
  scope :not_finished, -> { where(t[:finished_at].eq(nil)) }
18
18
 
19
- scope :scheduled, -> { where(t[:run_at].gt("now()")) }
20
- scope :not_scheduled, -> { where(t[:run_at].lteq("now()")) }
19
+ scope :scheduled, -> { where(t[:run_at].gt (Arel.sql("now()"))) }
20
+ scope :not_scheduled, -> { where(t[:run_at].lteq(Arel.sql("now()"))) }
21
21
 
22
22
  scope :ready, -> { not_errored.not_expired.not_finished.not_scheduled }
23
- scope :not_ready, -> { where(t[:error_count].gt(0).or(t[:expired_at].not_eq(nil)).or(t[:finished_at].not_eq(nil)).or(t[:run_at].gt("now()"))) }
23
+ scope :not_ready, -> { where(t[:error_count].gt(0).or(t[:expired_at].not_eq(nil)).or(t[:finished_at].not_eq(nil)).or(t[:run_at].gt(Arel.sql("now()")))) }
24
24
 
25
25
  class << self
26
26
  def by_job_class(job_class)