dispatch-rider 1.0.3 → 1.1.4

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