dispatch-rider 1.0.3 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- YmRhMWRiODkzNDMzMDU5NzBjYjNlYzFjZGE3Yzk5ZGFlNDIxMDViOA==
5
- data.tar.gz: !binary |-
6
- ODRkMGIyZWUzODdhMmYxNDg0MTgzZjQxYmQ3OGJlYWFkZTE1YTk0ZQ==
2
+ SHA1:
3
+ metadata.gz: 5abe7d067b30a87f2e255eae8d4cfc94782567b7
4
+ data.tar.gz: 2044e59ff77593e6c0d0aa10399210066228a7fd
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- ZTUzNjVhNDVkNjY0N2VjY2NmNjAxZDQ3NDk0NWZhNTY1M2ZjODA1ZjA0MGZi
10
- YzVjNmFjZWM1MWQ0OGZjNzhiMDM3MzdmOWZjMDMzOGUyYjRlMzU1YTI5YmM5
11
- YzZlMzRmNzY1MWZhYjE5NWM3MDdiNGMyNWUxZmVhYTZhZTkyZTk=
12
- data.tar.gz: !binary |-
13
- OGJjYjBkYWY5NzdlOTIxMTgyYzU4ZTI2ODc3ODgzNzM2MmE0YjMzNGJmMDI2
14
- OWM5OWQ1MTBiNmNjNzg2M2I4ZTVlODY5NTE1MGM5NzJjYmExYzMzYThmODc2
15
- ODBlZGI4YWNlYTUwMTU3MzI5MDY1MTg3ZjQzYmE3MWI4YWQ5MmM=
6
+ metadata.gz: 36e44305e710150417a6bd3f3673ff8b9fd33cb8ac44e259114eebfdb3f9d8690b270b16e03942978ea8bb4d9af2679111736d03923e9c173a3a9b88a43f9120
7
+ data.tar.gz: 336fbb538b2a9998cb3e53e26217b3dedaad6e198f25ee02e79f96513b102045b38b9881b32ffad529a0a6295f42789325bb5cb9ca8ee4a4767ad7658ac3fbbc
data/README.md CHANGED
@@ -212,6 +212,7 @@ DispatchRider.config do |config|
212
212
  end
213
213
 
214
214
  config.logger = Rails.logger
215
+ config.default_retry_timeout = 300
215
216
 
216
217
  config.error_handler = DispatchRider::DefaultErrorHandler # an object that responds to .call(message, exception)
217
218
 
@@ -243,6 +244,9 @@ Each callback can have hooks plugged into it at `before`, `after` and `around` t
243
244
  ### Manual Setup
244
245
 
245
246
  To setup a subscriber you'll need message handlers. The handlers are named the same as the message subjects.
247
+ Each handler may also specify a retry_timeout as shown below. When a job throws an exception it will be put back
248
+ on the queue in that time period if the queue supports timeouts. If the underlying queue (such as filesystem) does
249
+ not support retry then this setting is ineffective.
246
250
 
247
251
  Sample message handler:
248
252
  ```ruby
@@ -253,6 +257,33 @@ class ReadNews < DispatchRider::Handlers::Base
253
257
  puts headline
254
258
  end
255
259
  end
260
+
261
+ def retry_timeout
262
+ 10.minutes
263
+ end
264
+ end
265
+ ```
266
+
267
+ ### Timeout & retry handling
268
+
269
+ If you have a long running job, or if you wish to retry a job later, you may use two methods in your
270
+ handler class. return_to_queue and extend_timeout.
271
+
272
+ return_to_queue will retry your item immediately.
273
+ extend_timeout will tell the queue you wish to hold this item longer.
274
+
275
+ ```ruby
276
+ # app/handlers/foo_handler
277
+ class LongRunning < DispatchRider::Handlers::Base
278
+ def process(body)
279
+ my_loop.each do |item|
280
+
281
+ #... do some work ...
282
+ extend_timeout(1.hour)
283
+ end
284
+ rescue OutOfResourcesImOutError
285
+ return_to_queue #oops! Better give this to somebody else!
286
+ end
256
287
  end
257
288
  ```
258
289
 
@@ -14,6 +14,10 @@ module DispatchRider
14
14
  end
15
15
 
16
16
  delegate :before, :after, :around, :to => :callbacks
17
+
18
+ def default_retry_timeout=(val)
19
+ DispatchRider::Handlers::Base.set_default_retry(val)
20
+ end
17
21
 
18
22
  def handlers
19
23
  @handlers ||= begin
@@ -19,7 +19,7 @@ module DispatchRider
19
19
 
20
20
  def dispatch(message)
21
21
  callbacks.invoke(:dispatch_message, message) do
22
- handler_registrar.fetch(message.subject).new.do_process(message.body)
22
+ handler_registrar.fetch(message.subject).new.do_process(message)
23
23
  end
24
24
  true # success => true (delete message)
25
25
  end
@@ -3,16 +3,54 @@ module DispatchRider
3
3
  class Base
4
4
  include NamedProcess
5
5
  extend InheritanceTracking
6
+
7
+ class << self
8
+ def set_default_retry( amount )
9
+ define_method(:retry_timeout) do
10
+ amount
11
+ end
12
+ end
13
+ end
14
+
15
+ attr_reader :raw_message
6
16
 
7
- def do_process(options)
17
+ def do_process(raw_message)
8
18
  with_named_process(self.class.name) do
9
- process(options)
19
+ @raw_message = raw_message
20
+ process(raw_message.body)
10
21
  end
22
+ rescue Exception => e
23
+ self.retry if self.retry_on_failure?
24
+ raise e
11
25
  end
12
26
 
13
- def process(options)
27
+ def process(message)
14
28
  raise NotImplementedError, "Method 'process' not overridden in subclass!"
15
29
  end
30
+
31
+ protected
32
+
33
+ def extend_timeout(timeout)
34
+ raw_message.extend_timeout(timeout)
35
+ end
36
+
37
+ def return_to_queue
38
+ raw_message.return_to_queue
39
+ end
40
+
41
+ def retry
42
+ timeout = retry_timeout
43
+ case timeout
44
+ when :immediate
45
+ return_to_queue
46
+ else
47
+ extend_timeout(timeout)
48
+ end
49
+ end
50
+
51
+ def retry_on_failure?
52
+ self.respond_to? :retry_timeout
53
+ end
16
54
  end
17
55
  end
18
56
  end
@@ -4,6 +4,7 @@ module DispatchRider
4
4
  module QueueServices
5
5
  class AwsSqs < Base
6
6
  require "dispatch-rider/queue_services/aws_sqs/message_body_extractor"
7
+ require "dispatch-rider/queue_services/aws_sqs/sqs_received_message"
7
8
 
8
9
  class AbortExecution < RuntimeError; end
9
10
  class VisibilityTimeoutExceeded < RuntimeError; end
@@ -26,11 +27,11 @@ module DispatchRider
26
27
  def pop(&block)
27
28
  begin
28
29
  queue.receive_message do |raw_item|
29
- obj = ::OpenStruct.new(:item => raw_item, :message => construct_message_from(raw_item))
30
+ obj = SqsReceivedMessage.new(construct_message_from(raw_item), raw_item, queue)
30
31
 
31
- visibility_timout_shield(obj.message) do
32
- raise AbortExecution, "false received from handler" unless block.call(obj.message)
33
- obj.message
32
+ visibility_timeout_shield(obj) do
33
+ raise AbortExecution, "false received from handler" unless block.call(obj)
34
+ obj
34
35
  end
35
36
 
36
37
  end
@@ -57,17 +58,12 @@ module DispatchRider
57
58
 
58
59
  private
59
60
 
60
- def visibility_timeout
61
- queue.visibility_timeout
62
- end
63
-
64
- def visibility_timout_shield(message)
65
- start_time = Time.now
66
- timeout = visibility_timeout # capture it at start
61
+ def visibility_timeout_shield(message)
67
62
  begin
68
63
  yield
69
64
  ensure
70
- duration = Time.now - start_time
65
+ duration = Time.now - message.start_time
66
+ timeout = message.total_timeout
71
67
  raise VisibilityTimeoutExceeded, "message: #{message.subject}, #{message.body.inspect} took #{duration} seconds while the timeout was #{timeout}" if duration > timeout
72
68
  end
73
69
  end
@@ -0,0 +1,34 @@
1
+ module DispatchRider
2
+ module QueueServices
3
+ class AwsSqs < Base
4
+ class SqsReceivedMessage < ReceivedMessage
5
+ attr_reader :total_timeout, :start_time
6
+
7
+ def initialize(message, raw_item, queue)
8
+ @total_timeout = queue.visibility_timeout
9
+ @start_time = Time.now
10
+ super(message, raw_item)
11
+ end
12
+
13
+ # NOTE: Setting the visibility timeout resets the timeout to NOW and makes it visibility timeout this time
14
+ # Essentially resetting the timer on this message
15
+ def extend_timeout(timeout)
16
+ item.visibility_timeout = timeout
17
+ if timeout > 0
18
+ @total_timeout = timeout + (Time.now - start_time).to_i
19
+ end
20
+ end
21
+
22
+ # We effectively return the item to the queue by setting
23
+ # the visibility timeout to zero. The item
24
+ # should become immediately visible.
25
+ # The next receiver will reset the visibility
26
+ # to something appropriate
27
+ def return_to_queue
28
+ extend_timeout(0)
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+ end
@@ -5,6 +5,7 @@
5
5
  # initialize, push, pop and empty?
6
6
  module DispatchRider
7
7
  module QueueServices
8
+ require "dispatch-rider/queue_services/received_message"
8
9
  class Base
9
10
  attr_accessor :queue
10
11
 
@@ -26,17 +27,23 @@ module DispatchRider
26
27
  raise NotImplementedError
27
28
  end
28
29
 
30
+
31
+ #If you pass a block into pop it will wrap the deletion of the message with it's handling
29
32
  def pop(&block)
30
- obj = head
31
- if obj
32
- block.call(obj.message) && delete(obj.item)
33
- obj.message
33
+ received = head
34
+ if received
35
+ block.call(received) && delete(received.item)
36
+ received
34
37
  end
35
38
  end
36
39
 
37
40
  def head
38
41
  raw_item = raw_head
39
- raw_item && ::OpenStruct.new(:item => raw_item, :message => construct_message_from(raw_item))
42
+ raw_item && received_message_for(raw_item)
43
+ end
44
+
45
+ def received_message_for(raw_item)
46
+ QueueServices::ReceivedMessage.new(construct_message_from(raw_item), raw_item)
40
47
  end
41
48
 
42
49
  def raw_head
@@ -22,10 +22,18 @@ module DispatchRider
22
22
  def raw_head
23
23
  queue.pop
24
24
  end
25
+
26
+ def received_message_for(raw_item)
27
+ FsReceivedMessage.new(construct_message_from(raw_item), raw_item, queue)
28
+ end
25
29
 
26
30
  def construct_message_from(item)
27
31
  deserialize(item.read)
28
32
  end
33
+
34
+ def put_back(item)
35
+ queue.put_back(item)
36
+ end
29
37
 
30
38
  def delete(item)
31
39
  queue.remove item
@@ -0,0 +1,23 @@
1
+ module DispatchRider
2
+ module QueueServices
3
+ class FileSystem < Base
4
+ class FsReceivedMessage < ReceivedMessage
5
+ attr_reader :queue
6
+
7
+ def initialize(message, item, queue)
8
+ @queue = queue
9
+ super(message, item)
10
+ end
11
+
12
+ def extend_timeout(timeout)
13
+ #file system doesn't support timeouts on items, so we ignore this.
14
+ end
15
+
16
+ def return_to_queue
17
+ queue.put_back(item)
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ end
@@ -21,6 +21,11 @@ module DispatchRider
21
21
  FileUtils.mv(file_path, file_path_inflight)
22
22
  File.new(file_path_inflight)
23
23
  end
24
+
25
+ def put_back(item)
26
+ add(item)
27
+ remove(item)
28
+ end
24
29
 
25
30
  def remove(item)
26
31
  item.close
@@ -0,0 +1,23 @@
1
+ module DispatchRider
2
+ module QueueServices
3
+ class ReceivedMessage < SimpleDelegator
4
+
5
+ #Item is the raw message item as returned by the queue implementor
6
+ #it's contents will depend on the queue being used
7
+ attr_reader :item
8
+
9
+ def initialize(message, item)
10
+ @item = item
11
+ super(message)
12
+ end
13
+
14
+ def extend_timeout(time)
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def return_to_queue
19
+ raise NotImplementedError
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,4 +1,4 @@
1
1
  # This file specifies the current version of the gem.
2
2
  module DispatchRider
3
- VERSION = "1.0.3"
3
+ VERSION = "1.1.4"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dispatch-rider
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Suman Mukherjee
@@ -11,62 +11,62 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2014-01-15 00:00:00.000000000 Z
14
+ date: 2014-02-04 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activesupport
18
18
  requirement: !ruby/object:Gem::Requirement
19
19
  requirements:
20
- - - ! '>='
20
+ - - '>='
21
21
  - !ruby/object:Gem::Version
22
22
  version: '0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - ! '>='
27
+ - - '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0'
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: activemodel
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  requirements:
34
- - - ! '>='
34
+ - - '>='
35
35
  - !ruby/object:Gem::Version
36
36
  version: '0'
37
37
  type: :runtime
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
- - - ! '>='
41
+ - - '>='
42
42
  - !ruby/object:Gem::Version
43
43
  version: '0'
44
44
  - !ruby/object:Gem::Dependency
45
45
  name: daemons
46
46
  requirement: !ruby/object:Gem::Requirement
47
47
  requirements:
48
- - - ! '>='
48
+ - - '>='
49
49
  - !ruby/object:Gem::Version
50
50
  version: '0'
51
51
  type: :runtime
52
52
  prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
54
54
  requirements:
55
- - - ! '>='
55
+ - - '>='
56
56
  - !ruby/object:Gem::Version
57
57
  version: '0'
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: bundler
60
60
  requirement: !ruby/object:Gem::Requirement
61
61
  requirements:
62
- - - ! '>='
62
+ - - '>='
63
63
  - !ruby/object:Gem::Version
64
64
  version: '0'
65
65
  type: :development
66
66
  prerelease: false
67
67
  version_requirements: !ruby/object:Gem::Requirement
68
68
  requirements:
69
- - - ! '>='
69
+ - - '>='
70
70
  - !ruby/object:Gem::Version
71
71
  version: '0'
72
72
  - !ruby/object:Gem::Dependency
@@ -87,56 +87,56 @@ dependencies:
87
87
  name: rake
88
88
  requirement: !ruby/object:Gem::Requirement
89
89
  requirements:
90
- - - ! '>='
90
+ - - '>='
91
91
  - !ruby/object:Gem::Version
92
92
  version: '0'
93
93
  type: :development
94
94
  prerelease: false
95
95
  version_requirements: !ruby/object:Gem::Requirement
96
96
  requirements:
97
- - - ! '>='
97
+ - - '>='
98
98
  - !ruby/object:Gem::Version
99
99
  version: '0'
100
100
  - !ruby/object:Gem::Dependency
101
101
  name: travis-lint
102
102
  requirement: !ruby/object:Gem::Requirement
103
103
  requirements:
104
- - - ! '>='
104
+ - - '>='
105
105
  - !ruby/object:Gem::Version
106
106
  version: '0'
107
107
  type: :development
108
108
  prerelease: false
109
109
  version_requirements: !ruby/object:Gem::Requirement
110
110
  requirements:
111
- - - ! '>='
111
+ - - '>='
112
112
  - !ruby/object:Gem::Version
113
113
  version: '0'
114
114
  - !ruby/object:Gem::Dependency
115
115
  name: rspec
116
116
  requirement: !ruby/object:Gem::Requirement
117
117
  requirements:
118
- - - ! '>='
118
+ - - '>='
119
119
  - !ruby/object:Gem::Version
120
120
  version: '0'
121
121
  type: :development
122
122
  prerelease: false
123
123
  version_requirements: !ruby/object:Gem::Requirement
124
124
  requirements:
125
- - - ! '>='
125
+ - - '>='
126
126
  - !ruby/object:Gem::Version
127
127
  version: '0'
128
128
  - !ruby/object:Gem::Dependency
129
129
  name: debugger
130
130
  requirement: !ruby/object:Gem::Requirement
131
131
  requirements:
132
- - - ! '>='
132
+ - - '>='
133
133
  - !ruby/object:Gem::Version
134
134
  version: '0'
135
135
  type: :development
136
136
  prerelease: false
137
137
  version_requirements: !ruby/object:Gem::Requirement
138
138
  requirements:
139
- - - ! '>='
139
+ - - '>='
140
140
  - !ruby/object:Gem::Version
141
141
  version: '0'
142
142
  description: Messaging system that is customizable based on which queueing system
@@ -183,9 +183,12 @@ files:
183
183
  - lib/dispatch-rider/queue_services.rb
184
184
  - lib/dispatch-rider/queue_services/aws_sqs.rb
185
185
  - lib/dispatch-rider/queue_services/aws_sqs/message_body_extractor.rb
186
+ - lib/dispatch-rider/queue_services/aws_sqs/sqs_received_message.rb
186
187
  - lib/dispatch-rider/queue_services/base.rb
187
188
  - lib/dispatch-rider/queue_services/file_system.rb
189
+ - lib/dispatch-rider/queue_services/file_system/fs_received_message.rb
188
190
  - lib/dispatch-rider/queue_services/file_system/queue.rb
191
+ - lib/dispatch-rider/queue_services/received_message.rb
189
192
  - lib/dispatch-rider/queue_services/simple.rb
190
193
  - lib/dispatch-rider/registrars.rb
191
194
  - lib/dispatch-rider/registrars/base.rb
@@ -213,17 +216,17 @@ require_paths:
213
216
  - lib
214
217
  required_ruby_version: !ruby/object:Gem::Requirement
215
218
  requirements:
216
- - - ! '>='
219
+ - - '>='
217
220
  - !ruby/object:Gem::Version
218
221
  version: '0'
219
222
  required_rubygems_version: !ruby/object:Gem::Requirement
220
223
  requirements:
221
- - - ! '>='
224
+ - - '>='
222
225
  - !ruby/object:Gem::Version
223
226
  version: '0'
224
227
  requirements: []
225
228
  rubyforge_project:
226
- rubygems_version: 2.1.11
229
+ rubygems_version: 2.0.14
227
230
  signing_key:
228
231
  specification_version: 4
229
232
  summary: Messaging system based on the reactor patter. You can publish messages to