quebert 3.0.3 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +2 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +2 -1
- data/README.md +22 -1
- data/lib/quebert/async_sender/instance.rb +2 -0
- data/lib/quebert/controller/beanstalk.rb +25 -7
- data/lib/quebert/job.rb +15 -0
- data/lib/quebert/version.rb +1 -1
- data/spec/async_sender_spec.rb +10 -10
- data/spec/backend_spec.rb +19 -19
- data/spec/command_line_runner_spec.rb +31 -31
- data/spec/configuration_spec.rb +4 -4
- data/spec/consumer_spec.rb +47 -41
- data/spec/job_spec.rb +101 -40
- data/spec/quebert_spec.rb +2 -3
- data/spec/serializer_spec.rb +24 -24
- data/spec/support/jobs.rb +1 -1
- data/spec/support_spec.rb +8 -8
- data/spec/worker_spec.rb +9 -9
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: baaa9b7110f75a9e262655df715e382e9ed077ef
|
4
|
+
data.tar.gz: e7a853977371a583075d20f49b4ff3d60d8a3b46
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c1f3b987b48dcdd55440807402135e07c29f918a2912fbc5996121c81508b083db251d558d40491ea3a09452c63b6e799f04cff1879afd7bc2404dacaed0587
|
7
|
+
data.tar.gz: 38afc9cb326c59c5da71fb1d96396c3aac0eb4cc889d2e5288b8673792f7150b33a705bf7dea45cc0828246b54b669516f8dcefae78d2d30e35329d62b45c58b
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.4.1
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -33,6 +33,11 @@ Some features that are currently *missing* that I will soon add include:
|
|
33
33
|
|
34
34
|
There are two ways to enqueue jobs with Quebert: through the Job itself, provided you set a default back-end for the job, or put it on the backend.
|
35
35
|
|
36
|
+
### Supported Ruby Versions
|
37
|
+
|
38
|
+
Quebert officially is supported to run on the currently supported versions of MRI.
|
39
|
+
This includes versions >= `2.2.5`. Have a look at the `.travis.yml` configuration file to see all Ruby versions we support.
|
40
|
+
|
36
41
|
### Jobs
|
37
42
|
|
38
43
|
Quebert includes a Job class so you can implement how you want certain types of Jobs performed.
|
@@ -91,7 +96,7 @@ Quebert.config.from_hash(Rails.application.config.quebert)
|
|
91
96
|
Quebert.config.logger = Rails.logger
|
92
97
|
```
|
93
98
|
|
94
|
-
### Job
|
99
|
+
### Global Job hooks
|
95
100
|
|
96
101
|
Quebert has support for providing custom hooks to be called before, after & around your jobs are being run.
|
97
102
|
A common example is making sure that any active ActiveRecord database connections are put back on the connection pool after a job is done:
|
@@ -112,6 +117,22 @@ Quebert.config.around_job do |job|
|
|
112
117
|
end
|
113
118
|
```
|
114
119
|
|
120
|
+
### Beanstalk Job hooks
|
121
|
+
|
122
|
+
Jobs can define their own business logic that will get called surrounding Beanstalk events:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
class FooJob < Quebert::Job
|
126
|
+
def around_bury
|
127
|
+
# custom pre-bury code
|
128
|
+
yield
|
129
|
+
# custom post-bury code
|
130
|
+
end
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
Supported Beanstalk event hooks: `around_bury`, `around_release`, `around_delete`
|
135
|
+
|
115
136
|
### Async sender
|
116
137
|
|
117
138
|
Take any ol' class and include the Quebert::AsyncSender.
|
@@ -28,22 +28,22 @@ module Quebert
|
|
28
28
|
result = false
|
29
29
|
time = Benchmark.realtime do
|
30
30
|
result = job.perform!
|
31
|
-
|
31
|
+
delete
|
32
32
|
end
|
33
33
|
|
34
34
|
logger.info(job) { "Completed in #{(time*1000*1000).to_i/1000.to_f} ms\n" }
|
35
35
|
result
|
36
36
|
rescue Job::Delete
|
37
37
|
logger.info(job) { "Deleting job" }
|
38
|
-
|
38
|
+
delete
|
39
39
|
logger.info(job) { "Job deleted" }
|
40
40
|
rescue Job::Release
|
41
41
|
logger.info(job) { "Releasing with priority: #{job.priority} and delay: #{job.delay}" }
|
42
|
-
|
42
|
+
release(pri: job.priority, delay: job.delay)
|
43
43
|
logger.info(job) { "Job released" }
|
44
44
|
rescue Job::Bury
|
45
45
|
logger.info(job) { "Burying job" }
|
46
|
-
|
46
|
+
bury
|
47
47
|
logger.info(job) { "Job buried" }
|
48
48
|
rescue Job::Timeout => e
|
49
49
|
logger.info(job) { "Job timed out. Retrying with delay. #{e.inspect} #{e.backtrace.join("\n")}" }
|
@@ -56,7 +56,7 @@ module Quebert
|
|
56
56
|
retry_with_delay
|
57
57
|
rescue => e
|
58
58
|
logger.error(job) { "Error caught on perform. Burying job. #{e.inspect} #{e.backtrace.join("\n")}" }
|
59
|
-
|
59
|
+
bury
|
60
60
|
logger.error(job) { "Job buried" }
|
61
61
|
raise
|
62
62
|
end
|
@@ -67,11 +67,11 @@ module Quebert
|
|
67
67
|
|
68
68
|
if delay > MAX_TIMEOUT_RETRY_DELAY
|
69
69
|
logger.error(job) { "Max retry delay exceeded. Burying job" }
|
70
|
-
|
70
|
+
bury
|
71
71
|
logger.error(job) { "Job buried" }
|
72
72
|
else
|
73
73
|
logger.error(job) { "TTR exceeded. Releasing with priority: #{job.priority} and delay: #{delay}" }
|
74
|
-
|
74
|
+
release(pri: job.priority, delay: delay)
|
75
75
|
logger.error(job) { "Job released" }
|
76
76
|
end
|
77
77
|
rescue ::Beaneater::NotFoundError
|
@@ -79,6 +79,24 @@ module Quebert
|
|
79
79
|
# Sometimes the timer doesn't behave correctly and this job actually runs longer than
|
80
80
|
# allowed. At that point the beanstalk job no longer exists anymore. Lets let it go and don't blow up.
|
81
81
|
end
|
82
|
+
|
83
|
+
def bury
|
84
|
+
job.around_bury do
|
85
|
+
beanstalk_job.bury
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def release(opts)
|
90
|
+
job.around_release do
|
91
|
+
beanstalk_job.release(opts)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def delete
|
96
|
+
job.around_delete do
|
97
|
+
beanstalk_job.delete
|
98
|
+
end
|
99
|
+
end
|
82
100
|
end
|
83
101
|
end
|
84
102
|
end
|
data/lib/quebert/job.rb
CHANGED
@@ -89,6 +89,21 @@ module Quebert
|
|
89
89
|
@backend || Quebert.configuration.backend
|
90
90
|
end
|
91
91
|
|
92
|
+
# Event hook that can be overridden
|
93
|
+
def around_bury
|
94
|
+
yield
|
95
|
+
end
|
96
|
+
|
97
|
+
# Event hook that can be overridden
|
98
|
+
def around_release
|
99
|
+
yield
|
100
|
+
end
|
101
|
+
|
102
|
+
# Event hook that can be overridden
|
103
|
+
def around_delete
|
104
|
+
yield
|
105
|
+
end
|
106
|
+
|
92
107
|
protected
|
93
108
|
def delete!
|
94
109
|
raise Delete
|
data/lib/quebert/version.rb
CHANGED
data/spec/async_sender_spec.rb
CHANGED
@@ -36,34 +36,34 @@ describe AsyncSender::Class do
|
|
36
36
|
describe "#async_send" do
|
37
37
|
it "should async send class methods" do
|
38
38
|
Greeter.async_send(:hi, 'Jeannette')
|
39
|
-
@q.reserve.perform.
|
39
|
+
expect(@q.reserve.perform).to eql(Greeter.send(:hi, 'Jeannette'))
|
40
40
|
end
|
41
41
|
|
42
42
|
it "should async send instance methods" do
|
43
43
|
Greeter.new("brad").async_send(:hi, 'stunning')
|
44
|
-
@q.reserve.perform.
|
44
|
+
expect(@q.reserve.perform).to eql(Greeter.new("brad").hi('stunning'))
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
48
|
describe "#async() promise" do
|
49
49
|
it "should async send class methods" do
|
50
50
|
Greeter.async.hi('Jeannette')
|
51
|
-
@q.reserve.perform.
|
51
|
+
expect(@q.reserve.perform).to eql(Greeter.send(:hi, 'Jeannette'))
|
52
52
|
end
|
53
53
|
|
54
54
|
it "should async send instance methods" do
|
55
55
|
Greeter.new("brad").async.hi('stunning')
|
56
|
-
@q.reserve.perform.
|
56
|
+
expect(@q.reserve.perform).to eql(Greeter.new("brad").hi('stunning'))
|
57
57
|
end
|
58
58
|
|
59
59
|
it "should async send private instance methods" do
|
60
60
|
Greeter.new("brad").async.send(:bye, 'stunning')
|
61
|
-
@q.reserve.perform.
|
61
|
+
expect(@q.reserve.perform).to eql(Greeter.new("brad").send(:bye, 'stunning'))
|
62
62
|
end
|
63
63
|
|
64
64
|
it "should async send private class methods" do
|
65
65
|
Greeter.async.send(:bye, 'Jeannette')
|
66
|
-
@q.reserve.perform.
|
66
|
+
expect(@q.reserve.perform).to eql(Greeter.send(:bye, 'Jeannette'))
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
@@ -89,7 +89,7 @@ describe AsyncSender::ActiveRecord do
|
|
89
89
|
|
90
90
|
it "should async_send instance method" do
|
91
91
|
User.first.async_send(:name)
|
92
|
-
@q.reserve.perform.
|
92
|
+
expect(@q.reserve.perform).to eql(User.first.name)
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
@@ -101,20 +101,20 @@ describe AsyncSender::ActiveRecord do
|
|
101
101
|
u.send(:write_attribute, :last_name, "Jones")
|
102
102
|
end
|
103
103
|
user.async.name
|
104
|
-
@q.reserve.perform.
|
104
|
+
expect(@q.reserve.perform).to eql("Barf Jones")
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
108
108
|
it "should async class method" do
|
109
109
|
email = "brad@bradgessler.com"
|
110
110
|
User.async.emailizer(email)
|
111
|
-
@q.reserve.perform.
|
111
|
+
expect(@q.reserve.perform).to eql(email)
|
112
112
|
end
|
113
113
|
|
114
114
|
it "should async_send and successfully serialize param object" do
|
115
115
|
user = User.new(:first_name => 'Brad')
|
116
116
|
user2 = User.new(:first_name => 'Steel')
|
117
117
|
user.async.email!(user2)
|
118
|
-
@q.reserve.perform.first_name.
|
118
|
+
expect(@q.reserve.perform.first_name).to eql('Steel')
|
119
119
|
end
|
120
120
|
end
|
data/spec/backend_spec.rb
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Backend do
|
4
|
-
it "
|
5
|
-
Quebert.backends.keys.
|
4
|
+
it "has register backends" do
|
5
|
+
expect(Quebert.backends.keys).to match_array([:in_process, :beanstalk, :sync])
|
6
6
|
end
|
7
7
|
|
8
|
-
it "
|
8
|
+
it "registers backends" do
|
9
9
|
Quebert.backends.register :twenty, 20
|
10
|
-
Quebert.backends[:twenty].
|
10
|
+
expect(Quebert.backends[:twenty]).to eql(20)
|
11
11
|
end
|
12
12
|
|
13
|
-
it "
|
13
|
+
it "unregisters backends" do
|
14
14
|
Quebert.backends.unregister :twenty
|
15
|
-
Quebert.backends[:twenty].
|
15
|
+
expect(Quebert.backends[:twenty]).to be_nil
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -21,15 +21,15 @@ describe Backend::InProcess do
|
|
21
21
|
@q = Backend::InProcess.new
|
22
22
|
end
|
23
23
|
|
24
|
-
it "
|
24
|
+
it "puts on queue" do
|
25
25
|
3.times do |num|
|
26
26
|
@q.put Adder.new(num)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
it "
|
30
|
+
it "consumes from queue" do
|
31
31
|
3.times do |num|
|
32
|
-
@q.reserve.perform.
|
32
|
+
expect(@q.reserve.perform).to eql(num)
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
@@ -40,19 +40,19 @@ describe Backend::Beanstalk do
|
|
40
40
|
@q.drain!
|
41
41
|
end
|
42
42
|
|
43
|
-
it "
|
43
|
+
it "puts on queue" do
|
44
44
|
3.times do |num|
|
45
45
|
@q.put Adder.new(num)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
it "
|
49
|
+
it "consumes from queue" do
|
50
50
|
3.times do |num|
|
51
|
-
@q.reserve.perform.
|
51
|
+
expect(@q.reserve.perform).to eql(num)
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
it "
|
55
|
+
it "consumes from multiple queues" do
|
56
56
|
@q.queues = ["a", "b"]
|
57
57
|
job1 = Adder.new(1)
|
58
58
|
job1.queue = "a"
|
@@ -60,8 +60,8 @@ describe Backend::Beanstalk do
|
|
60
60
|
job2 = Adder.new(2)
|
61
61
|
job2.queue = "b"
|
62
62
|
@q.put(job2)
|
63
|
-
@q.reserve.perform.
|
64
|
-
@q.reserve.perform.
|
63
|
+
expect(@q.reserve.perform).to eql(1)
|
64
|
+
expect(@q.reserve.perform).to eql(2)
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
@@ -70,15 +70,15 @@ describe Backend::Sync do
|
|
70
70
|
@q = Backend::Sync.new
|
71
71
|
end
|
72
72
|
|
73
|
-
it "
|
73
|
+
it "puts on queue" do
|
74
74
|
3.times do |num|
|
75
|
-
@q.put(Adder.new(num)).
|
75
|
+
expect(@q.put(Adder.new(num))).to eql(num)
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
-
it "
|
79
|
+
it "does nothing when consuming from queue" do
|
80
80
|
3.times do |num|
|
81
|
-
@q.reserve.perform.
|
81
|
+
expect(@q.reserve.perform).to be_nil
|
82
82
|
end
|
83
83
|
end
|
84
84
|
end
|
@@ -4,74 +4,74 @@ describe CommandLineRunner do
|
|
4
4
|
before(:all) do
|
5
5
|
Quebert.config.backend = Backend::InProcess.new
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
context "log-file" do
|
9
|
-
it "
|
9
|
+
it "writes log file" do
|
10
10
|
clean_file 'log.log' do
|
11
|
-
|
11
|
+
expect {
|
12
12
|
CommandLineRunner.dispatch(%w(worker --log log.log))
|
13
|
-
}.
|
13
|
+
}.to change { File.read('log.log') if File.exists?('log.log') }
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
context "pid-file" do
|
19
|
-
it "
|
19
|
+
it "writes pid" do
|
20
20
|
clean_file 'pid.pid' do
|
21
|
-
File.exists?('pid').
|
21
|
+
expect(File.exists?('pid')).to be_falsey
|
22
22
|
CommandLineRunner.dispatch(%w(worker --pid pid.pid))
|
23
|
-
Support::PidFile.read('pid.pid').
|
23
|
+
expect(Support::PidFile.read('pid.pid')).to eql(Process.pid)
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
27
|
-
it "
|
26
|
+
|
27
|
+
it "removes stale" do
|
28
28
|
clean_file 'pid.pid', "-1" do
|
29
29
|
CommandLineRunner.dispatch(%w(worker --pid pid.pid))
|
30
|
-
Support::PidFile.read('pid.pid').
|
30
|
+
expect(Support::PidFile.read('pid.pid')).to eql(Process.pid)
|
31
31
|
end
|
32
32
|
end
|
33
|
-
|
34
|
-
it "
|
33
|
+
|
34
|
+
it "complains if the pid is already running" do
|
35
35
|
clean_file 'pid.pid', Process.pid do
|
36
|
-
|
36
|
+
expect {
|
37
37
|
CommandLineRunner.dispatch(%w(worker --pid pid.pid))
|
38
|
-
}.
|
39
|
-
Support::PidFile.read('pid.pid').
|
38
|
+
}.to raise_exception(Support::PidFile::ProcessRunning)
|
39
|
+
expect(Support::PidFile.read('pid.pid')).to eql(Process.pid)
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
context "config-file" do
|
45
|
-
it "
|
45
|
+
it "auto-detects rails environment file" do
|
46
46
|
clean_file './config/environment.rb', "raise 'RailsConfig'" do
|
47
|
-
|
47
|
+
expect {
|
48
48
|
CommandLineRunner.dispatch(%w(worker))
|
49
|
-
}.
|
49
|
+
}.to raise_exception('RailsConfig')
|
50
50
|
end
|
51
51
|
end
|
52
|
-
|
53
|
-
it "
|
52
|
+
|
53
|
+
it "runs config file" do
|
54
54
|
clean_file './super_awesome.rb', "raise 'SuperAwesome'" do
|
55
|
-
|
55
|
+
expect {
|
56
56
|
CommandLineRunner.dispatch(%w(worker --config ./super_awesome.rb))
|
57
|
-
}.
|
57
|
+
}.to raise_exception('SuperAwesome')
|
58
58
|
end
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
context "chdir" do
|
64
64
|
before(:each) do
|
65
65
|
@chdir = Dir.pwd
|
66
66
|
end
|
67
|
-
|
68
|
-
it "
|
67
|
+
|
68
|
+
it "changes chdir" do
|
69
69
|
CommandLineRunner.dispatch(%w(worker --chdir /))
|
70
|
-
Dir.pwd.
|
70
|
+
expect(Dir.pwd).to eql('/')
|
71
71
|
end
|
72
|
-
|
72
|
+
|
73
73
|
after(:each) do
|
74
74
|
Dir.chdir(@chdir)
|
75
75
|
end
|
76
76
|
end
|
77
|
-
end
|
77
|
+
end
|
data/spec/configuration_spec.rb
CHANGED
@@ -11,11 +11,11 @@ describe Configuration do
|
|
11
11
|
|
12
12
|
it "should configure backend" do
|
13
13
|
backend = @config.backend
|
14
|
-
backend.
|
14
|
+
expect(backend).to be_instance_of(Quebert::Backend::Beanstalk)
|
15
15
|
# Blech, gross nastiness in their lib, but we need to look in to see if this stuff as configed
|
16
|
-
backend.send(:beanstalkd_connection).connection.host.
|
17
|
-
backend.send(:beanstalkd_connection).connection.port.
|
18
|
-
backend.send(:default_tube).name.
|
16
|
+
expect(backend.send(:beanstalkd_connection).connection.host).to eql("localhost")
|
17
|
+
expect(backend.send(:beanstalkd_connection).connection.port).to eql(11300)
|
18
|
+
expect(backend.send(:default_tube).name).to eql("quebert-config-test")
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
data/spec/consumer_spec.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Controller::Base do
|
4
|
-
it "
|
5
|
-
|
4
|
+
it "performs a job" do
|
5
|
+
expect(
|
6
|
+
Controller::Base.new(Adder.new(1,2)).perform
|
7
|
+
).to eql(3)
|
6
8
|
end
|
7
9
|
|
8
|
-
it "
|
10
|
+
it "rescues all raised job actions" do
|
9
11
|
[ReleaseJob, DeleteJob, BuryJob].each do |job|
|
10
|
-
|
12
|
+
expect {
|
11
13
|
Controller::Base.new(job.new).perform
|
12
|
-
}.
|
14
|
+
}.to_not raise_exception
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
@@ -24,76 +26,80 @@ describe Controller::Beanstalk do
|
|
24
26
|
@q.drain!
|
25
27
|
end
|
26
28
|
|
27
|
-
it "
|
29
|
+
it "deletes job off queue after succesful run" do
|
28
30
|
@q.put Adder.new(1, 2)
|
29
|
-
@q.peek(:ready).
|
30
|
-
@q.reserve.perform.
|
31
|
-
@q.peek(:ready).
|
31
|
+
expect(@q.peek(:ready)).to_not be_nil
|
32
|
+
expect(@q.reserve.perform).to eql(3)
|
33
|
+
expect(@q.peek(:ready)).to be_nil
|
32
34
|
end
|
33
35
|
|
34
|
-
it "
|
36
|
+
it "buries job if an exception occurs in job" do
|
35
37
|
@q.put Exceptional.new
|
36
|
-
@q.peek(:ready).
|
37
|
-
|
38
|
-
@q.peek(:buried).
|
38
|
+
expect(@q.peek(:ready)).to_not be_nil
|
39
|
+
expect { @q.reserve.perform }.to raise_exception(RuntimeError, "Exceptional")
|
40
|
+
expect(@q.peek(:buried)).to_not be_nil
|
39
41
|
end
|
40
42
|
|
41
|
-
it "
|
43
|
+
it "buries an AR job if an exception occurs deserializing it" do
|
42
44
|
tube = @q.send(:default_tube)
|
43
45
|
tube.put({:foo => "bar"}.to_json)
|
44
|
-
tube.peek(:ready).
|
45
|
-
|
46
|
-
tube.peek(:buried).
|
46
|
+
expect(tube.peek(:ready)).to_not be_nil
|
47
|
+
expect { @q.reserve.perform }.to raise_exception(NoMethodError)
|
48
|
+
expect(tube.peek(:buried)).to_not be_nil
|
47
49
|
end
|
48
50
|
|
49
51
|
context "job actions" do
|
50
|
-
it "
|
52
|
+
it "deletes a job" do
|
51
53
|
@q.put DeleteJob.new
|
52
|
-
@q.peek(:ready).
|
54
|
+
expect(@q.peek(:ready)).to_not be_nil
|
53
55
|
@q.reserve.perform
|
54
|
-
@q.peek(:ready).
|
56
|
+
expect(@q.peek(:ready)).to be_nil
|
55
57
|
end
|
56
58
|
|
57
|
-
it "
|
59
|
+
it "releases a job" do
|
58
60
|
@q.put ReleaseJob.new
|
59
|
-
@q.peek(:ready).
|
61
|
+
expect(@q.peek(:ready)).to_not be_nil
|
60
62
|
@q.reserve.perform
|
61
|
-
@q.peek(:ready).
|
63
|
+
expect(@q.peek(:ready)).to_not be_nil
|
62
64
|
end
|
63
65
|
|
64
|
-
it "
|
66
|
+
it "buries a job" do
|
65
67
|
@q.put BuryJob.new
|
66
|
-
@q.peek(:ready).
|
67
|
-
@q.peek(:buried).
|
68
|
+
expect(@q.peek(:ready)).to_not be_nil
|
69
|
+
expect(@q.peek(:buried)).to be_nil
|
68
70
|
@q.reserve.perform
|
69
|
-
@q.peek(:ready).
|
70
|
-
@q.peek(:buried).
|
71
|
+
expect(@q.peek(:ready)).to be_nil
|
72
|
+
expect(@q.peek(:buried)).to_not be_nil
|
71
73
|
end
|
72
74
|
end
|
73
75
|
|
74
|
-
it "
|
76
|
+
it "retries a job with a delay and then buries it" do
|
75
77
|
TimeoutJob.backend = @q
|
76
78
|
TimeoutJob.new.enqueue
|
77
|
-
@q.peek(:ready).
|
79
|
+
expect(@q.peek(:ready)).to_not be_nil
|
78
80
|
job = @q.reserve
|
79
|
-
job.beanstalk_job.stats["releases"].
|
80
|
-
job.beanstalk_job.stats["delay"].
|
81
|
-
|
81
|
+
expect(job.beanstalk_job.stats["releases"]).to eql(0)
|
82
|
+
expect(job.beanstalk_job.stats["delay"]).to eql(0)
|
83
|
+
expect {
|
84
|
+
job.perform
|
85
|
+
}.to raise_exception(Quebert::Job::Timeout)
|
82
86
|
|
83
|
-
@q.peek(:ready).
|
87
|
+
expect(@q.peek(:ready)).to be_nil
|
84
88
|
beanstalk_job = @q.peek(:delayed)
|
85
|
-
beanstalk_job.
|
86
|
-
beanstalk_job.stats["releases"].
|
87
|
-
beanstalk_job.stats["delay"].
|
89
|
+
expect(beanstalk_job).to_not be_nil
|
90
|
+
expect(beanstalk_job.stats["releases"]).to eql(1)
|
91
|
+
expect(beanstalk_job.stats["delay"]).to eql(Quebert::Controller::Beanstalk::TIMEOUT_RETRY_GROWTH_RATE**beanstalk_job.stats["releases"])
|
88
92
|
|
89
93
|
sleep(3)
|
90
94
|
|
91
95
|
# lets set the max retry delay so it should bury instead of delay
|
92
96
|
redefine_constant Quebert::Controller::Beanstalk, :MAX_TIMEOUT_RETRY_DELAY, 1
|
93
|
-
|
97
|
+
expect {
|
98
|
+
@q.reserve.perform
|
99
|
+
}.to raise_exception(Quebert::Job::Timeout)
|
94
100
|
|
95
|
-
@q.peek(:ready).
|
96
|
-
@q.peek(:delayed).
|
97
|
-
@q.peek(:buried).
|
101
|
+
expect(@q.peek(:ready)).to be_nil
|
102
|
+
expect(@q.peek(:delayed)).to be_nil
|
103
|
+
expect(@q.peek(:buried)).to_not be_nil
|
98
104
|
end
|
99
105
|
end
|
data/spec/job_spec.rb
CHANGED
@@ -7,21 +7,23 @@ describe Quebert::Job do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
it "shoud initialize with block" do
|
10
|
-
|
10
|
+
expect(
|
11
|
+
Adder.new(1,2,3){|a| a.priority = 8080 }.priority
|
12
|
+
).to eql(8080)
|
11
13
|
end
|
12
14
|
|
13
15
|
it "should perform!" do
|
14
|
-
Adder.new(1,2,3).perform
|
16
|
+
expect(Adder.new(1,2,3).perform!).to eql(6)
|
15
17
|
end
|
16
18
|
|
17
19
|
it "should perform 0 arg jobs" do
|
18
|
-
Adder.new.perform
|
20
|
+
expect(Adder.new.perform!).to eql(0)
|
19
21
|
end
|
20
22
|
|
21
23
|
it "should raise not implemented on base job" do
|
22
|
-
|
24
|
+
expect {
|
23
25
|
Job.new.perform
|
24
|
-
}.
|
26
|
+
}.to raise_exception(NotImplementedError)
|
25
27
|
end
|
26
28
|
|
27
29
|
it "should convert job to and from JSON" do
|
@@ -30,69 +32,69 @@ describe Quebert::Job do
|
|
30
32
|
job.queue = "foo"
|
31
33
|
serialized = job.to_json
|
32
34
|
unserialized = Adder.from_json(serialized)
|
33
|
-
unserialized.
|
34
|
-
unserialized.args.
|
35
|
-
unserialized.queue.
|
35
|
+
expect(unserialized).to be_a(Adder)
|
36
|
+
expect(unserialized.args).to eql(args)
|
37
|
+
expect(unserialized.queue).to eql("foo")
|
36
38
|
end
|
37
39
|
|
38
40
|
it "should have default MEDIUM priority" do
|
39
|
-
Job.new.priority.
|
41
|
+
expect(Job.new.priority).to eql(Quebert::Job::Priority::MEDIUM)
|
40
42
|
end
|
41
43
|
|
42
44
|
describe "Quebert::Job::Priority" do
|
43
45
|
it "should have LOW priority of 4294967296" do
|
44
|
-
Quebert::Job::Priority::LOW.
|
46
|
+
expect(Quebert::Job::Priority::LOW).to eql(4294967296)
|
45
47
|
end
|
46
48
|
it "should have MEDIUM priority of 2147483648" do
|
47
|
-
Quebert::Job::Priority::MEDIUM.
|
49
|
+
expect(Quebert::Job::Priority::MEDIUM).to eql(2147483648)
|
48
50
|
end
|
49
51
|
it "should have HIGH priority of 0" do
|
50
|
-
Quebert::Job::Priority::HIGH.
|
52
|
+
expect(Quebert::Job::Priority::HIGH).to eql(0)
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
56
|
context "actions" do
|
55
57
|
it "should raise release" do
|
56
|
-
|
58
|
+
expect {
|
57
59
|
ReleaseJob.new.perform
|
58
|
-
}.
|
60
|
+
}.to raise_exception(Job::Release)
|
59
61
|
end
|
60
62
|
|
61
63
|
it "should raise delete" do
|
62
|
-
|
64
|
+
expect {
|
63
65
|
DeleteJob.new.perform
|
64
|
-
}.
|
66
|
+
}.to raise_exception(Job::Delete)
|
65
67
|
end
|
66
68
|
|
67
69
|
it "should raise bury" do
|
68
|
-
|
70
|
+
expect {
|
69
71
|
BuryJob.new.perform
|
70
|
-
}.
|
72
|
+
}.to raise_exception(Job::Bury)
|
71
73
|
end
|
72
74
|
end
|
73
75
|
|
74
76
|
context "job queue" do
|
75
77
|
it "should enqueue" do
|
76
|
-
|
78
|
+
expect {
|
77
79
|
Adder.new(1,2,3).enqueue
|
78
|
-
}.
|
80
|
+
}.to change(@q, :size).by(1)
|
79
81
|
end
|
80
82
|
|
81
83
|
context "#enqueue options" do
|
82
84
|
let(:job){ Adder.new(1,2,3) }
|
83
85
|
|
84
86
|
it "should enqueue with pri" do
|
85
|
-
job.
|
87
|
+
expect(job).to receive(:pri=).with(100)
|
86
88
|
job.enqueue(:pri => 100)
|
87
89
|
end
|
88
90
|
|
89
91
|
it "should enqueue with ttr" do
|
90
|
-
job.
|
92
|
+
expect(job).to receive(:ttr=).with(90)
|
91
93
|
job.enqueue(:ttr => 90)
|
92
94
|
end
|
93
95
|
|
94
96
|
it "should enqueue with delay" do
|
95
|
-
job.
|
97
|
+
expect(job).to receive(:delay=).with(80)
|
96
98
|
job.enqueue(:delay => 80)
|
97
99
|
end
|
98
100
|
end
|
@@ -114,17 +116,17 @@ describe Quebert::Job do
|
|
114
116
|
user = User.new(:first_name => "Steel")
|
115
117
|
user.async(:priority => 1, :delay => 2, :ttr => 300).email!("somebody", nil, nil)
|
116
118
|
job = @q.reserve
|
117
|
-
job.beanstalk_job.pri.
|
118
|
-
job.beanstalk_job.delay.
|
119
|
-
job.beanstalk_job.ttr.
|
119
|
+
expect(job.beanstalk_job.pri).to eql(1)
|
120
|
+
expect(job.beanstalk_job.delay).to eql(2)
|
121
|
+
expect(job.beanstalk_job.ttr).to eql(300 + Quebert::Backend::Beanstalk::TTR_BUFFER)
|
120
122
|
end
|
121
123
|
|
122
124
|
it "should enqueue and honor beanstalk options" do
|
123
125
|
User.async(:priority => 1, :delay => 2, :ttr => 300).emailizer("somebody", nil, nil)
|
124
126
|
job = @q.reserve
|
125
|
-
job.beanstalk_job.pri.
|
126
|
-
job.beanstalk_job.delay.
|
127
|
-
job.beanstalk_job.ttr.
|
127
|
+
expect(job.beanstalk_job.pri).to eql(1)
|
128
|
+
expect(job.beanstalk_job.delay).to eql(2)
|
129
|
+
expect(job.beanstalk_job.ttr).to eql(300 + Quebert::Backend::Beanstalk::TTR_BUFFER)
|
128
130
|
end
|
129
131
|
end
|
130
132
|
|
@@ -133,17 +135,76 @@ describe Quebert::Job do
|
|
133
135
|
user = User.new(:first_name => "Steel")
|
134
136
|
user.async_send(:email!, "somebody", nil, nil, :beanstalk => {:priority => 1, :delay => 2, :ttr => 300})
|
135
137
|
job = @q.reserve
|
136
|
-
job.beanstalk_job.pri.
|
137
|
-
job.beanstalk_job.delay.
|
138
|
-
job.beanstalk_job.ttr.
|
138
|
+
expect(job.beanstalk_job.pri).to eql(1)
|
139
|
+
expect(job.beanstalk_job.delay).to eql(2)
|
140
|
+
expect(job.beanstalk_job.ttr).to eql(300 + Quebert::Backend::Beanstalk::TTR_BUFFER)
|
139
141
|
end
|
140
142
|
|
141
143
|
it "should enqueue and honor beanstalk options" do
|
142
144
|
User.async_send(:emailizer, "somebody", nil, nil, :beanstalk => {:priority => 1, :delay => 2, :ttr => 300})
|
143
145
|
job = @q.reserve
|
144
|
-
job.beanstalk_job.pri.
|
145
|
-
job.beanstalk_job.delay.
|
146
|
-
job.beanstalk_job.ttr.
|
146
|
+
expect(job.beanstalk_job.pri).to eql(1)
|
147
|
+
expect(job.beanstalk_job.delay).to eql(2)
|
148
|
+
expect(job.beanstalk_job.ttr).to eql(300 + Quebert::Backend::Beanstalk::TTR_BUFFER)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "Beanstalk event hooks" do
|
153
|
+
it "calls the bury event hook" do
|
154
|
+
class BuryJob
|
155
|
+
attr_accessor :before_bury_ran, :after_bury_ran
|
156
|
+
def around_bury
|
157
|
+
self.before_bury_ran = true
|
158
|
+
yield
|
159
|
+
self.after_bury_ran = true
|
160
|
+
end
|
161
|
+
end
|
162
|
+
BuryJob.backend = @q
|
163
|
+
job = BuryJob.new.tap(&:enqueue)
|
164
|
+
controller = @q.reserve
|
165
|
+
expect(controller.job.before_bury_ran).to be_falsey
|
166
|
+
expect(controller.job.after_bury_ran).to be_falsey
|
167
|
+
controller.perform
|
168
|
+
expect(controller.job.before_bury_ran).to be_truthy
|
169
|
+
expect(controller.job.after_bury_ran).to be_truthy
|
170
|
+
end
|
171
|
+
|
172
|
+
it "calls the delete event hook" do
|
173
|
+
class DeleteJob
|
174
|
+
attr_accessor :before_delete_ran, :after_delete_ran
|
175
|
+
def around_delete
|
176
|
+
self.before_delete_ran = true
|
177
|
+
yield
|
178
|
+
self.after_delete_ran = true
|
179
|
+
end
|
180
|
+
end
|
181
|
+
DeleteJob.backend = @q
|
182
|
+
job = DeleteJob.new.tap(&:enqueue)
|
183
|
+
controller = @q.reserve
|
184
|
+
expect(controller.job.before_delete_ran).to be_falsey
|
185
|
+
expect(controller.job.after_delete_ran).to be_falsey
|
186
|
+
controller.perform
|
187
|
+
expect(controller.job.before_delete_ran).to be_truthy
|
188
|
+
expect(controller.job.after_delete_ran).to be_truthy
|
189
|
+
end
|
190
|
+
|
191
|
+
it "calls the release event hook" do
|
192
|
+
class ReleaseJob
|
193
|
+
attr_accessor :before_release_ran, :after_release_ran
|
194
|
+
def around_release
|
195
|
+
self.before_release_ran = true
|
196
|
+
yield
|
197
|
+
self.after_release_ran = true
|
198
|
+
end
|
199
|
+
end
|
200
|
+
ReleaseJob.backend = @q
|
201
|
+
job = ReleaseJob.new.tap(&:enqueue)
|
202
|
+
controller = @q.reserve
|
203
|
+
expect(controller.job.before_release_ran).to be_falsey
|
204
|
+
expect(controller.job.after_release_ran).to be_falsey
|
205
|
+
controller.perform
|
206
|
+
expect(controller.job.before_release_ran).to be_truthy
|
207
|
+
expect(controller.job.after_release_ran).to be_truthy
|
147
208
|
end
|
148
209
|
end
|
149
210
|
end
|
@@ -151,9 +212,9 @@ describe Quebert::Job do
|
|
151
212
|
|
152
213
|
context "Timeout" do
|
153
214
|
it "should respect TTR option" do
|
154
|
-
|
215
|
+
expect {
|
155
216
|
TimeoutJob.new.perform!
|
156
|
-
}.
|
217
|
+
}.to raise_exception(Quebert::Job::Timeout)
|
157
218
|
end
|
158
219
|
end
|
159
220
|
|
@@ -181,10 +242,10 @@ describe Quebert::Job do
|
|
181
242
|
|
182
243
|
jobs.each(&:perform!)
|
183
244
|
|
184
|
-
before_jobs.
|
185
|
-
after_jobs.
|
245
|
+
expect(before_jobs).to eql jobs
|
246
|
+
expect(after_jobs).to eql jobs
|
186
247
|
# around_job hooks are called twice per job (before & after its performed)
|
187
|
-
around_jobs.
|
248
|
+
expect(around_jobs).to eql jobs.zip(jobs).flatten
|
188
249
|
end
|
189
250
|
end
|
190
251
|
end
|
data/spec/quebert_spec.rb
CHANGED
data/spec/serializer_spec.rb
CHANGED
@@ -6,31 +6,31 @@ describe Serializer::ActiveRecord do
|
|
6
6
|
|
7
7
|
it "should serialize" do
|
8
8
|
h = Serializer::ActiveRecord.serialize(user)
|
9
|
-
h['model'].
|
10
|
-
h['attributes']['first_name'].
|
11
|
-
h['attributes']['id'].
|
9
|
+
expect(h['model']).to eql('User')
|
10
|
+
expect(h['attributes']['first_name']).to eql('Tom')
|
11
|
+
expect(h['attributes']['id']).to eql(user.id)
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
it "should deserialize" do
|
15
15
|
u = Serializer::ActiveRecord.deserialize(Serializer::ActiveRecord.serialize(user))
|
16
|
-
u.first_name.
|
17
|
-
u.id.
|
16
|
+
expect(u.first_name).to eql('Tom')
|
17
|
+
expect(u.id).to eql(user.id)
|
18
18
|
end
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
context "unpersisted" do
|
22
22
|
let(:user) { User.new(:first_name => 'brad') }
|
23
23
|
|
24
24
|
it "should serialize" do
|
25
25
|
h = Serializer::ActiveRecord.serialize(user)
|
26
|
-
h['model'].
|
27
|
-
h['attributes']['first_name'].
|
28
|
-
h['attributes']['id'].
|
26
|
+
expect(h['model']).to eql('User')
|
27
|
+
expect(h['attributes']['first_name']).to eql('brad')
|
28
|
+
expect(h['attributes']['id']).to be_nil
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
it "should deserialize" do
|
32
32
|
u = Serializer::ActiveRecord.deserialize(Serializer::ActiveRecord.serialize(user))
|
33
|
-
u.first_name.
|
33
|
+
expect(u.first_name).to eql('brad')
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -50,44 +50,44 @@ describe Serializer::Job do
|
|
50
50
|
|
51
51
|
describe "#serialize" do
|
52
52
|
it "shold have job" do
|
53
|
-
serialized['job'].
|
53
|
+
expect(serialized['job']).to eql('Quebert::Job')
|
54
54
|
end
|
55
55
|
|
56
56
|
it "should have args" do
|
57
|
-
serialized['args'][0]['payload'].
|
58
|
-
serialized['args'][1]['payload'].
|
59
|
-
serialized['args'][1]['serializer'].
|
57
|
+
expect(serialized['args'][0]['payload']).to eql(100)
|
58
|
+
expect(serialized['args'][1]['payload']).to eql(Serializer::ActiveRecord.serialize(args[1]))
|
59
|
+
expect(serialized['args'][1]['serializer']).to eql('Quebert::Serializer::ActiveRecord')
|
60
60
|
end
|
61
61
|
|
62
62
|
it "should have priority" do
|
63
|
-
serialized['priority'].
|
63
|
+
expect(serialized['priority']).to eql(1)
|
64
64
|
end
|
65
65
|
|
66
66
|
it "should have delay" do
|
67
|
-
serialized['delay'].
|
67
|
+
expect(serialized['delay']).to eql(2)
|
68
68
|
end
|
69
69
|
|
70
70
|
it "should have ttr" do
|
71
|
-
serialized['ttr'].
|
71
|
+
expect(serialized['ttr']).to eql(300)
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
75
|
describe "#deserialize" do
|
76
76
|
it "should have args" do
|
77
|
-
deserialized.args[0].
|
78
|
-
deserialized.args[1].first_name.
|
77
|
+
expect(deserialized.args[0]).to eql(100)
|
78
|
+
expect(deserialized.args[1].first_name).to eql('Brad')
|
79
79
|
end
|
80
80
|
|
81
81
|
it "should have delay" do
|
82
|
-
deserialized.delay.
|
82
|
+
expect(deserialized.delay).to eql(2)
|
83
83
|
end
|
84
84
|
|
85
85
|
it "should have priority" do
|
86
|
-
deserialized.priority.
|
86
|
+
expect(deserialized.priority).to eql(1)
|
87
87
|
end
|
88
88
|
|
89
89
|
it "should have ttr" do
|
90
|
-
deserialized.ttr.
|
90
|
+
expect(deserialized.ttr).to eql(300)
|
91
91
|
end
|
92
92
|
end
|
93
93
|
end
|
data/spec/support/jobs.rb
CHANGED
data/spec/support_spec.rb
CHANGED
@@ -3,21 +3,21 @@ require 'spec_helper'
|
|
3
3
|
describe Support::ClassRegistry do
|
4
4
|
Super = Class.new
|
5
5
|
Sub = Class.new(Super)
|
6
|
-
|
6
|
+
|
7
7
|
before(:all) do
|
8
8
|
@registry = Support::ClassRegistry.new
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
it "should store class symbols" do
|
12
12
|
@registry[:'Super'] = Super
|
13
|
-
@registry[:'Super'].
|
13
|
+
expect(@registry[:'Super']).to eql(Super)
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
it "should retrieve class keys" do
|
17
|
-
@registry[Super].
|
17
|
+
expect(@registry[Super]).to eql(Super)
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
it "should retrieve subclass keys" do
|
21
|
-
@registry[Sub].
|
21
|
+
expect(@registry[Sub]).to eql(Super)
|
22
22
|
end
|
23
|
-
end
|
23
|
+
end
|
data/spec/worker_spec.rb
CHANGED
@@ -8,26 +8,26 @@ describe Worker do
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
it "
|
11
|
+
it "starts" do
|
12
12
|
@w.start
|
13
13
|
end
|
14
14
|
|
15
15
|
context "pluggable exception handler" do
|
16
|
-
it "
|
16
|
+
it "raises exception if nothing is provided" do
|
17
17
|
@q.put Exceptional.new
|
18
|
-
|
18
|
+
expect { @w.start }.to raise_exception(RuntimeError, "Exceptional")
|
19
19
|
end
|
20
20
|
|
21
|
-
it "
|
21
|
+
it "defaults to Quebert.config.worker.exception_handler handler" do
|
22
22
|
@q.put Exceptional.new
|
23
|
-
Quebert.config.worker.exception_handler = Proc.new{|e, opts| e.
|
24
|
-
|
23
|
+
Quebert.config.worker.exception_handler = Proc.new{|e, opts| expect(e).to be_a(StandardError) }
|
24
|
+
expect { @w.start }.to_not raise_exception
|
25
25
|
end
|
26
26
|
|
27
|
-
it "
|
27
|
+
it "intercepts exceptions" do
|
28
28
|
@q.put Exceptional.new
|
29
|
-
@w.exception_handler = Proc.new{|e, opts| e.
|
30
|
-
|
29
|
+
@w.exception_handler = Proc.new{|e, opts| expect(e).to be_a(StandardError) }
|
30
|
+
expect { @w.start }.to_not raise_exception
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quebert
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brad Gessler
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2017-04-17 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: json
|
@@ -67,6 +67,7 @@ files:
|
|
67
67
|
- ".rspec"
|
68
68
|
- ".ruby-version"
|
69
69
|
- ".travis.yml"
|
70
|
+
- CHANGELOG.md
|
70
71
|
- Gemfile
|
71
72
|
- Guardfile
|
72
73
|
- LICENSE.md
|
@@ -131,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
132
|
version: '0'
|
132
133
|
requirements: []
|
133
134
|
rubyforge_project: quebert
|
134
|
-
rubygems_version: 2.
|
135
|
+
rubygems_version: 2.6.11
|
135
136
|
signing_key:
|
136
137
|
specification_version: 4
|
137
138
|
summary: A worker queue framework built around beanstalkd
|