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 +4 -4
- data/RELEASE_NOTES.rdoc +6 -0
- data/lib/beetle/message.rb +14 -3
- data/lib/beetle/r_c.rb +1 -0
- data/lib/beetle/version.rb +1 -1
- data/test/beetle/message_test.rb +38 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 136ef3ee13c61c833081834cca6ad34323af71328f3ec1f21b6aa412167cf1a2
|
4
|
+
data.tar.gz: 026b2705d3b6d202b756ff6da3b56a385da5dc16453e2099419fe69c7f69905f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 40233f2af492cccd37792173a1541dc6f3bb55c59a1709fec2d2fb437ff62d12e3ff6d0243284a142dd0b8d51dacf2e5d2b45afd2bd244e8f5eae6dbdbd81257
|
7
|
+
data.tar.gz: 4dc724d9f5566102642191bc0628b83fc9b933f6a03418b5f9e5dbb0433e66ec8d6450d91d3ba7593d4436b91d5511a297287db393970d4aba30d5856cd269fb
|
data/RELEASE_NOTES.rdoc
CHANGED
@@ -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
|
data/lib/beetle/message.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require "timeout"
|
2
2
|
|
3
3
|
module Beetle
|
4
|
-
# Instances of class Message are created when a
|
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
|
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!
|
data/lib/beetle/r_c.rb
CHANGED
data/lib/beetle/version.rb
CHANGED
data/test/beetle/message_test.rb
CHANGED
@@ -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
|
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.
|
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-
|
15
|
+
date: 2018-07-31 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: uuid4r
|