rabbit-wq 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -22,7 +22,45 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- TODO: Write usage instructions here
25
+ ### Queueing Work
26
26
 
27
+ class SomeWorker < Struct.new( :some_variable )
28
+ def call
29
+ # do some work
30
+ end
31
+ end
32
+
33
+ worker = SomeWorker.new( 1 )
34
+ RabbitWQ::Work.enqueue( worker )
27
35
 
28
- ## Contributing
36
+ ### Queueing Work in the Future
37
+
38
+ RabbitWQ::Work.enqueue( worker, delay: 30000 )
39
+
40
+ ### Queueing Work with a Retry
41
+
42
+ RabbitWQ::Work.enqueue( worker, retry: 1 )
43
+
44
+ ### Queueing Work with a Retry and a Retry Delay
45
+
46
+ RabbitWQ::Work.enqueue( worker, retry: 1, retry_delay: 30000 )
47
+
48
+ ### Queueing Work with a Retry and Auto-Scaling Retry Dealy
49
+
50
+ RabbitWQ::Work.enqueue( worker, retry: 1, retry_delay: 'auto-scale' )
51
+
52
+ Auto-scale will set up retries at the following intervals: 1 min, 5 mins, 15 mins, 30 mins,
53
+ 1 hr, 6 hrs, 12 hrs, 24 hrs. and 48 hrs.
54
+
55
+ ### Using the Worker Module
56
+
57
+ class SomeWorker < Struct.new( :some_variable )
58
+ include RabbitWQ::Worker
59
+
60
+ def call
61
+ # do some work
62
+ end
63
+ end
64
+
65
+ worker = SomeWorker.new( 1 )
66
+ worker.work # same as RabbitWQ::Work.enqueue( worker )
@@ -0,0 +1,47 @@
1
+ module RabbitWQ
2
+ class Command
3
+
4
+ attr_reader :options
5
+
6
+ def initialize( args )
7
+ @options = {
8
+ :quiet => true,
9
+ :pid_dir => "#{Rails.root}/tmp/pids"
10
+ }
11
+
12
+ opts = OptionParser.new do |opts|
13
+ opts.banner = "Usage: #{File.basename($0)} [options] start|stop|restart"
14
+
15
+ opts.on('-h', '--help', 'Show this message') do
16
+ puts opts
17
+ exit 1
18
+ end
19
+ opts.on('--pid-dir=DIR', 'Specifies an alternate directory in which to store the process ids.') do |dir|
20
+ @options[:pid_dir] = dir
21
+ end
22
+ opts.on('-i', '--interactive', 'Start the process in interactive mode.') do |n|
23
+ @options[:interactive] = true
24
+ end
25
+ end
26
+
27
+ @args = opts.parse!( args )
28
+ end
29
+
30
+ def start
31
+ if options[:interactive]
32
+ start_interactive( options )
33
+ else
34
+ start_daemonized( options )
35
+ end
36
+ end
37
+
38
+ def start_interactive( options )
39
+ server = RabbitWQ::Server.new( options.merge( log: nil ))
40
+ server.start
41
+ end
42
+
43
+ def start_daemonized( options )
44
+ end
45
+
46
+ end
47
+ end
@@ -1,3 +1,5 @@
1
+ require 'yell'
2
+
1
3
  module RabbitWQ
2
4
  module Logging
3
5
 
@@ -1,10 +1,12 @@
1
1
  require 'celluloid/autostart'
2
+ require 'yaml'
2
3
 
3
4
  module RabbitWQ
4
5
  class MessageHandler
5
6
 
6
7
  include Celluloid
7
8
  include Logging
9
+ include Queues
8
10
 
9
11
  REQUEUE = true
10
12
 
@@ -12,18 +14,57 @@ module RabbitWQ
12
14
  channel = options[:channel]
13
15
  delivery_info = options[:delivery_info]
14
16
  metadata = options[:metadata]
17
+ headers = metadata[:headers]
15
18
  payload = options[:payload]
16
19
 
17
- info "PAYLOAD ARRIVED #{payload}"
20
+ debug "PAYLOAD ARRIVED #{payload}"
21
+
22
+ $stdout.puts headers.inspect
23
+ worker = YAML::load( payload )
24
+ worker.call
18
25
  channel.ack delivery_info.delivery_tag
26
+ rescue => e
27
+ debug e.message
28
+ handle_error( e, channel, delivery_info, payload, metadata )
19
29
  end
20
30
 
21
31
  protected
22
32
 
33
+ def handle_error( e, channel, delivery_info, payload, metadata )
34
+ headers = metadata[:headers]
35
+
36
+ if headers['retry']
37
+ retry_delay = headers.fetch( 'retry_delay', 30000 )
38
+ attempt = headers.fetch( 'attempt', 1 ).to_i
39
+
40
+ if retry_delay == 'auto-scale'
41
+ retry_delay = retry_delays( attempt )
42
+ end
43
+
44
+ Work.enqueue_payload( payload, headers.merge( delay: retry_delay, attempt: attempt + 1 ))
45
+ channel.nack delivery_info.delivery_tag
46
+
47
+ return
48
+ end
49
+ end
50
+
23
51
  def requeue( channel, delivery_info, e=nil )
24
52
  info ANSI.yellow { 'REQUEUE ' + e.message }
25
53
  channel.reject delivery_info.delivery_tag, REQUEUE
26
54
  end
27
55
 
56
+ def retry_delays( retry_num )
57
+ {
58
+ 1 => 1,
59
+ 2 => 5,
60
+ 3 => 15,
61
+ 4 => 30,
62
+ 5 => 60,
63
+ 6 => 360, # 6 hrs
64
+ 7 => 720, # 12 hrs
65
+ 8 => 1440, # 24 hrs
66
+ 9 => 2880, # 48 hrs
67
+ }[retry_num] * 60000
68
+ end
28
69
  end
29
70
  end
@@ -0,0 +1,27 @@
1
+ module RabbitWQ
2
+ module Queues
3
+
4
+ protected
5
+
6
+ def mq
7
+ @mq ||= ::Bunny.new.tap do |bunny|
8
+ bunny.start
9
+ end
10
+ end
11
+
12
+ def channel
13
+ @channel ||= mq.create_channel
14
+ end
15
+
16
+ #def work_exchange
17
+ #@work_exchange ||= channel.direct( WORK_EXCHANGE, durable: true )
18
+ #end
19
+
20
+ #def work_queue
21
+ #@work_queue ||= channel.queue( QUEUE,
22
+ #durable: true ).
23
+ #bind( work_exchange )
24
+ #end
25
+
26
+ end
27
+ end
@@ -3,11 +3,12 @@ require 'bunny'
3
3
  module RabbitWQ
4
4
  class Server
5
5
 
6
- include ServerLogging
7
6
  include Logging
7
+ include Queues
8
+ include ServerLogging
8
9
 
9
- attr_reader :message_consumer,
10
- :options,
10
+ attr_reader :options,
11
+ :work_consumer,
11
12
  :work_exchange
12
13
 
13
14
  def initialize( options )
@@ -29,28 +30,18 @@ module RabbitWQ
29
30
 
30
31
  def finalize
31
32
  info "SHUTTING DOWN"
32
- message_consumer.cancel
33
+ work_consumer.cancel
33
34
  mq.close
34
35
  end
35
36
 
36
- def mq
37
- @mq ||= ::Bunny.new.tap do |bunny|
38
- bunny.start
39
- end
40
- end
41
-
42
- def channel
43
- @channel ||= mq.create_channel
44
- end
45
-
46
37
  def work_exchange
47
38
  @work_exchange ||= channel.direct( WORK_EXCHANGE, durable: true )
48
39
  end
49
40
 
50
- def queue
51
- @queue ||= channel.queue( QUEUE,
52
- durable: true ).
53
- bind( work_exchange )
41
+ def work_queue
42
+ @work_queue ||= channel.queue( QUEUE,
43
+ durable: true ).
44
+ bind( work_exchange )
54
45
  end
55
46
 
56
47
  def pool
@@ -58,8 +49,8 @@ module RabbitWQ
58
49
  end
59
50
 
60
51
  def run
61
- @message_consumer = queue.subscribe( manual_ack: true ) do |delivery_info, metadata, payload|
62
- debug "LISTENER RECEIVED #{payload}"
52
+ @work_consumer = work_queue.subscribe( manual_ack: true ) do |delivery_info, metadata, payload|
53
+ info "LISTENER RECEIVED #{payload}"
63
54
 
64
55
  pool.async.call( payload: payload,
65
56
  delivery_info: delivery_info,
@@ -1,3 +1,3 @@
1
1
  module RabbitWQ
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -4,27 +4,37 @@ module RabbitWQ
4
4
  module Work
5
5
 
6
6
  def self.enqueue( worker, options={} )
7
- options[:delay] = nil if options[:delay] && options[:delay] < 5000
7
+ payload = worker.to_yaml
8
+ enqueue_payload( payload, options )
9
+ end
10
+
11
+ def self.enqueue_payload( payload, options={} )
12
+ delay = options.delete( :delay )
13
+ delay = nil if delay && delay < 5000
8
14
 
9
15
  mq = ::Bunny.new.tap { |bunny| bunny.start }
10
16
  channel = mq.create_channel
11
17
 
12
- if options[:delay]
13
- delay_x = channel.direct( "#{DELAY_EXCHANGE_PREFIX}-#{options[:delay]}ms", durable: true )
14
- work_x = channel.direct( WORK_EXCHANGE, durable: true )
15
- queue = channel.queue( "#{DELAY_QUEUE_PREFIX}-#{options[:delay]}ms",
16
- durable: true,
17
- arguments: { "x-dead-letter-exchange" => work_x.name,
18
- "x-message-ttl" => options[:delay] } ).
19
- bind( delay_x )
18
+ if delay
19
+ delay_x = channel.direct( "#{DELAY_EXCHANGE_PREFIX}-#{delay}ms", durable: true )
20
+ work_x = channel.direct( WORK_EXCHANGE, durable: true )
20
21
 
21
- delay_x.publish( 'hello', durable: true )
22
+ channel.queue( "#{DELAY_QUEUE_PREFIX}-#{delay}ms",
23
+ durable: true,
24
+ arguments: { "x-dead-letter-exchange" => work_x.name,
25
+ "x-message-ttl" => delay } ).
26
+ bind( delay_x )
27
+
28
+ delay_x.publish( payload, durable: true,
29
+ headers: options )
22
30
  return
23
31
  end
24
32
 
25
33
  work_q = channel.queue( QUEUE, durable: true )
26
- work_q.publish( 'hello', durable: true )
34
+ work_q.publish( payload, durable: true,
35
+ headers: options )
27
36
  end
28
37
 
38
+
29
39
  end
30
40
  end
@@ -0,0 +1,10 @@
1
+ module RabbitWQ
2
+ module Worker
3
+
4
+ def work( options={} )
5
+ RabbitWQ::Work.enqueue( self, options )
6
+ self
7
+ end
8
+
9
+ end
10
+ end
data/lib/rabbit_wq.rb CHANGED
@@ -4,20 +4,23 @@ module RabbitWQ
4
4
 
5
5
  APP_ID = "rabbit-wq"
6
6
  APP_NAME = "Rabbit Work Queue"
7
- COMPANY = "Look Forward Enterprises"
8
7
  DELAY_QUEUE_PREFIX = "work-delay" # TODO: Make this configurable (from ENV, or file?)
9
8
  DELAY_EXCHANGE_PREFIX = "work-delay" # TODO: Make this configurable (from ENV, or file?)
9
+ ERROR_QUEUE = "work-error"
10
10
  INT = "INT"
11
11
  QUEUE = "work" # TODO: Make this configurable (from ENV, or file?)
12
- VERSION_COPYRIGHT = "v#{VERSION} \u00A9#{Time.now.year} #{COMPANY}"
12
+ VERSION_COPYRIGHT = "v#{VERSION} \u00A9#{Time.now.year}"
13
13
  WORK_EXCHANGE = "work" # TODO: Make this configurable (from ENV, or file?)
14
14
 
15
+ autoload :Command, 'rabbit_wq/command'
15
16
  autoload :Configuration, 'rabbit_wq/configuration'
16
17
  autoload :Logging, 'rabbit_wq/logging'
18
+ autoload :Queues, 'rabbit_wq/queues'
17
19
  autoload :MessageHandler, 'rabbit_wq/message_handler'
18
20
  autoload :Server, 'rabbit_wq/server'
19
21
  autoload :ServerLogging, 'rabbit_wq/server_logging'
20
22
  autoload :Work, 'rabbit_wq/work'
23
+ autoload :Worker, 'rabbit_wq/worker'
21
24
 
22
25
  def self.configuration
23
26
  @configuration ||= Configuration.new
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rabbit-wq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-11-27 00:00:00.000000000 Z
12
+ date: 2013-11-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -126,13 +126,16 @@ files:
126
126
  - lib/rabbit-wq.rb
127
127
  - lib/rabbit_wq.rb
128
128
  - lib/rabbit_wq/cli.rb
129
+ - lib/rabbit_wq/command.rb
129
130
  - lib/rabbit_wq/configuration.rb
130
131
  - lib/rabbit_wq/logging.rb
131
132
  - lib/rabbit_wq/message_handler.rb
133
+ - lib/rabbit_wq/queues.rb
132
134
  - lib/rabbit_wq/server.rb
133
135
  - lib/rabbit_wq/server_logging.rb
134
136
  - lib/rabbit_wq/version.rb
135
137
  - lib/rabbit_wq/work.rb
138
+ - lib/rabbit_wq/worker.rb
136
139
  - rabbit-wq.gemspec
137
140
  homepage: ''
138
141
  licenses:
@@ -149,7 +152,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
149
152
  version: '0'
150
153
  segments:
151
154
  - 0
152
- hash: -1285656029023150188
155
+ hash: 2689177657118754314
153
156
  required_rubygems_version: !ruby/object:Gem::Requirement
154
157
  none: false
155
158
  requirements:
@@ -158,7 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
161
  version: '0'
159
162
  segments:
160
163
  - 0
161
- hash: -1285656029023150188
164
+ hash: 2689177657118754314
162
165
  requirements: []
163
166
  rubyforge_project:
164
167
  rubygems_version: 1.8.25