rabbit-wq 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.
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