que 1.0.0.beta4 → 1.0.0.beta5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/{ruby.yml → tests.yml} +8 -4
- data/README.md +29 -5
- data/bin/command_line_interface.rb +2 -2
- data/docs/README.md +6 -6
- data/lib/que/active_record/model.rb +1 -1
- data/lib/que/connection.rb +4 -0
- data/lib/que/job_buffer.rb +27 -18
- data/lib/que/locker.rb +3 -1
- data/lib/que/version.rb +1 -1
- data/lib/que/worker.rb +10 -3
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90ae5c7c65d93f4ea3833d80351c2e0d3ed3f706ba00411937e8aba2137a41d7
|
4
|
+
data.tar.gz: 58aba934ee2735522e602dd5777de666c6acbee1cb531d9918ce6f2b75f3b693
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1cbfa5f3c6d13868897ac9f1fcf079aaeaa29c9eadfd231044a484878fbf8e99f4b159a4458690027b181b29c5da3abafcc1c9de2d33b3c487b1fac4a15c7e50
|
7
|
+
data.tar.gz: 1ed4b71f51cd72e3968de6ec745e2de592dc021e9d334526630b3011d3f5526388b8562da4bf513066cb68d4dee033043115027ddfc0e9bef5e816ed2c35ede5
|
@@ -1,18 +1,22 @@
|
|
1
|
-
name:
|
1
|
+
name: tests
|
2
2
|
|
3
|
-
on: [pull_request]
|
3
|
+
on: [push, pull_request]
|
4
4
|
|
5
5
|
jobs:
|
6
6
|
test:
|
7
7
|
runs-on: ubuntu-latest
|
8
8
|
strategy:
|
9
9
|
matrix:
|
10
|
-
ruby_version: [2.5.x, 2.6.x]
|
10
|
+
ruby_version: [2.5.x, 2.6.x, 2.7.x]
|
11
11
|
gemfile: ["4.2", "5.2", "6.0"]
|
12
|
-
postgres_version: [9, 10, 11]
|
12
|
+
postgres_version: [9, 10, 11, 12]
|
13
|
+
exclude:
|
14
|
+
- { gemfile: "4.2", ruby_version: "2.7.x" }
|
13
15
|
services:
|
14
16
|
db:
|
15
17
|
image: postgres:${{ matrix.postgres_version }}
|
18
|
+
env:
|
19
|
+
POSTGRES_HOST_AUTH_METHOD: trust
|
16
20
|
ports: ['5432:5432']
|
17
21
|
options: >-
|
18
22
|
--health-cmd pg_isready
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Que
|
1
|
+
# Que ![tests](https://github.com/que-rb/que/workflows/tests/badge.svg)
|
2
2
|
|
3
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/que-rb/que/tree/0.x).**
|
4
4
|
|
@@ -17,7 +17,7 @@ Additionally, there are the general benefits of storing jobs in Postgres, alongs
|
|
17
17
|
* **Fewer Dependencies** - If you're already using Postgres (and you probably should be), a separate queue is another moving part that can break.
|
18
18
|
* **Security** - Postgres' support for SSL connections keeps your data safe in transport, for added protection when you're running workers on cloud platforms that you can't completely control.
|
19
19
|
|
20
|
-
Que's primary goal is reliability. You should be able to leave your application running indefinitely without worrying about jobs being lost due to a lack of transactional support, or left in limbo due to a crashing process. Que does everything it can to ensure that jobs you queue are performed exactly once (though the occasional repetition of a job can be impossible to avoid - see the docs on [how to write a reliable job](
|
20
|
+
Que's primary goal is reliability. You should be able to leave your application running indefinitely without worrying about jobs being lost due to a lack of transactional support, or left in limbo due to a crashing process. Que does everything it can to ensure that jobs you queue are performed exactly once (though the occasional repetition of a job can be impossible to avoid - see the docs on [how to write a reliable job](/docs/README.md#writing-reliable-jobs)).
|
21
21
|
|
22
22
|
Que's secondary goal is performance. The worker process is multithreaded, so that a single process can run many jobs simultaneously.
|
23
23
|
|
@@ -114,8 +114,14 @@ You can also add options to run the job after a specific time, or with a specifi
|
|
114
114
|
``` ruby
|
115
115
|
ChargeCreditCard.enqueue card.id, user_id: current_user.id, run_at: 1.day.from_now, priority: 5
|
116
116
|
```
|
117
|
+
## Running the Que Worker
|
118
|
+
In order to process jobs, you must start a separate worker process outside of your main server.
|
117
119
|
|
118
|
-
|
120
|
+
```
|
121
|
+
bundle exec que
|
122
|
+
```
|
123
|
+
|
124
|
+
Try running `que -h` to get a list of runtime options:
|
119
125
|
```
|
120
126
|
$ que -h
|
121
127
|
usage: que [options] [file/to/require] ...
|
@@ -130,7 +136,21 @@ You may need to pass que a file path to require so that it can load your app. Qu
|
|
130
136
|
|
131
137
|
If you're using ActiveRecord to dump your database's schema, please [set your schema_format to :sql](http://guides.rubyonrails.org/migrations.html#types-of-schema-dumps) so that Que's table structure is managed correctly. This is a good idea regardless, as the `:ruby` schema format doesn't support many of PostgreSQL's advanced features.
|
132
138
|
|
133
|
-
Pre-1.0, the default queue name needed to be configured in order for Que to work out of the box with Rails. In 1.0 the default queue name is now 'default', as Rails expects, but when Rails enqueues some types of jobs it may try to use another queue name that isn't worked by default
|
139
|
+
Pre-1.0, the default queue name needed to be configured in order for Que to work out of the box with Rails. In 1.0 the default queue name is now 'default', as Rails expects, but when Rails enqueues some types of jobs it may try to use another queue name that isn't worked by default. You can either:
|
140
|
+
|
141
|
+
* [Configure Rails](https://guides.rubyonrails.org/configuring.html) to send all internal job types to the 'default' queue by adding the following to `config/application.rb`:
|
142
|
+
```ruby
|
143
|
+
config.action_mailer.deliver_later_queue_name = :default
|
144
|
+
config.action_mailbox.queues.incineration = :default
|
145
|
+
config.action_mailbox.queues.routing = :default
|
146
|
+
config.active_storage.queues.analysis = :default
|
147
|
+
config.active_storage.queues.purge = :default
|
148
|
+
```
|
149
|
+
|
150
|
+
* [Tell que](/docs#multiple-queues) to work all of these queues (less efficient because it requires polling all of them):
|
151
|
+
```
|
152
|
+
que -q default -q mailers -q action_mailbox_incineration -q action_mailbox_routing -q active_storage_analysis -q active_storage_purge
|
153
|
+
```
|
134
154
|
|
135
155
|
Also, if you would like to integrate Que with Active Job, you can do it by setting the adapter in `config/application.rb` or in a specific environment by setting it in `config/environments/production.rb`, for example:
|
136
156
|
```ruby
|
@@ -147,12 +167,17 @@ If you later decide to switch a job from Active Job to Que to have transactional
|
|
147
167
|
|
148
168
|
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.
|
149
169
|
|
170
|
+
## Documentation
|
171
|
+
|
172
|
+
**For full documentation, see [here](docs/README.md)**.
|
173
|
+
|
150
174
|
## Related Projects
|
151
175
|
|
152
176
|
These projects are tested to be compatible with Que 1.x:
|
153
177
|
|
154
178
|
- [que-web](https://github.com/statianzo/que-web) is a Sinatra-based UI for inspecting your job queue.
|
155
179
|
- [que-scheduler](https://github.com/hlascelles/que-scheduler) lets you schedule tasks using a cron style config file.
|
180
|
+
- [que-locks](https://github.com/airhorns/que-locks) lets you lock around job execution for so only one job runs at once for a set of arguments.
|
156
181
|
|
157
182
|
If you have a project that uses or relates to Que, feel free to submit a PR adding it to the list!
|
158
183
|
|
@@ -164,7 +189,6 @@ If you have a project that uses or relates to Que, feel free to submit a PR addi
|
|
164
189
|
|
165
190
|
Regarding contributions, one of the project's priorities is to keep Que as simple, lightweight and dependency-free as possible, and pull requests that change too much or wouldn't be useful to the majority of Que's users have a good chance of being rejected. If you're thinking of submitting a pull request that adds a new feature, consider starting a discussion in [que-talk](https://groups.google.com/forum/#!forum/que-talk) first about what it would do and how it would be implemented. If it's a sufficiently large feature, or if most of Que's users wouldn't find it useful, it may be best implemented as a standalone gem, like some of the related projects above.
|
166
191
|
|
167
|
-
|
168
192
|
### Specs
|
169
193
|
|
170
194
|
A note on running specs - Que's worker system is multithreaded and therefore prone to race conditions. As such, if you've touched that code, a single spec run passing isn't a guarantee that any changes you've made haven't introduced bugs. One thing I like to do before pushing changes is rerun the specs many times and watching for hangs. You can do this from the command line with something like:
|
data/docs/README.md
CHANGED
@@ -37,9 +37,9 @@ Docs Index
|
|
37
37
|
* [destroy](#destroy)
|
38
38
|
* [finish](#finish)
|
39
39
|
* [expire](#expire)
|
40
|
-
* [retry_in](#
|
41
|
-
* [error_count](#
|
42
|
-
* [default_resolve_action](#
|
40
|
+
* [retry_in](#retry_in)
|
41
|
+
* [error_count](#error_count)
|
42
|
+
* [default_resolve_action](#default_resolve_action)
|
43
43
|
- [Writing Reliable Jobs](#writing-reliable-jobs)
|
44
44
|
* [Timeouts](#timeouts)
|
45
45
|
- [Middleware](#middleware)
|
@@ -122,7 +122,7 @@ ActiveRecord::Base.transaction do
|
|
122
122
|
end
|
123
123
|
```
|
124
124
|
|
125
|
-
There are other docs to read if you're using [Sequel](#using-sequel) or [plain Postgres connections](#using-plain-connections) (with no ORM at all) instead of ActiveRecord.
|
125
|
+
There are other docs to read if you're using [Sequel](#using-sequel) or [plain Postgres connections](#using-plain-postgres-connections) (with no ORM at all) instead of ActiveRecord.
|
126
126
|
|
127
127
|
### Managing the Jobs Table
|
128
128
|
|
@@ -463,7 +463,7 @@ Que.enqueue current_user.id, job_class: 'ProcessCreditCard', queue: 'credit_card
|
|
463
463
|
|
464
464
|
To ensure safe operation, Que needs to be very careful in how it shuts down. When a Ruby process ends normally, it calls Thread#kill on any threads that are still running - unfortunately, if a thread is in the middle of a transaction when this happens, there is a risk that it will be prematurely commited, resulting in data corruption. See [here](http://blog.headius.com/2008/02/ruby-threadraise-threadkill-timeoutrb.html) and [here](http://coderrr.wordpress.com/2011/05/03/beware-of-threadkill-or-your-activerecord-transactions-are-in-danger-of-being-partially-committed/) for more detail on this.
|
465
465
|
|
466
|
-
To prevent this, Que will block the worker process from exiting until all jobs it is working have completed normally. Unfortunately, if you have long-running jobs, this may take a very long time (and if something goes wrong with a job's logic, it may never happen). The solution in this case is SIGKILL - luckily, Ruby processes that are killed via SIGKILL will end without using Thread#kill on its running threads. This is safer than exiting normally - when PostgreSQL loses the connection it will simply roll back the open transaction, if any, and unlock the job so it can be retried later by another worker. Be sure to read [Writing Reliable Jobs](#writing-reliable-jobs
|
466
|
+
To prevent this, Que will block the worker process from exiting until all jobs it is working have completed normally. Unfortunately, if you have long-running jobs, this may take a very long time (and if something goes wrong with a job's logic, it may never happen). The solution in this case is SIGKILL - luckily, Ruby processes that are killed via SIGKILL will end without using Thread#kill on its running threads. This is safer than exiting normally - when PostgreSQL loses the connection it will simply roll back the open transaction, if any, and unlock the job so it can be retried later by another worker. Be sure to read [Writing Reliable Jobs](#writing-reliable-jobs) for information on how to design your jobs to fail safely.
|
467
467
|
|
468
468
|
So, be prepared to use SIGKILL on your Ruby processes if they run for too long. For example, Heroku takes a good approach to this - when Heroku's platform is shutting down a process, it sends SIGTERM, waits ten seconds, then sends SIGKILL if the process still hasn't exited. This is a nice compromise - it will give each of your currently running jobs ten seconds to complete, and any jobs that haven't finished by then will be interrupted and retried later.
|
469
469
|
|
@@ -593,7 +593,7 @@ Additionally, including `Que::ActiveJob::JobExtensions` lets you define a run()
|
|
593
593
|
|
594
594
|
## Job Helper Methods
|
595
595
|
|
596
|
-
There are a number of instance methods on Que::Job that you can use in your jobs, preferably in transactions. See [Writing Reliable Jobs](
|
596
|
+
There are a number of instance methods on Que::Job that you can use in your jobs, preferably in transactions. See [Writing Reliable Jobs](#writing-reliable-jobs) for more information on where to use these methods.
|
597
597
|
|
598
598
|
### destroy
|
599
599
|
|
data/lib/que/connection.rb
CHANGED
data/lib/que/job_buffer.rb
CHANGED
@@ -59,10 +59,8 @@ module Que
|
|
59
59
|
|
60
60
|
# Relying on the hash's contents being sorted, here.
|
61
61
|
priority_queues.reverse_each do |_, pq|
|
62
|
-
pq.
|
63
|
-
|
64
|
-
break if job.nil? # False would mean we're stopping.
|
65
|
-
pq.push(job)
|
62
|
+
pq.populate do
|
63
|
+
_shift_job(pq.priority)
|
66
64
|
end
|
67
65
|
end
|
68
66
|
|
@@ -75,7 +73,7 @@ module Que
|
|
75
73
|
|
76
74
|
def shift(priority = nil)
|
77
75
|
queue = priority_queues.fetch(priority) { raise Error, "not a permitted priority! #{priority}" }
|
78
|
-
queue.pop
|
76
|
+
queue.pop || shift_job(priority)
|
79
77
|
end
|
80
78
|
|
81
79
|
def shift_job(priority = nil)
|
@@ -158,6 +156,10 @@ module Que
|
|
158
156
|
sync { _stopping? }
|
159
157
|
end
|
160
158
|
|
159
|
+
def job_available?(priority)
|
160
|
+
(job = @array.first) && job.priority_sufficient?(priority)
|
161
|
+
end
|
162
|
+
|
161
163
|
private
|
162
164
|
|
163
165
|
def _buffer_space
|
@@ -210,15 +212,14 @@ module Que
|
|
210
212
|
def pop
|
211
213
|
sync do
|
212
214
|
loop do
|
213
|
-
|
214
|
-
|
215
|
-
|
215
|
+
if @stopping
|
216
|
+
return false
|
217
|
+
elsif item = @items.pop
|
216
218
|
return item
|
219
|
+
elsif job_buffer.job_available?(priority)
|
220
|
+
return false
|
217
221
|
end
|
218
222
|
|
219
|
-
job = job_buffer.shift_job(priority)
|
220
|
-
return job unless job.nil? # False means we're stopping.
|
221
|
-
|
222
223
|
@waiting += 1
|
223
224
|
@cv.wait(mutex)
|
224
225
|
@waiting -= 1
|
@@ -226,18 +227,20 @@ module Que
|
|
226
227
|
end
|
227
228
|
end
|
228
229
|
|
229
|
-
def
|
230
|
+
def stop
|
230
231
|
sync do
|
231
|
-
|
232
|
-
@
|
233
|
-
@cv.signal
|
232
|
+
@stopping = true
|
233
|
+
@cv.broadcast
|
234
234
|
end
|
235
235
|
end
|
236
236
|
|
237
|
-
def
|
237
|
+
def populate
|
238
238
|
sync do
|
239
|
-
|
240
|
-
|
239
|
+
waiting_count.times do
|
240
|
+
job = yield
|
241
|
+
break if job.nil? # False would mean we're stopping.
|
242
|
+
_push(job)
|
243
|
+
end
|
241
244
|
end
|
242
245
|
end
|
243
246
|
|
@@ -250,6 +253,12 @@ module Que
|
|
250
253
|
def sync(&block)
|
251
254
|
mutex.synchronize(&block)
|
252
255
|
end
|
256
|
+
|
257
|
+
def _push(item)
|
258
|
+
Que.assert(waiting_count > 0)
|
259
|
+
@items << item
|
260
|
+
@cv.signal
|
261
|
+
end
|
253
262
|
end
|
254
263
|
end
|
255
264
|
end
|
data/lib/que/locker.rb
CHANGED
@@ -393,10 +393,12 @@ module Que
|
|
393
393
|
}
|
394
394
|
end
|
395
395
|
|
396
|
+
materalize_cte = connection.server_version >= 12_00_00
|
397
|
+
|
396
398
|
jobs =
|
397
399
|
connection.execute \
|
398
400
|
<<-SQL
|
399
|
-
WITH jobs AS (SELECT * FROM que_jobs WHERE id IN (#{ids.join(', ')}))
|
401
|
+
WITH jobs AS #{materalize_cte ? 'MATERIALIZED' : ''} (SELECT * FROM que_jobs WHERE id IN (#{ids.join(', ')}))
|
400
402
|
SELECT * FROM jobs WHERE pg_try_advisory_lock(id)
|
401
403
|
SQL
|
402
404
|
|
data/lib/que/version.rb
CHANGED
data/lib/que/worker.rb
CHANGED
@@ -54,10 +54,17 @@ module Que
|
|
54
54
|
private
|
55
55
|
|
56
56
|
def work_loop
|
57
|
-
# Blocks until a job of the appropriate priority is available.
|
58
|
-
#
|
57
|
+
# Blocks until a job of the appropriate priority is available.
|
58
|
+
# `fetch_next_metajob` normally returns a job to be processed.
|
59
|
+
# If the queue is shutting down it will return false, which breaks the loop and
|
59
60
|
# lets the thread finish.
|
60
|
-
while metajob = fetch_next_metajob
|
61
|
+
while (metajob = fetch_next_metajob) != false
|
62
|
+
# If metajob is nil instead of false, we've hit a rare race condition where
|
63
|
+
# there was a job in the buffer when the worker code checked, but the job was
|
64
|
+
# picked up by the time we got around to shifting it off the buffer.
|
65
|
+
# Letting this case go unhandled leads to worker threads exiting pre-maturely, so
|
66
|
+
# we check explicitly and continue the loop.
|
67
|
+
next if metajob.nil?
|
61
68
|
id = metajob.id
|
62
69
|
|
63
70
|
Que.internal_log(:worker_received_job, self) { {id: id} }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: que
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.beta5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Hanks
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-12-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -32,7 +32,7 @@ executables:
|
|
32
32
|
extensions: []
|
33
33
|
extra_rdoc_files: []
|
34
34
|
files:
|
35
|
-
- ".github/workflows/
|
35
|
+
- ".github/workflows/tests.yml"
|
36
36
|
- ".gitignore"
|
37
37
|
- CHANGELOG.1.0.beta.md
|
38
38
|
- CHANGELOG.md
|
@@ -99,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
99
|
- !ruby/object:Gem::Version
|
100
100
|
version: 1.3.1
|
101
101
|
requirements: []
|
102
|
-
rubygems_version: 3.
|
102
|
+
rubygems_version: 3.1.6
|
103
103
|
signing_key:
|
104
104
|
specification_version: 4
|
105
105
|
summary: A PostgreSQL-based Job Queue
|