beetle 2.1.2 → 2.2.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.
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