beetle 2.1.2 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f5f5128c31760c138a20945e91916f87f3da03794c9b342ba94a810325b88fa6
4
- data.tar.gz: 6166c1d54ac5266f1fd66e306736df5f27ee3286e4f12d9dbb20de690367be9b
3
+ metadata.gz: 136ef3ee13c61c833081834cca6ad34323af71328f3ec1f21b6aa412167cf1a2
4
+ data.tar.gz: 026b2705d3b6d202b756ff6da3b56a385da5dc16453e2099419fe69c7f69905f
5
5
  SHA512:
6
- metadata.gz: e5ead5bd603609b459e3d76f7a917d5d9b3346e641936b20695f15e53740de358f25c796f6a69e97c644544b92e28832bcf11532dd9d13a50253174d7922b6f0
7
- data.tar.gz: 3b5edb7ff6978a85ce29a426148918a2354ad6f065afb6124753e624b588f1e479bf4c81f556434d1d3a01638bfdd826e1de39976c44cbdaa2bc0b80cf073b5a
6
+ metadata.gz: 40233f2af492cccd37792173a1541dc6f3bb55c59a1709fec2d2fb437ff62d12e3ff6d0243284a142dd0b8d51dacf2e5d2b45afd2bd244e8f5eae6dbdbd81257
7
+ data.tar.gz: 4dc724d9f5566102642191bc0628b83fc9b933f6a03418b5f9e5dbb0433e66ec8d6450d91d3ba7593d4436b91d5511a297287db393970d4aba30d5856cd269fb
@@ -1,5 +1,11 @@
1
1
  = Release Notes
2
2
 
3
+ == Version 2.2.0
4
+
5
+ * Support specifying a whitelist of retriable exceptions when registering a
6
+ message handler. Exceptions which are not on the list will be regarded as
7
+ irrecoverable failures.
8
+
3
9
  == Version 2.1.2
4
10
 
5
11
  * Fixed that redis key GC would never complete when a key scheduled
@@ -1,7 +1,7 @@
1
1
  require "timeout"
2
2
 
3
3
  module Beetle
4
- # Instances of class Message are created when a scubscription callback fires. Class
4
+ # Instances of class Message are created when a subscription callback fires. Class
5
5
  # Message contains the code responsible for message deduplication and determining if it
6
6
  # should retry executing the message handler after a handler has crashed (or forcefully
7
7
  # aborted).
@@ -52,6 +52,8 @@ module Beetle
52
52
  attr_reader :attempts_limit
53
53
  # how many exceptions we should tolerate before giving up
54
54
  attr_reader :exceptions_limit
55
+ # array of exceptions accepted to be rescued and retried
56
+ attr_reader :retry_on
55
57
  # exception raised by handler execution
56
58
  attr_reader :exception
57
59
  # value returned by handler execution
@@ -72,6 +74,7 @@ module Beetle
72
74
  @attempts_limit = opts[:attempts] || DEFAULT_HANDLER_EXECUTION_ATTEMPTS
73
75
  @exceptions_limit = opts[:exceptions] || DEFAULT_EXCEPTION_LIMIT
74
76
  @attempts_limit = @exceptions_limit + 1 if @attempts_limit <= @exceptions_limit
77
+ @retry_on = opts[:retry_on] || nil
75
78
  @store = opts[:store]
76
79
  max_delay = opts[:max_delay] || @delay
77
80
  @max_delay = max_delay if max_delay >= 2*@delay
@@ -79,7 +82,6 @@ module Beetle
79
82
 
80
83
  # extracts various values from the AMQP header properties
81
84
  def decode #:nodoc:
82
- # p header.attributes
83
85
  amqp_headers = header.attributes
84
86
  @uuid = amqp_headers[:message_id]
85
87
  @timestamp = amqp_headers[:timestamp]
@@ -138,7 +140,7 @@ module Beetle
138
140
  Time.now.to_i
139
141
  end
140
142
 
141
- # a message has expired if the header expiration timestamp is msaller than the current time
143
+ # a message has expired if the header expiration timestamp is smaller than the current time
142
144
  def expired?
143
145
  @expires_at < now
144
146
  end
@@ -218,6 +220,10 @@ module Beetle
218
220
  @store.get(msg_id, :exceptions).to_i > exceptions_limit
219
221
  end
220
222
 
223
+ def exception_accepted?
224
+ @exception.nil? || retry_on.nil? || retry_on.any?{ |klass| @exception.is_a? klass}
225
+ end
226
+
221
227
  # have we already seen this message? if not, set the status to "incomplete" and store
222
228
  # the message exipration timestamp in the deduplication store.
223
229
  def key_exists?
@@ -347,6 +353,11 @@ module Beetle
347
353
  ack!
348
354
  logger.debug "Beetle: reached the handler exceptions limit: #{exceptions_limit} on #{msg_id}"
349
355
  RC::ExceptionsLimitReached
356
+ elsif !exception_accepted?
357
+ completed!
358
+ ack!
359
+ logger.debug "Beetle: `#{@exception.class.name}` not accepted: `retry_on`=[#{retry_on.join(',')}] on #{msg_id}"
360
+ RC::ExceptionNotAccepted
350
361
  else
351
362
  delete_mutex!
352
363
  timed_out!
@@ -30,6 +30,7 @@ module Beetle
30
30
  rc :Ancient
31
31
  rc :AttemptsLimitReached, :failure
32
32
  rc :ExceptionsLimitReached, :failure
33
+ rc :ExceptionNotAccepted, :failure
33
34
  rc :Delayed, :reject
34
35
  rc :HandlerCrash, :reject
35
36
  rc :HandlerNotYetTimedOut, :reject
@@ -1,3 +1,3 @@
1
1
  module Beetle
2
- VERSION = "2.1.2"
2
+ VERSION = "2.2.0"
3
3
  end
@@ -345,7 +345,7 @@ module Beetle
345
345
  assert_equal "52", @store.get(message.msg_id, :delay)
346
346
  end
347
347
 
348
- test "a message should delete the mutex before resetting the timer if attempts and exception limits havn't been reached" do
348
+ test "a message should delete the mutex before resetting the timer if attempts and exception limits haven't been reached" do
349
349
  Message.stubs(:now).returns(9)
350
350
  header = header_with_params({})
351
351
  message = Message.new("somequeue", header, 'foo', :delay => 42, :timeout => 10.seconds, :exceptions => 1, :store => @store)
@@ -377,6 +377,41 @@ module Beetle
377
377
  assert_equal RC::ExceptionsLimitReached, message.__send__(:process_internal, proc)
378
378
  end
379
379
 
380
+ test "a message should not be acked if the handler crashes and the exception has been registered" do
381
+ header = header_with_params({})
382
+ RegisteredException = Class.new(StandardError)
383
+ message = Message.new("somequeue", header, 'foo', :timeout => 10.seconds, :exceptions => 2,
384
+ :retry_on => [RegisteredException], :store => @store)
385
+ assert !message.attempts_limit_reached?
386
+ assert !message.exceptions_limit_reached?
387
+ assert !message.timed_out?
388
+ assert !message.simple?
389
+ assert message.exception_accepted? # @exception yet nil, hence 'accepted'
390
+
391
+ proc = lambda {|_| raise RegisteredException, "crash"}
392
+ message.expects(:completed!).never
393
+ header.expects(:ack).never
394
+ assert_equal RC::HandlerCrash, message.__send__(:process_internal, proc)
395
+ end
396
+
397
+ test "a message should be acked if the handler crashes and the exception has not been registered" do
398
+ header = header_with_params({})
399
+ RegisteredException = Class.new(StandardError)
400
+ OtherException = Class.new(StandardError)
401
+ message = Message.new("somequeue", header, 'foo', :timeout => 10.seconds, :exceptions => 2,
402
+ :retry_on => [RegisteredException], :store => @store)
403
+ assert !message.attempts_limit_reached?
404
+ assert !message.exceptions_limit_reached?
405
+ assert !message.timed_out?
406
+ assert !message.simple?
407
+ assert message.exception_accepted? # @exception yet nil, hence 'accepted'
408
+
409
+ proc = lambda {|_| raise OtherException, "crash"}
410
+ message.expects(:completed!).once
411
+ header.expects(:ack)
412
+ assert_equal RC::ExceptionNotAccepted, message.__send__(:process_internal, proc)
413
+ end
414
+
380
415
  test "a message should be acked if the handler crashes and the attempts limit has been reached" do
381
416
  header = header_with_params({})
382
417
  message = Message.new("somequeue", header, 'foo', :timeout => 10.seconds, :attempts => 2, :store => @store)
@@ -647,8 +682,8 @@ module Beetle
647
682
  adapter: "mysql2",
648
683
  username: "root",
649
684
  encoding: "utf8",
650
- host: "127.0.0.1",
651
- port: 3306
685
+ host: ENV['MYSQL_HOST'] || "127.0.0.1",
686
+ port: ENV['MYSQL_PORT'] || 3306
652
687
  )
653
688
  end
654
689
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beetle
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.2
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Kaes
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2018-06-18 00:00:00.000000000 Z
15
+ date: 2018-07-31 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: uuid4r