strobemonkey-job_queue 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +62 -0
- data/VERSION.yml +4 -0
- data/lib/job_queue.rb +8 -0
- data/lib/job_queue/adapters/amqp_adapter.rb +28 -0
- data/lib/job_queue/adapters/beanstalk_adapter.rb +24 -0
- data/lib/job_queue/adapters/starling_adapter.rb +30 -0
- data/lib/job_queue/adapters/test_adapter.rb +20 -0
- data/lib/job_queue/adapters/verbose_adapter.rb +19 -0
- data/lib/job_queue/job_queue.rb +52 -0
- data/spec/amqp_adapter_spec.rb +8 -0
- data/spec/beanstalk_adapter_spec.rb +48 -0
- data/spec/job_queue_spec.rb +5 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/starling_adapter_spec.rb +56 -0
- data/spec/test_adapter_spec.rb +39 -0
- data/spec/verbose_adapter_spec.rb +15 -0
- metadata +71 -0
data/README.markdown
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
JobQueue
|
2
|
+
========
|
3
|
+
|
4
|
+
`job_queue` allows you to use lots of message queues with exactly the same interface so you don't need to worry about which queue to pick :)
|
5
|
+
|
6
|
+
This should get you started:
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'job_queue'
|
10
|
+
|
11
|
+
Before you can do anything you must specify an adapter to use
|
12
|
+
|
13
|
+
JobQueue.adapter = JobQueue::BeanstalkAdapter.new
|
14
|
+
|
15
|
+
Jobs can then be simply added to the queue
|
16
|
+
|
17
|
+
JobQueue.put("flubble bubble")
|
18
|
+
|
19
|
+
In your workers you'll want to subscribe to a queue
|
20
|
+
|
21
|
+
JobQueue.subscribe do |job|
|
22
|
+
puts job
|
23
|
+
end
|
24
|
+
|
25
|
+
This subscribe block takes care of waiting for the next job to arrive and the block is passed exactly what you passed in. If you want to exit the loop just throw :stop.
|
26
|
+
|
27
|
+
JobQueue.subscribe do |job|
|
28
|
+
# Wait - I changed my mind!
|
29
|
+
throw :stop
|
30
|
+
end
|
31
|
+
|
32
|
+
What should you put on the queue
|
33
|
+
--------------------------------
|
34
|
+
|
35
|
+
You might love Ruby right now, but why lock yourself in? Often the kinds of things you use queues for are the kind of things you'll want to optimize. This is a good place to start:
|
36
|
+
|
37
|
+
JSON.generate({:some => "hash"})
|
38
|
+
JSON.parse(job)
|
39
|
+
|
40
|
+
Can you show me a nice processing daemon?
|
41
|
+
-----------------------------------------
|
42
|
+
|
43
|
+
Yes. Just a minute...
|
44
|
+
|
45
|
+
Adapters
|
46
|
+
========
|
47
|
+
|
48
|
+
Take your pick! Right now we have:
|
49
|
+
|
50
|
+
Beanstalk
|
51
|
+
---------
|
52
|
+
<http://xph.us/software/beanstalkd/>
|
53
|
+
|
54
|
+
AMQP
|
55
|
+
----
|
56
|
+
<http://github.com/tmm1/amqp/>
|
57
|
+
|
58
|
+
You need to run all your code within an eventmachine loop to use AMQP.
|
59
|
+
|
60
|
+
Starling
|
61
|
+
--------
|
62
|
+
<http://github.com/starling/starling/>
|
data/VERSION.yml
ADDED
data/lib/job_queue.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'job_queue/job_queue'
|
3
|
+
|
4
|
+
JobQueue.autoload 'AMQPAdapter', 'job_queue/adapters/amqp_adapter'
|
5
|
+
JobQueue.autoload 'BeanstalkAdapter', 'job_queue/adapters/beanstalk_adapter'
|
6
|
+
JobQueue.autoload 'TestAdapter', 'job_queue/adapters/test_adapter'
|
7
|
+
JobQueue.autoload 'VerboseAdapter', 'job_queue/adapters/verbose_adapter'
|
8
|
+
JobQueue.autoload 'StarlingAdapter', 'job_queue/adapters/starling_adapter'
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'mq'
|
2
|
+
|
3
|
+
class JobQueue::AMQPAdapter
|
4
|
+
def initialize
|
5
|
+
amq = MQ.new
|
6
|
+
@exchange = amq.direct('photo', :durable => true)
|
7
|
+
@queue = amq.queue('photo_worker', :durable => true)
|
8
|
+
@queue.bind(@exchange)
|
9
|
+
end
|
10
|
+
|
11
|
+
def put(string)
|
12
|
+
@queue.publish(string, :persistent => true)
|
13
|
+
end
|
14
|
+
|
15
|
+
def subscribe(error_report, &block)
|
16
|
+
EM.add_periodic_timer(0) do
|
17
|
+
begin
|
18
|
+
@queue.pop do |header, body|
|
19
|
+
next unless body
|
20
|
+
JobQueue.logger.info "AMQP received #{body}"
|
21
|
+
yield body
|
22
|
+
end
|
23
|
+
rescue => e
|
24
|
+
error_report.call(job.body, e)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'beanstalk-client'
|
2
|
+
|
3
|
+
class JobQueue::BeanstalkAdapter
|
4
|
+
def initialize
|
5
|
+
@beanstalk = Beanstalk::Pool.new(['localhost:11300'])
|
6
|
+
end
|
7
|
+
|
8
|
+
def put(string)
|
9
|
+
@beanstalk.put(string)
|
10
|
+
end
|
11
|
+
|
12
|
+
def subscribe(error_report, &block)
|
13
|
+
loop do
|
14
|
+
begin
|
15
|
+
job = @beanstalk.reserve
|
16
|
+
JobQueue.logger.info "Beanstalk received #{job.body}"
|
17
|
+
yield job.body
|
18
|
+
job.delete
|
19
|
+
rescue => e
|
20
|
+
error_report.call(job.body, e)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'memcache'
|
2
|
+
require 'starling'
|
3
|
+
|
4
|
+
class JobQueue::StarlingAdapter
|
5
|
+
def initialize(queue_name, server, port)
|
6
|
+
@queue_name = queue_name
|
7
|
+
@server = server
|
8
|
+
@port = port
|
9
|
+
@starling = Starling.new( @server + ':' + @port )
|
10
|
+
end
|
11
|
+
|
12
|
+
def put(string)
|
13
|
+
@starling.set(@queue_name, string)
|
14
|
+
end
|
15
|
+
|
16
|
+
def subscribe(error_report, &block)
|
17
|
+
|
18
|
+
loop do
|
19
|
+
begin
|
20
|
+
job = @starling.fetch(@queue_name)
|
21
|
+
JobQueue.logger.info "Starling received #{job}"
|
22
|
+
yield job
|
23
|
+
sleep 0.25
|
24
|
+
rescue => e
|
25
|
+
error_report.call(job, e)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class JobQueue::TestAdapter
|
2
|
+
def initialize
|
3
|
+
@queue = []
|
4
|
+
end
|
5
|
+
|
6
|
+
def put(string)
|
7
|
+
@queue << string
|
8
|
+
end
|
9
|
+
|
10
|
+
def subscribe(error_report, &block)
|
11
|
+
loop do
|
12
|
+
begin
|
13
|
+
sleep 0.1 if @queue.empty?
|
14
|
+
yield @queue.shift
|
15
|
+
rescue
|
16
|
+
error_report.call(job.body, e)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# This isn't a queue at all, it just writes to standard output.
|
2
|
+
#
|
3
|
+
# It might be useful for testing.
|
4
|
+
#
|
5
|
+
class JobQueue::VerboseAdapter
|
6
|
+
def initialize
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
def put(string)
|
11
|
+
JobQueue.logger.debug "===== NEW JOB ADDED TO QUEUE ===="
|
12
|
+
JobQueue.logger.debug string
|
13
|
+
JobQueue.logger.debug "===== END OF MESSAGE ============"
|
14
|
+
end
|
15
|
+
|
16
|
+
def subscribe(error_report, &block)
|
17
|
+
raise "Not implemented. Use a better adapter!!"
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# JobQueue abstracts the task of adding work to a queue.
|
2
|
+
#
|
3
|
+
# Beanstalk is fantastic, but maybe not "enterprise grade".
|
4
|
+
#
|
5
|
+
# AMQP is fantastic, but it's bloody complex and has to run inside an
|
6
|
+
# eventmachine loop.
|
7
|
+
#
|
8
|
+
# Take your pick!
|
9
|
+
#
|
10
|
+
# Before use, an adapter must be chosen:
|
11
|
+
#
|
12
|
+
# JobQueue.adapter = JobQueue::BeanstalkAdapter.new
|
13
|
+
#
|
14
|
+
# Jobs can then be simply added to the queue with
|
15
|
+
#
|
16
|
+
# JobQueue.put("flubble bubble")
|
17
|
+
#
|
18
|
+
class JobQueue
|
19
|
+
class << self
|
20
|
+
attr_accessor :adapter
|
21
|
+
attr_accessor :logger
|
22
|
+
|
23
|
+
def logger
|
24
|
+
@logger ||= begin
|
25
|
+
logger = Logger.new(STDOUT)
|
26
|
+
logger.level = Logger::WARN
|
27
|
+
logger.debug("Created logger")
|
28
|
+
logger
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.put(string)
|
34
|
+
adapter.put(string)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.subscribe(error_report = nil, &block)
|
38
|
+
catch :stop do
|
39
|
+
error_report ||= Proc.new do |job, e|
|
40
|
+
JobQueue.logger.error \
|
41
|
+
"Job failed\n" \
|
42
|
+
"==========\n" \
|
43
|
+
"Job content: #{job.inspect}\n" \
|
44
|
+
"Exception: #{e.message}\n" \
|
45
|
+
"#{e.backtrace.join("\n")}\n" \
|
46
|
+
"\n"
|
47
|
+
end
|
48
|
+
|
49
|
+
adapter.subscribe(error_report, &block)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe JobQueue::BeanstalkAdapter do
|
4
|
+
before :all do
|
5
|
+
JobQueue.adapter = JobQueue::BeanstalkAdapter.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should write onto queue and fetch stuff back off" do
|
9
|
+
JobQueue.put("hello")
|
10
|
+
|
11
|
+
JobQueue.subscribe do |job|
|
12
|
+
@job = job
|
13
|
+
throw :stop
|
14
|
+
end
|
15
|
+
|
16
|
+
@job.should == "hello"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should output message if error raised in job" do
|
20
|
+
JobQueue.put("hello")
|
21
|
+
|
22
|
+
JobQueue.logger.should_receive(:error).with(/Job failed\w*/)
|
23
|
+
|
24
|
+
index = 0
|
25
|
+
JobQueue.subscribe do |job|
|
26
|
+
index +=1
|
27
|
+
raise 'foo' if index == 1
|
28
|
+
throw :stop
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should use error_report block if supplied" do
|
33
|
+
JobQueue.put("hello")
|
34
|
+
|
35
|
+
error_report = Proc.new do |job, e|
|
36
|
+
JobQueue.logger.error "Yikes that broke matey!"
|
37
|
+
end
|
38
|
+
|
39
|
+
JobQueue.logger.should_receive(:error).with("Yikes that broke matey!")
|
40
|
+
|
41
|
+
index = 0
|
42
|
+
JobQueue.subscribe(error_report) do |job|
|
43
|
+
index +=1
|
44
|
+
raise 'foo' if index == 1
|
45
|
+
throw :stop
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe JobQueue::StarlingAdapter do
|
4
|
+
|
5
|
+
def fairly_unique_message
|
6
|
+
rand(0.1).to_s.split('.')[1]
|
7
|
+
end
|
8
|
+
|
9
|
+
before :all do
|
10
|
+
JobQueue.adapter = JobQueue::StarlingAdapter.new( 'test_queue', 'localhost', '22122' )
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should write onto queue and fetch stuff back off" do
|
14
|
+
message = fairly_unique_message
|
15
|
+
JobQueue.put(message)
|
16
|
+
|
17
|
+
JobQueue.subscribe do |job|
|
18
|
+
@job = job
|
19
|
+
throw :stop
|
20
|
+
end
|
21
|
+
|
22
|
+
@job.should == message
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should output message if error raised in job" do
|
26
|
+
message = fairly_unique_message
|
27
|
+
JobQueue.put(message)
|
28
|
+
|
29
|
+
JobQueue.logger.should_receive(:error).with(/Job failed\w*/)
|
30
|
+
|
31
|
+
index = 0
|
32
|
+
JobQueue.subscribe do |job|
|
33
|
+
index +=1
|
34
|
+
raise 'foo' if index == 1
|
35
|
+
throw :stop
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should use error_report block if supplied" do
|
40
|
+
message = fairly_unique_message
|
41
|
+
JobQueue.put(message)
|
42
|
+
|
43
|
+
error_report = Proc.new do |job, e|
|
44
|
+
JobQueue.logger.error "Yikes that broke matey!"
|
45
|
+
end
|
46
|
+
|
47
|
+
JobQueue.logger.should_receive(:error).with("Yikes that broke matey!")
|
48
|
+
|
49
|
+
index = 0
|
50
|
+
JobQueue.subscribe(error_report) do |job|
|
51
|
+
index +=1
|
52
|
+
raise 'foo' if index == 1
|
53
|
+
throw :stop
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe JobQueue::TestAdapter do
|
4
|
+
before :all do
|
5
|
+
JobQueue.adapter = JobQueue::TestAdapter.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should write onto queue and fetch stuff back off" do
|
9
|
+
JobQueue.put("hello")
|
10
|
+
|
11
|
+
JobQueue.subscribe do |job|
|
12
|
+
@job = job
|
13
|
+
throw :stop
|
14
|
+
end
|
15
|
+
|
16
|
+
@job.should == "hello"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should pull items off in the order the were added" do
|
20
|
+
JobQueue.put("foo")
|
21
|
+
JobQueue.put("bar")
|
22
|
+
|
23
|
+
retrieved_jobs = []
|
24
|
+
|
25
|
+
begin
|
26
|
+
Timeout::timeout(0.5) do
|
27
|
+
JobQueue.subscribe do |job|
|
28
|
+
retrieved_jobs << job
|
29
|
+
end
|
30
|
+
end
|
31
|
+
rescue Timeout::Error
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
retrieved_jobs[0].should == "foo"
|
36
|
+
retrieved_jobs[1].should == "bar"
|
37
|
+
retrieved_jobs[2].should == nil
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe JobQueue::VerboseAdapter do
|
4
|
+
before :all do
|
5
|
+
JobQueue.adapter = JobQueue::VerboseAdapter.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should write onto queue and output a very verbose message to stdout" do
|
9
|
+
JobQueue.logger.should_receive(:debug).with("===== NEW JOB ADDED TO QUEUE ====")
|
10
|
+
JobQueue.logger.should_receive(:debug).with("hello")
|
11
|
+
JobQueue.logger.should_receive(:debug).with("===== END OF MESSAGE ============")
|
12
|
+
|
13
|
+
JobQueue.put("hello")
|
14
|
+
end
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: strobemonkey-job_queue
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martyn Loughran
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-12 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: JobQueue means you don't have to worry about your queue any more!
|
17
|
+
email: me@mloughran.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README.markdown
|
26
|
+
- VERSION.yml
|
27
|
+
- lib/job_queue
|
28
|
+
- lib/job_queue/adapters
|
29
|
+
- lib/job_queue/adapters/amqp_adapter.rb
|
30
|
+
- lib/job_queue/adapters/beanstalk_adapter.rb
|
31
|
+
- lib/job_queue/adapters/test_adapter.rb
|
32
|
+
- lib/job_queue/adapters/verbose_adapter.rb
|
33
|
+
- lib/job_queue/adapters/starling_adapter.rb
|
34
|
+
- lib/job_queue/job_queue.rb
|
35
|
+
- lib/job_queue.rb
|
36
|
+
- spec/amqp_adapter_spec.rb
|
37
|
+
- spec/beanstalk_adapter_spec.rb
|
38
|
+
- spec/job_queue_spec.rb
|
39
|
+
- spec/spec_helper.rb
|
40
|
+
- spec/test_adapter_spec.rb
|
41
|
+
- spec/verbose_adapter_spec.rb
|
42
|
+
- spec/starling_adapter_spec.rb
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://github.com/mloughran/job_queue
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options:
|
47
|
+
- --inline-source
|
48
|
+
- --charset=UTF-8
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.2.0
|
67
|
+
signing_key:
|
68
|
+
specification_version: 2
|
69
|
+
summary: JobQueue means you don't have to worry about your queue any more!
|
70
|
+
test_files: []
|
71
|
+
|