pwwka 0.6.0 → 0.7.0.RC1
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 +4 -4
- data/Gemfile.lock +9 -12
- data/README.md +28 -2
- data/lib/pwwka/channel_connector.rb +13 -1
- data/lib/pwwka/configuration.rb +2 -0
- data/lib/pwwka/receiver.rb +10 -6
- data/lib/pwwka/test_handler.rb +2 -2
- data/lib/pwwka/version.rb +1 -1
- data/spec/receiver_spec.rb +49 -2
- data/spec/spec_helper.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86fe2875f9d9221237358a25d30bcecee224554a
|
4
|
+
data.tar.gz: c1cad94e3a3d146b18480354297b5214544ecca8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b56b412dc2ce73c3dec1ca1ca1f20432bbea8565699aff56e006fa00df9605e627a58db4a6aca9db248890d5cf70a2e69e5bbe4fb093f066d65a2c706584f4ea
|
7
|
+
data.tar.gz: 154f1b930db5502349df6b0c8b15723ce4f726c95b4d742db5b277078c097afb529b17639634a9b31b079497bf1b4dfe171e55b9e903ea924d55a9746f83947d
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pwwka (0.
|
4
|
+
pwwka (0.7.0.RC1)
|
5
5
|
activemodel
|
6
6
|
activesupport
|
7
7
|
bunny
|
@@ -10,23 +10,20 @@ PATH
|
|
10
10
|
GEM
|
11
11
|
remote: https://www.rubygems.org/
|
12
12
|
specs:
|
13
|
-
activemodel (
|
14
|
-
activesupport (=
|
15
|
-
|
16
|
-
|
13
|
+
activemodel (5.0.0.1)
|
14
|
+
activesupport (= 5.0.0.1)
|
15
|
+
activesupport (5.0.0.1)
|
16
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
17
|
i18n (~> 0.7)
|
18
|
-
json (~> 1.7, >= 1.7.7)
|
19
18
|
minitest (~> 5.1)
|
20
|
-
thread_safe (~> 0.3, >= 0.3.4)
|
21
19
|
tzinfo (~> 1.1)
|
22
20
|
amq-protocol (2.0.1)
|
23
|
-
|
24
|
-
bunny (2.3.1)
|
21
|
+
bunny (2.6.1)
|
25
22
|
amq-protocol (>= 2.0.1)
|
23
|
+
concurrent-ruby (1.0.2)
|
26
24
|
diff-lcs (1.2.5)
|
27
25
|
i18n (0.7.0)
|
28
|
-
|
29
|
-
minitest (5.8.4)
|
26
|
+
minitest (5.10.1)
|
30
27
|
mono_logger (1.1.0)
|
31
28
|
multi_json (1.11.2)
|
32
29
|
rack (1.6.4)
|
@@ -76,4 +73,4 @@ DEPENDENCIES
|
|
76
73
|
rspec
|
77
74
|
|
78
75
|
BUNDLED WITH
|
79
|
-
1.
|
76
|
+
1.13.6
|
data/README.md
CHANGED
@@ -98,12 +98,12 @@ send_message!(payload, routing_key)
|
|
98
98
|
```
|
99
99
|
|
100
100
|
### Error Handling
|
101
|
+
|
101
102
|
This method accepts several strategies for handling errors, pass in using the `on_error` parameter:
|
102
103
|
|
103
104
|
* `:raise`: Log the error and raise the exception received from Bunny. (default strategy)
|
104
105
|
* `:ignore`: Log the error and return false.
|
105
|
-
* `:resque`: Log the error and return false. Also, enqueue a job with Resque
|
106
|
-
to send the message. See `send_message_async` below.
|
106
|
+
* `:resque`: Log the error and return false. Also, enqueue a job with Resque to send the message. See `send_message_async` below. **Note, this doesn't guarantee the message will actually be sent—it just guarantees an attempt is made to queue a Resque job [which could fail]**
|
107
107
|
|
108
108
|
### Delayed Messages
|
109
109
|
You might want to delay sending a message (for example, if you have just created a database
|
@@ -217,6 +217,32 @@ class ClientIndexMessageHandler
|
|
217
217
|
|
218
218
|
end
|
219
219
|
```
|
220
|
+
#### Handling Errors
|
221
|
+
|
222
|
+
By default, if your handler raises an uncaught exception, the message will be Nacked, **but not requeued**. This means
|
223
|
+
it's dropped on the floor and likely won't have been completely processed.
|
224
|
+
|
225
|
+
You can configure `requeue_on_error` in the configuration to change this behavior:
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
require 'pwwka'
|
229
|
+
Pwwka.configure do |config|
|
230
|
+
|
231
|
+
# ...
|
232
|
+
|
233
|
+
config.requeue_on_error = true
|
234
|
+
end
|
235
|
+
```
|
236
|
+
|
237
|
+
This will requeue the message **exactly once**. It uses the headers to check if the message has been retried. If it
|
238
|
+
hasn't, and your handler raises an exception, it will be placed back on the queue. The second time your handler
|
239
|
+
processes it, there is a header indicating it's been retried, so if a failure happens again, the message **is not
|
240
|
+
requeued**.
|
241
|
+
|
242
|
+
Because requeuing puts the message at the head of the queue, a hard failure will result in an infinite loop, which will
|
243
|
+
lead to filling up your queue. Nevertheless, this should address intermittent failures.
|
244
|
+
|
245
|
+
**It is recommended that you set this option**. It's off for backwards compatibility.
|
220
246
|
|
221
247
|
#### Handling Messages with Resque
|
222
248
|
|
@@ -27,6 +27,18 @@ module Pwwka
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def delayed_queue
|
30
|
+
# This works by hacking the dead letter exchange concept with a timeout.
|
31
|
+
# We set up a delayed exchange that has a delayed queue. This queue, configured below,
|
32
|
+
# sets its dead letter exchange to be the main exchange (topic_exchange above).
|
33
|
+
#
|
34
|
+
# This means that when a message send to the delayed queue is either nack'ed with no retry OR
|
35
|
+
# it's TTL expires, it will be sent to the configured dead letter exchange, which is the main topic_exchange.
|
36
|
+
#
|
37
|
+
# Since nothing is actually consuming messages on the delayed queue, the only way messages can be removed and
|
38
|
+
# sent back to the main exchange is if their TTL expires. As you can see in Pwwka::Transmitter#send_delayed_message!
|
39
|
+
# we set an expiration on the message and send it to the delayed exchange. This means that the delay time is the TTL,
|
40
|
+
# so the messages sits in the delayed queue until its TTL/delay expires, and then it's sent onto the
|
41
|
+
# main exchange for everyone to consume. Thus creating a delay.
|
30
42
|
raise_if_delayed_not_allowed
|
31
43
|
@delayed_queue ||= begin
|
32
44
|
queue = channel.queue("pwwka_delayed_#{Pwwka.environment}", durable: true,
|
@@ -35,7 +47,7 @@ module Pwwka
|
|
35
47
|
})
|
36
48
|
queue.bind(delayed_exchange)
|
37
49
|
queue
|
38
|
-
end
|
50
|
+
end
|
39
51
|
end
|
40
52
|
alias :create_delayed_queue :delayed_queue
|
41
53
|
|
data/lib/pwwka/configuration.rb
CHANGED
@@ -10,6 +10,7 @@ module Pwwka
|
|
10
10
|
attr_accessor :logger
|
11
11
|
attr_accessor :options
|
12
12
|
attr_accessor :send_message_resque_backoff_strategy
|
13
|
+
attr_accessor :requeue_on_error
|
13
14
|
|
14
15
|
def initialize
|
15
16
|
@rabbit_mq_host = nil
|
@@ -20,6 +21,7 @@ module Pwwka
|
|
20
21
|
@send_message_resque_backoff_strategy = [5, #intermittent glitch?
|
21
22
|
60, # quick interruption
|
22
23
|
600, 600, 600] # longer-term outage?
|
24
|
+
@requeue_on_error = false
|
23
25
|
end
|
24
26
|
|
25
27
|
def payload_logging
|
data/lib/pwwka/receiver.rb
CHANGED
@@ -29,9 +29,13 @@ module Pwwka
|
|
29
29
|
receiver.ack(delivery_info.delivery_tag)
|
30
30
|
logf "Processed Message on %{queue_name} -> %{payload}, %{routing_key}", queue_name: queue_name, payload: payload, routing_key: delivery_info.routing_key
|
31
31
|
rescue => e
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
if Pwwka.configuration.requeue_on_error && !delivery_info.redelivered
|
33
|
+
logf "Retrying an Error Processing Message on %{queue_name} -> %{payload}, %{routing_key}: %{exception}: %{backtrace}", queue_name: queue_name, payload: payload, routing_key: delivery_info.routing_key, exception: e, backtrace: e.backtrace.join(';'), at: :error
|
34
|
+
receiver.nack_requeue(delivery_info.delivery_tag)
|
35
|
+
else
|
36
|
+
logf "Error Processing Message on %{queue_name} -> %{payload}, %{routing_key}: %{exception}: %{backtrace}", queue_name: queue_name, payload: payload, routing_key: delivery_info.routing_key, exception: e, backtrace: e.backtrace.join(';'), at: :error
|
37
|
+
receiver.nack(delivery_info.delivery_tag)
|
38
|
+
end
|
35
39
|
end
|
36
40
|
end
|
37
41
|
rescue Interrupt => _
|
@@ -44,7 +48,7 @@ module Pwwka
|
|
44
48
|
|
45
49
|
def topic_queue
|
46
50
|
@topic_queue ||= begin
|
47
|
-
queue = channel.queue(queue_name, durable: true)
|
51
|
+
queue = channel.queue(queue_name, durable: true, arguments: {})
|
48
52
|
queue.bind(topic_exchange, routing_key: routing_key)
|
49
53
|
queue
|
50
54
|
end
|
@@ -63,14 +67,14 @@ module Pwwka
|
|
63
67
|
end
|
64
68
|
|
65
69
|
def drop_queue
|
66
|
-
topic_queue.purge
|
70
|
+
topic_queue.purge
|
67
71
|
topic_queue.delete
|
68
72
|
end
|
69
73
|
|
70
74
|
def test_teardown
|
71
75
|
drop_queue
|
72
76
|
topic_exchange.delete
|
73
|
-
channel_connector.connection_close
|
77
|
+
channel_connector.connection_close
|
74
78
|
end
|
75
79
|
|
76
80
|
end
|
data/lib/pwwka/test_handler.rb
CHANGED
@@ -30,7 +30,7 @@ module Pwwka
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
# Get the message on the queue as TestHandler::Message
|
33
|
+
# Get the message on the queue as TestHandler::Message
|
34
34
|
def pop_message
|
35
35
|
delivery_info, properties, payload = test_queue.pop
|
36
36
|
Message.new(delivery_info,
|
@@ -70,7 +70,7 @@ module Pwwka
|
|
70
70
|
channel_connector.delayed_exchange.delete
|
71
71
|
end
|
72
72
|
|
73
|
-
channel_connector.connection_close
|
73
|
+
channel_connector.connection_close
|
74
74
|
end
|
75
75
|
|
76
76
|
# Simple class to hold a popped message.
|
data/lib/pwwka/version.rb
CHANGED
data/spec/receiver_spec.rb
CHANGED
@@ -25,6 +25,7 @@ describe Pwwka::Receiver do
|
|
25
25
|
|
26
26
|
after(:each) do
|
27
27
|
Pwwka.configuration.logger = @original_logger
|
28
|
+
Pwwka.configuration.requeue_on_error = false
|
28
29
|
@receiver.test_teardown rescue nil
|
29
30
|
end
|
30
31
|
|
@@ -37,7 +38,12 @@ describe Pwwka::Receiver do
|
|
37
38
|
end
|
38
39
|
|
39
40
|
it "should nack the sent message if an error is raised" do
|
40
|
-
|
41
|
+
exception = begin
|
42
|
+
raise "blow up"
|
43
|
+
rescue => ex
|
44
|
+
ex
|
45
|
+
end
|
46
|
+
expect(HandyHandler).to receive(:handle!).and_raise(ex)
|
41
47
|
expect(@receiver).not_to receive(:ack)
|
42
48
|
expect(@receiver).to receive(:nack).with(instance_of(Fixnum))
|
43
49
|
Pwwka::Transmitter.send_message!(payload, routing_key)
|
@@ -45,7 +51,48 @@ describe Pwwka::Receiver do
|
|
45
51
|
expect(logger).to have_received(:info).with(/START Transmitting.*#{Regexp.escape(payload.to_s)}/)
|
46
52
|
expect(logger).to have_received(:info).with(/END Transmitting.*#{Regexp.escape(payload.to_s)}/)
|
47
53
|
expect(logger).to have_received(:info).with(/AFTER Transmitting.*#{Regexp.escape(payload.to_s)}/)
|
48
|
-
expect(logger).to have_received(:error).with(/Error Processing Message.*#{Regexp.escape(payload.to_s)}/)
|
54
|
+
expect(logger).to have_received(:error).with(/Error Processing Message.*#{Regexp.escape(payload.to_s)}.*#{Regexp.escape(exception.backtrace.join(';'))}/)
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when we're configured to requeue on error" do
|
58
|
+
before do
|
59
|
+
Pwwka.configuration.requeue_on_error = true
|
60
|
+
end
|
61
|
+
it "should nack_requeue the sent message if it hasn't been retried before" do
|
62
|
+
exception = begin
|
63
|
+
raise "blow up"
|
64
|
+
rescue => ex
|
65
|
+
ex
|
66
|
+
end
|
67
|
+
expect(HandyHandler).to receive(:handle!).and_raise(ex)
|
68
|
+
expect(@receiver).not_to receive(:ack)
|
69
|
+
expect(@receiver).to receive(:nack_requeue).with(instance_of(Fixnum))
|
70
|
+
Pwwka::Transmitter.send_message!(payload, routing_key)
|
71
|
+
@receiver.test_teardown # force the message to be processed and exception handled
|
72
|
+
expect(logger).to have_received(:info).with(/START Transmitting.*#{Regexp.escape(payload.to_s)}/)
|
73
|
+
expect(logger).to have_received(:info).with(/END Transmitting.*#{Regexp.escape(payload.to_s)}/)
|
74
|
+
expect(logger).to have_received(:info).with(/AFTER Transmitting.*#{Regexp.escape(payload.to_s)}/)
|
75
|
+
expect(logger).to have_received(:error).with(/Error Processing Message.*#{Regexp.escape(payload.to_s)}.*#{Regexp.escape(exception.backtrace.join(';'))}/)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should nack the sent message if it HAS been retried before" do
|
79
|
+
# Super cheesy, but I don't see another way to access this
|
80
|
+
allow_any_instance_of(Bunny::DeliveryInfo).to receive(:redelivered).and_return(true)
|
81
|
+
exception = begin
|
82
|
+
raise "blow up"
|
83
|
+
rescue => ex
|
84
|
+
ex
|
85
|
+
end
|
86
|
+
expect(HandyHandler).to receive(:handle!).and_raise(ex)
|
87
|
+
expect(@receiver).not_to receive(:ack)
|
88
|
+
expect(@receiver).to receive(:nack).with(instance_of(Fixnum))
|
89
|
+
Pwwka::Transmitter.send_message!(payload, routing_key)
|
90
|
+
@receiver.test_teardown # force the message to be processed and exception handled
|
91
|
+
expect(logger).to have_received(:info).with(/START Transmitting.*#{Regexp.escape(payload.to_s)}/)
|
92
|
+
expect(logger).to have_received(:info).with(/END Transmitting.*#{Regexp.escape(payload.to_s)}/)
|
93
|
+
expect(logger).to have_received(:info).with(/AFTER Transmitting.*#{Regexp.escape(payload.to_s)}/)
|
94
|
+
expect(logger).to have_received(:error).with(/Error Processing Message.*#{Regexp.escape(payload.to_s)}.*#{Regexp.escape(exception.backtrace.join(';'))}/)
|
95
|
+
end
|
49
96
|
end
|
50
97
|
|
51
98
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pwwka
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0.RC1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stitch Fix Engineering
|
@@ -15,7 +15,7 @@ authors:
|
|
15
15
|
autorequire:
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
|
-
date: 2016-
|
18
|
+
date: 2016-12-15 00:00:00.000000000 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: bunny
|
@@ -183,12 +183,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
183
183
|
version: '0'
|
184
184
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
185
185
|
requirements:
|
186
|
-
- - "
|
186
|
+
- - ">"
|
187
187
|
- !ruby/object:Gem::Version
|
188
|
-
version:
|
188
|
+
version: 1.3.1
|
189
189
|
requirements: []
|
190
190
|
rubyforge_project:
|
191
|
-
rubygems_version: 2.
|
191
|
+
rubygems_version: 2.5.1
|
192
192
|
signing_key:
|
193
193
|
specification_version: 4
|
194
194
|
summary: Send and receive messages via RabbitMQ
|