que 0.0.1 → 0.1.0
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.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/README.md +85 -44
- data/Rakefile +412 -0
- data/lib/generators/que/install_generator.rb +22 -0
- data/lib/generators/que/templates/add_que.rb +9 -0
- data/lib/que.rb +55 -5
- data/lib/que/adapters/active_record.rb +9 -0
- data/lib/que/adapters/base.rb +49 -0
- data/lib/que/adapters/connection_pool.rb +14 -0
- data/lib/que/adapters/pg.rb +17 -0
- data/lib/que/adapters/sequel.rb +14 -0
- data/lib/que/job.rb +128 -149
- data/lib/que/railtie.rb +20 -0
- data/lib/que/rake_tasks.rb +35 -0
- data/lib/que/sql.rb +121 -0
- data/lib/que/version.rb +1 -1
- data/lib/que/worker.rb +93 -156
- data/que.gemspec +8 -6
- data/spec/adapters/active_record_spec.rb +39 -0
- data/spec/adapters/connection_pool_spec.rb +12 -0
- data/spec/adapters/pg_spec.rb +5 -0
- data/spec/adapters/sequel_spec.rb +25 -0
- data/spec/connection_spec.rb +12 -0
- data/spec/helper_spec.rb +19 -0
- data/spec/pool_spec.rb +116 -0
- data/spec/queue_spec.rb +134 -0
- data/spec/spec_helper.rb +48 -25
- data/spec/support/helpers.rb +9 -0
- data/spec/support/jobs.rb +33 -0
- data/spec/support/shared_examples/adapter.rb +16 -0
- data/spec/support/shared_examples/multithreaded_adapter.rb +42 -0
- data/spec/work_spec.rb +247 -0
- data/spec/worker_spec.rb +117 -0
- metadata +73 -15
- data/spec/unit/error_spec.rb +0 -45
- data/spec/unit/queue_spec.rb +0 -67
- data/spec/unit/work_spec.rb +0 -168
- data/spec/unit/worker_spec.rb +0 -31
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.0
|
4
|
+
version: 0.1.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: 2013-11-
|
11
|
+
date: 2013-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -73,7 +73,21 @@ dependencies:
|
|
73
73
|
- - '>='
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '0'
|
76
|
-
type: :
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activerecord
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
@@ -87,14 +101,28 @@ dependencies:
|
|
87
101
|
- - '>='
|
88
102
|
- !ruby/object:Gem::Version
|
89
103
|
version: '0'
|
90
|
-
type: :
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: connection_pool
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
91
119
|
prerelease: false
|
92
120
|
version_requirements: !ruby/object:Gem::Requirement
|
93
121
|
requirements:
|
94
122
|
- - '>='
|
95
123
|
- !ruby/object:Gem::Version
|
96
124
|
version: '0'
|
97
|
-
description:
|
125
|
+
description: A job queue that uses PostgreSQL's advisory locks for speed and reliability.
|
98
126
|
email:
|
99
127
|
- christopher.m.hanks@gmail.com
|
100
128
|
executables: []
|
@@ -107,17 +135,37 @@ files:
|
|
107
135
|
- LICENSE.txt
|
108
136
|
- README.md
|
109
137
|
- Rakefile
|
138
|
+
- lib/generators/que/install_generator.rb
|
139
|
+
- lib/generators/que/templates/add_que.rb
|
110
140
|
- lib/que.rb
|
141
|
+
- lib/que/adapters/active_record.rb
|
142
|
+
- lib/que/adapters/base.rb
|
143
|
+
- lib/que/adapters/connection_pool.rb
|
144
|
+
- lib/que/adapters/pg.rb
|
145
|
+
- lib/que/adapters/sequel.rb
|
111
146
|
- lib/que/job.rb
|
147
|
+
- lib/que/railtie.rb
|
148
|
+
- lib/que/rake_tasks.rb
|
149
|
+
- lib/que/sql.rb
|
112
150
|
- lib/que/version.rb
|
113
151
|
- lib/que/worker.rb
|
114
152
|
- que.gemspec
|
153
|
+
- spec/adapters/active_record_spec.rb
|
154
|
+
- spec/adapters/connection_pool_spec.rb
|
155
|
+
- spec/adapters/pg_spec.rb
|
156
|
+
- spec/adapters/sequel_spec.rb
|
157
|
+
- spec/connection_spec.rb
|
158
|
+
- spec/helper_spec.rb
|
159
|
+
- spec/pool_spec.rb
|
160
|
+
- spec/queue_spec.rb
|
115
161
|
- spec/spec_helper.rb
|
116
|
-
- spec/
|
117
|
-
- spec/
|
118
|
-
- spec/
|
119
|
-
- spec/
|
120
|
-
|
162
|
+
- spec/support/helpers.rb
|
163
|
+
- spec/support/jobs.rb
|
164
|
+
- spec/support/shared_examples/adapter.rb
|
165
|
+
- spec/support/shared_examples/multithreaded_adapter.rb
|
166
|
+
- spec/work_spec.rb
|
167
|
+
- spec/worker_spec.rb
|
168
|
+
homepage: https://github.com/chanks/que
|
121
169
|
licenses:
|
122
170
|
- MIT
|
123
171
|
metadata: {}
|
@@ -140,10 +188,20 @@ rubyforge_project:
|
|
140
188
|
rubygems_version: 2.1.9
|
141
189
|
signing_key:
|
142
190
|
specification_version: 4
|
143
|
-
summary:
|
191
|
+
summary: A PostgreSQL-based Job Queue
|
144
192
|
test_files:
|
193
|
+
- spec/adapters/active_record_spec.rb
|
194
|
+
- spec/adapters/connection_pool_spec.rb
|
195
|
+
- spec/adapters/pg_spec.rb
|
196
|
+
- spec/adapters/sequel_spec.rb
|
197
|
+
- spec/connection_spec.rb
|
198
|
+
- spec/helper_spec.rb
|
199
|
+
- spec/pool_spec.rb
|
200
|
+
- spec/queue_spec.rb
|
145
201
|
- spec/spec_helper.rb
|
146
|
-
- spec/
|
147
|
-
- spec/
|
148
|
-
- spec/
|
149
|
-
- spec/
|
202
|
+
- spec/support/helpers.rb
|
203
|
+
- spec/support/jobs.rb
|
204
|
+
- spec/support/shared_examples/adapter.rb
|
205
|
+
- spec/support/shared_examples/multithreaded_adapter.rb
|
206
|
+
- spec/work_spec.rb
|
207
|
+
- spec/worker_spec.rb
|
data/spec/unit/error_spec.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe "Que::Job error handling" do
|
4
|
-
class ErrorJob < Que::Job
|
5
|
-
def perform(*args)
|
6
|
-
raise "Boo!"
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
it "should increment the error_count, persist the error message and reschedule the job" do
|
11
|
-
ErrorJob.queue
|
12
|
-
|
13
|
-
proc { Que::Job.work }.should raise_error RuntimeError, "Boo!"
|
14
|
-
Que::Job.count.should be 1
|
15
|
-
|
16
|
-
job = Que::Job.first
|
17
|
-
data = JSON.load(job.data)
|
18
|
-
data['error_count'].should be 1
|
19
|
-
data['error_message'].should =~ /Boo!/
|
20
|
-
job.run_at.should be_within(1).of Time.now + 4
|
21
|
-
end
|
22
|
-
|
23
|
-
it "should reschedule jobs with exponentially increasing times" do
|
24
|
-
ErrorJob.queue
|
25
|
-
Que::Job.dataset.update :data => JSON.dump(:error_count => 5)
|
26
|
-
|
27
|
-
proc { Que::Job.work }.should raise_error RuntimeError, "Boo!"
|
28
|
-
Que::Job.count.should be 1
|
29
|
-
|
30
|
-
job = Que::Job.first
|
31
|
-
data = JSON.load(job.data)
|
32
|
-
data['error_count'].should be 6
|
33
|
-
data['error_message'].should =~ /Boo!/
|
34
|
-
job.run_at.should be_within(1).of Time.now + 1299
|
35
|
-
end
|
36
|
-
|
37
|
-
it "should handle errors from jobs that cannot be deserialized" do
|
38
|
-
DB[:jobs].insert :type => 'NonexistentJob', :priority => 1
|
39
|
-
proc { Que::Job.work }.should raise_error NameError, /uninitialized constant NonexistentJob/
|
40
|
-
|
41
|
-
job = DB[:jobs].first
|
42
|
-
JSON.load(job[:data])['error_count'].should be 1
|
43
|
-
job[:run_at].should be_within(1).of Time.now + 4
|
44
|
-
end
|
45
|
-
end
|
data/spec/unit/queue_spec.rb
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe "Que::Job.queue" do
|
4
|
-
it "should create a job in the DB" do
|
5
|
-
Que::Job.queue :param1 => 4, :param2 => 'ferret', :param3 => false
|
6
|
-
Que::Job.count.should == 1
|
7
|
-
|
8
|
-
job = Que::Job.first
|
9
|
-
job.type.should == 'Que::Job'
|
10
|
-
job.run_at.should be_within(1).of Time.now
|
11
|
-
job.priority.should be 5 # Defaults to lowest priority.
|
12
|
-
JSON.load(job.args).should == [{'param1' => 4, 'param2' => 'ferret', 'param3' => false}]
|
13
|
-
end
|
14
|
-
|
15
|
-
it "should accept a :run_at argument" do
|
16
|
-
time = Time.at(Time.now.to_i)
|
17
|
-
Que::Job.queue :user_id => 4, :test_number => 8, :run_at => time
|
18
|
-
|
19
|
-
Que::Job.count.should == 1
|
20
|
-
job = Que::Job.first
|
21
|
-
job.type.should == 'Que::Job'
|
22
|
-
job.run_at.should == time
|
23
|
-
job.priority.should == 5
|
24
|
-
JSON.load(job.args).should == [{'user_id' => 4, 'test_number' => 8}]
|
25
|
-
end
|
26
|
-
|
27
|
-
it "should accept a :priority argument" do
|
28
|
-
Que::Job.queue :user_id => 4, :test_number => 8, :priority => 1
|
29
|
-
|
30
|
-
Que::Job.count.should == 1
|
31
|
-
job = Que::Job.first
|
32
|
-
job.type.should == 'Que::Job'
|
33
|
-
job.run_at.should be_within(1).of Time.now
|
34
|
-
job.priority.should be 1
|
35
|
-
JSON.load(job.args).should == [{'user_id' => 4, 'test_number' => 8}]
|
36
|
-
end
|
37
|
-
|
38
|
-
it "should respect a default_priority for the class" do
|
39
|
-
class TestPriorityJob < Que::Job
|
40
|
-
@default_priority = 2
|
41
|
-
end
|
42
|
-
|
43
|
-
TestPriorityJob.queue :user_id => 4, :test_number => 8
|
44
|
-
|
45
|
-
Que::Job.count.should == 1
|
46
|
-
job = Que::Job.first
|
47
|
-
job.type.should == 'TestPriorityJob'
|
48
|
-
job.run_at.should be_within(1).of Time.now
|
49
|
-
job.priority.should be 2
|
50
|
-
JSON.load(job.args).should == [{'user_id' => 4, 'test_number' => 8}]
|
51
|
-
end
|
52
|
-
|
53
|
-
it "should let a :priority option override a default_priority for the class" do
|
54
|
-
class OtherTestPriorityJob < Que::Job
|
55
|
-
@default_priority = 2
|
56
|
-
end
|
57
|
-
|
58
|
-
OtherTestPriorityJob.queue :user_id => 4, :test_number => 8, :priority => 4
|
59
|
-
|
60
|
-
Que::Job.count.should == 1
|
61
|
-
job = Que::Job.first
|
62
|
-
job.type.should == 'OtherTestPriorityJob'
|
63
|
-
job.run_at.should be_within(1).of Time.now
|
64
|
-
job.priority.should be 4
|
65
|
-
JSON.load(job.args).should == [{'user_id' => 4, 'test_number' => 8}]
|
66
|
-
end
|
67
|
-
end
|
data/spec/unit/work_spec.rb
DELETED
@@ -1,168 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe "Que::Job.work" do
|
4
|
-
it "should automatically delete jobs from the database's queue" do
|
5
|
-
Que::Job.count.should be 0
|
6
|
-
Que::Job.queue
|
7
|
-
Que::Job.count.should be 1
|
8
|
-
Que::Job.work
|
9
|
-
Que::Job.count.should be 0
|
10
|
-
end
|
11
|
-
|
12
|
-
it "should pass a job's arguments to its perform method" do
|
13
|
-
class JobWorkTest < Que::Job
|
14
|
-
def perform(*args)
|
15
|
-
$passed_args = args
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
JobWorkTest.queue 5, 'ferret', :lazy => true
|
20
|
-
|
21
|
-
Que::Job.work
|
22
|
-
$passed_args.should == [5, 'ferret', {'lazy' => true}]
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should prefer a job with higher priority" do
|
26
|
-
Que::Job.queue
|
27
|
-
Que::Job.queue :priority => 1
|
28
|
-
|
29
|
-
Que::Job.select_order_map(:priority).should == [1, 5]
|
30
|
-
Que::Job.work
|
31
|
-
Que::Job.select_order_map(:priority).should == [5]
|
32
|
-
end
|
33
|
-
|
34
|
-
it "should prefer a job that was scheduled to run longer ago" do
|
35
|
-
now = Time.at(Time.now.to_i) # Prevent rounding errors by rounding to the nearest second.
|
36
|
-
recently = now - 60
|
37
|
-
long_ago = now - 61
|
38
|
-
|
39
|
-
Que::Job.queue :run_at => recently
|
40
|
-
Que::Job.queue :run_at => long_ago
|
41
|
-
|
42
|
-
Que::Job.select_order_map(:run_at).should == [long_ago, recently]
|
43
|
-
Que::Job.work
|
44
|
-
Que::Job.select_order_map(:run_at).should == [recently]
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should only work a job whose run_at has already passed" do
|
48
|
-
now = Time.at(Time.now.to_i) # Prevent rounding errors by rounding to the nearest second.
|
49
|
-
past = now - 60
|
50
|
-
soon = now + 60
|
51
|
-
|
52
|
-
Que::Job.queue :run_at => past
|
53
|
-
Que::Job.queue :run_at => soon
|
54
|
-
|
55
|
-
Que::Job.select_order_map(:run_at).should == [past, soon]
|
56
|
-
Que::Job.work
|
57
|
-
Que::Job.select_order_map(:run_at).should == [soon]
|
58
|
-
Que::Job.work
|
59
|
-
Que::Job.select_order_map(:run_at).should == [soon]
|
60
|
-
end
|
61
|
-
|
62
|
-
it "should prefer a job that was scheduled earlier, and therefore has a lower job_id" do
|
63
|
-
time = Time.now - 60
|
64
|
-
Que::Job.queue :run_at => time
|
65
|
-
Que::Job.queue :run_at => time
|
66
|
-
|
67
|
-
a, b = Que::Job.select_order_map(:job_id)
|
68
|
-
Que::Job.work
|
69
|
-
Que::Job.select_order_map(:job_id).should == [b]
|
70
|
-
end
|
71
|
-
|
72
|
-
it "should lock the job it selects" do
|
73
|
-
$q1, $q2 = Queue.new, Queue.new
|
74
|
-
|
75
|
-
class LockJob < Que::Job
|
76
|
-
def perform(*args)
|
77
|
-
$q1.push nil
|
78
|
-
$q2.pop
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
job = LockJob.queue
|
83
|
-
@thread = Thread.new { Que::Job.work }
|
84
|
-
|
85
|
-
# Wait until job is being worked.
|
86
|
-
$q1.pop
|
87
|
-
|
88
|
-
# Job should be advisory-locked...
|
89
|
-
DB.select{pg_try_advisory_lock(job.job_id)}.single_value.should be false
|
90
|
-
|
91
|
-
# ...and Job.work should ignore advisory-locked jobs.
|
92
|
-
Que::Job.work.should be nil
|
93
|
-
|
94
|
-
# Let LockJob finish.
|
95
|
-
$q2.push nil
|
96
|
-
|
97
|
-
# Make sure there aren't any errors.
|
98
|
-
@thread.join
|
99
|
-
end
|
100
|
-
|
101
|
-
it "that raises a Que::Job::Retry should abort the job, leaving it to be retried" do
|
102
|
-
class RetryJob < Que::Job
|
103
|
-
def perform(*args)
|
104
|
-
raise Que::Job::Retry
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
job = RetryJob.queue
|
109
|
-
Que::Job.count.should be 1
|
110
|
-
Que::Job.work
|
111
|
-
Que::Job.count.should be 1
|
112
|
-
|
113
|
-
same_job = Que::Job.first
|
114
|
-
same_job.job_id.should == job.job_id
|
115
|
-
same_job.run_at.should == job.run_at
|
116
|
-
same_job.data['error_count'].should be nil
|
117
|
-
end
|
118
|
-
|
119
|
-
it "should handle subclasses of other jobs" do
|
120
|
-
$class_job_array = []
|
121
|
-
|
122
|
-
class ClassJob < Que::Job
|
123
|
-
def perform(*args)
|
124
|
-
$class_job_array << 2
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
class SubclassJob < ClassJob
|
129
|
-
def perform(*args)
|
130
|
-
$class_job_array << 1
|
131
|
-
super
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
SubclassJob.queue
|
136
|
-
Que::Job.get(:type).should == 'SubclassJob'
|
137
|
-
Que::Job.work
|
138
|
-
$class_job_array.should == [1, 2]
|
139
|
-
end
|
140
|
-
|
141
|
-
describe "should support a logger" do
|
142
|
-
before do
|
143
|
-
@logger = Object.new
|
144
|
-
def @logger.method_missing(m, message)
|
145
|
-
@messages ||= []
|
146
|
-
@messages << message
|
147
|
-
end
|
148
|
-
Que.logger = @logger
|
149
|
-
end
|
150
|
-
|
151
|
-
after do
|
152
|
-
Que.logger = nil
|
153
|
-
end
|
154
|
-
|
155
|
-
def messages
|
156
|
-
@logger.instance_variable_get(:@messages)
|
157
|
-
end
|
158
|
-
|
159
|
-
it "should write messages to the logger" do
|
160
|
-
Que::Job.queue
|
161
|
-
Que::Job.work
|
162
|
-
|
163
|
-
messages.should be_an_instance_of Array
|
164
|
-
messages.length.should == 1
|
165
|
-
messages[0].should =~ /\AWorked job in/
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
data/spec/unit/worker_spec.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Que::Worker do
|
4
|
-
class ExceptionJob < Que::Job
|
5
|
-
def perform(*args)
|
6
|
-
raise "Blah!"
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
it "should not be taken out by an error, and keep looking for jobs" do
|
11
|
-
ExceptionJob.queue
|
12
|
-
Que::Job.queue
|
13
|
-
|
14
|
-
Que::Worker.state = :async
|
15
|
-
Que::Worker.wake!
|
16
|
-
@worker = Que::Worker.workers.first
|
17
|
-
|
18
|
-
{} until @worker.thread[:state] == :sleeping
|
19
|
-
|
20
|
-
# Job was worked, ExceptionJob remains.
|
21
|
-
Que::Job.select_map(:type).should == ['ExceptionJob']
|
22
|
-
end
|
23
|
-
|
24
|
-
it "#async? and #up? should return whether Worker is async and whether there are workers running, respectively" do
|
25
|
-
Que::Worker.should_not be_async
|
26
|
-
Que::Worker.should_not be_up
|
27
|
-
Que::Worker.state = :async
|
28
|
-
Que::Worker.should be_async
|
29
|
-
Que::Worker.should be_up
|
30
|
-
end
|
31
|
-
end
|