que 0.8.2 → 0.9.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
  SHA1:
3
- metadata.gz: 8029277244c5b5f8e4d838c9e16b2ebddd402919
4
- data.tar.gz: 06fe39d7feb085098468d4b7f019aa0a40433016
3
+ metadata.gz: 6e9631191c30bd35b86706f09611faebc0f99e07
4
+ data.tar.gz: b53b3005e278471f4e4ee7e55a41637db002a2be
5
5
  SHA512:
6
- metadata.gz: 3564b06d93c6001367f7d728e6ba22600abe53609a6551bc73aac2dda6cd6ab55629e12b002adb9998981960358617f8b0fd045d5ee9367ef52e1fd371dfb654
7
- data.tar.gz: 2f7731e0b01ffed874937273c86364f7a19bd6de67a573f8c82aedf1dcc5918e6c841ae1b78dbbd90c085f9f5112c5440551f72f4738081ec9c24a1b8f49aae7
6
+ metadata.gz: 7a8da4e60a2d3e9bd85be9891cf7d3c736621ed5e72d07d2f6145e2430ca7af15d678441c2d3bd9358aee1aab84fde5a73d14be0af1a87c2425a3ec51de652de
7
+ data.tar.gz: 1245796b0d471b8ab3ccc528f016cb244dad07fc80282fcae82e5bd1203281df01e40bd012e04cc1eade1a63a8424a3488043a81ddf68d02886ca6b8256c5067
@@ -1,3 +1,7 @@
1
+ ### 0.9.0 (2014-12-16)
2
+
3
+ * The error_handler callable is now be passed two objects, the error and the job that raised it. If your current error_handler is a proc, as recommended in the docs, you shouldn't need to make any code changes, unless you want to use the job in your error handling. If your error_handler is a lambda, or another callable with a strict arity requirement, you'll want to change it before upgrading. (#69) (statianzo)
4
+
1
5
  ### 0.8.2 (2014-10-12)
2
6
 
3
7
  * Fix errors raised during rollbacks in the ActiveRecord adapter, which remained silent until Rails 4.2. (#64, #65) (Strech)
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **TL;DR: Que is a high-performance alternative to DelayedJob or QueueClassic 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
4
 
5
- Que 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:
5
+ 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
6
 
7
7
  * **Concurrency** - Workers don't block each other when trying to lock jobs, as often occurs with "SELECT FOR UPDATE"-style locking. This allows for very high throughput with a large number of workers.
8
8
  * **Efficiency** - Locks are held in memory, so locking a job doesn't incur a disk write. These first two points are what limit performance with other queues - all workers trying to lock jobs have to wait behind one that's persisting its UPDATE on a locked_at column to disk (and the disks of however many other servers your database is synchronously replicating to). Under heavy load, Que's bottleneck is CPU, not I/O.
@@ -104,6 +104,13 @@ To determine what happens when a job is queued, you can set Que's mode in your a
104
104
 
105
105
  If you're using ActiveRecord to dump your database's schema, you'll probably want to [set schema_format to :sql](http://guides.rubyonrails.org/migrations.html#types-of-schema-dumps) so that Que's table structure is managed correctly.
106
106
 
107
+ ## Related Gems
108
+
109
+ * [que-web](https://github.com/statianzo/que-web) is a Sinatra-based UI for inspecting your job queue.
110
+ * [que-testing](https://github.com/statianzo/que-testing) allows making assertions on enqueued jobs.
111
+
112
+ If you have a gem that uses or relates to Que, feel free to submit a PR adding it to the list!
113
+
107
114
  ## Contributing
108
115
 
109
116
  1. Fork it
@@ -6,24 +6,48 @@ One of Que's goals to be easily extensible and hackable (and if anyone has any s
6
6
 
7
7
  Que's support for scheduling jobs makes it easy to implement reliable recurring jobs. For example, suppose you want to run a job every hour that processes the users created in that time:
8
8
 
9
- class Cron < Que::Job
10
- def run
11
- users = User.where(:created_at => @attrs[:run_at]...(@attrs[:run_at] + 1.hour))
12
- # Do something with users.
13
-
14
- ActiveRecord::Base.transaction do
15
- destroy
16
- self.class.enqueue :run_at => @attrs[:run_at] + 1.hour
9
+ class CronJob < Que::Job
10
+ # Default repetition interval in seconds. Can be overridden in
11
+ # subclasses. Can use 1.minute if using Rails.
12
+ INTERVAL = 60
13
+
14
+ attr_reader :start_at, :end_at, :run_again_at, :time_range
15
+
16
+ def _run
17
+ args = attrs[:args].first
18
+ @start_at, @end_at = Time.at(args.delete('start_at')), Time.at(args.delete('end_at'))
19
+ @run_again_at = @end_at + self.class::INTERVAL
20
+ @time_range = @start_at...@end_at
21
+
22
+ super
23
+
24
+ args['start_at'] = @end_at.to_f
25
+ args['end_at'] = @run_again_at.to_f
26
+ self.class.enqueue(args, run_at: @run_again_at)
17
27
  end
18
28
  end
19
- end
20
29
 
21
- Note that instead of using Time.now in our database query, and requeueing the job at 1.hour.from_now, we use the run_at of the current job as our timestamp. This corrects for delays in running the job. Suppose that there's a backlog of priority jobs, or that the worker briefly goes down, and this job, which was supposed to run at 11:00 a.m. isn't run until 11:05 a.m. A lazier implementation would look for users created after 1.hour.ago, and miss those that signed up between 10:00 a.m. and 10:05 a.m.
30
+ class MyCronJob < CronJob
31
+ INTERVAL = 3600
32
+
33
+ def run(args)
34
+ User.where(created_at: time_range).each { ... }
35
+ end
36
+ end
37
+
38
+ # To enqueue:
39
+ tf = Time.now
40
+ t0 = Time.now - 3600
41
+ MyCronJob.enqueue :start_at => t0.to_f, :end_at => tf.to_f
42
+
43
+ Note that instead of using Time.now in our database query, and requeueing the job at 1.hour.from_now, we use job arguments to track start and end times. This lets us correct for delays in running the job. Suppose that there's a backlog of priority jobs, or that the worker briefly goes down, and this job, which was supposed to run at 11:00 a.m. isn't run until 11:05 a.m. A lazier implementation would look for users created after 1.hour.ago, and miss those that signed up between 10:00 a.m. and 10:05 a.m.
22
44
 
23
45
  This also compensates for clock drift. `Time.now` on one of your application servers may not match `Time.now` on another application server may not match `now()` on your database server. The best way to stay reliable is have a single authoritative source on what the current time is, and your best source for authoritative information is always your database (this is why Que uses Postgres' `now()` function when locking jobs, by the way).
24
46
 
25
47
  Note also the use of the triple-dot range, which results in a query like `SELECT "users".* FROM "users" WHERE ("users"."created_at" >= '2014-01-08 10:00:00.000000' AND "users"."created_at" < '2014-01-08 11:00:00.000000')` instead of a BETWEEN condition. This ensures that a user created at 11:00 am exactly isn't processed twice, by the jobs starting at both 10 am and 11 am.
26
48
 
49
+ Finally, by passing both the start and end times for the period to be processed, and only using the interval to calculate the period for the following job, we make it easy to change the interval at which the job runs, without the risk of missing or double-processing any users.
50
+
27
51
  ### DelayedJob-style Jobs
28
52
 
29
53
  DelayedJob offers a simple API for delaying methods to objects:
@@ -118,7 +118,7 @@ module Que
118
118
 
119
119
  if Que.error_handler
120
120
  # Similarly, protect the work loop from a failure of the error handler.
121
- Que.error_handler.call(error) rescue nil
121
+ Que.error_handler.call(error, job) rescue nil
122
122
  end
123
123
 
124
124
  return {:event => :job_errored, :error => error, :job => job}
@@ -1,3 +1,3 @@
1
1
  module Que
2
- Version = '0.8.2'
2
+ Version = '0.9.0'
3
3
  end
@@ -4,20 +4,60 @@ require 'spec_helper'
4
4
  # stay functional.
5
5
  describe "Customizing Que" do
6
6
  it "Cron should allow for easy recurring jobs" do
7
- class Cron < Que::Job
8
- def run
9
- destroy
10
- self.class.enqueue :run_at => @attrs[:run_at] + 3600
7
+ begin
8
+ class CronJob < Que::Job
9
+ # Default repetition interval in seconds. Can be overridden in
10
+ # subclasses. Can use 1.minute if using Rails.
11
+ INTERVAL = 60
12
+
13
+ attr_reader :start_at, :end_at, :run_again_at, :time_range
14
+
15
+ def _run
16
+ args = attrs[:args].first
17
+ @start_at, @end_at = Time.at(args.delete('start_at')), Time.at(args.delete('end_at'))
18
+ @run_again_at = @end_at + self.class::INTERVAL
19
+ @time_range = @start_at...@end_at
20
+
21
+ super
22
+
23
+ args['start_at'] = @end_at.to_f
24
+ args['end_at'] = @run_again_at.to_f
25
+ self.class.enqueue(args, run_at: @run_again_at)
26
+ end
11
27
  end
12
- end
13
28
 
14
- Cron.enqueue
29
+ class MyCronJob < CronJob
30
+ INTERVAL = 1.5
15
31
 
16
- run_at = DB[:que_jobs].get(:run_at).to_f
32
+ def run(args)
33
+ $args = args.dup
34
+ $time_range = time_range
35
+ end
36
+ end
17
37
 
18
- Que::Job.work
38
+ t = (Time.now - 1000).to_f.round(6)
39
+ MyCronJob.enqueue :start_at => t, :end_at => t + 1.5, :arg => 4
19
40
 
20
- DB[:que_jobs].get(:run_at).to_f.should be_within(0.000001).of(run_at + 3600)
41
+ $args.should be nil
42
+ $time_range.should be nil
43
+
44
+ Que::Job.work
45
+
46
+ $args.should == {'arg' => 4}
47
+ $time_range.begin.to_f.round(6).should be_within(0.000001).of t
48
+ $time_range.end.to_f.round(6).should be_within(0.000001).of t + 1.5
49
+ $time_range.exclude_end?.should be true
50
+
51
+ DB[:que_jobs].get(:run_at).to_f.round(6).should be_within(0.000001).of(t + 3.0)
52
+ args = JSON.parse(DB[:que_jobs].get(:args)).first
53
+ args.keys.should == ['arg', 'start_at', 'end_at']
54
+ args['arg'].should == 4
55
+ args['start_at'].should be_within(0.000001).of(t + 1.5)
56
+ args['end_at'].should be_within(0.000001).of(t + 3.0)
57
+ ensure
58
+ $args = nil
59
+ $time_range = nil
60
+ end
21
61
  end
22
62
 
23
63
  it "Object#delay should allow for simpler job enqueueing" do
@@ -323,6 +323,22 @@ describe Que::Job, '.work' do
323
323
  end
324
324
  end
325
325
 
326
+ it "should pass job to an error handler, if one is defined" do
327
+ begin
328
+ jobs = []
329
+ Que.error_handler = proc { |error, job| jobs << job }
330
+
331
+ ErrorJob.enqueue
332
+ result = Que::Job.work
333
+
334
+ jobs.count.should be 1
335
+ job = jobs[0]
336
+ job.should be result[:job]
337
+ ensure
338
+ Que.error_handler = nil
339
+ end
340
+ end
341
+
326
342
  it "should not do anything if the error handler itelf throws an error" do
327
343
  begin
328
344
  Que.error_handler = proc { |error| raise "Another error!" }
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: 0.8.2
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Hanks
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-12 00:00:00.000000000 Z
11
+ date: 2014-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler