sidekiq-sqs 0.0.8 → 0.0.9
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.
data/lib/sidekiq-sqs.rb
CHANGED
@@ -7,6 +7,7 @@ require 'sidekiq-sqs/fetcher'
|
|
7
7
|
require 'sidekiq-sqs/client'
|
8
8
|
require 'sidekiq-sqs/processor'
|
9
9
|
require 'sidekiq-sqs/worker'
|
10
|
+
require 'sidekiq-sqs/aws-sdk/batch_send_failure_patch'
|
10
11
|
|
11
12
|
# TODO The retry server middleware directly writes to a retry zset.
|
12
13
|
# TODO Need a queue-prefix option to support multiple rails envs
|
@@ -25,6 +26,7 @@ module Sidekiq
|
|
25
26
|
Sidekiq::Client.send :include, Sidekiq::Sqs::Client
|
26
27
|
Sidekiq::Processor.send :include, Sidekiq::Sqs::Processor
|
27
28
|
Sidekiq::Worker::ClassMethods.send :include, Sidekiq::Sqs::Worker
|
29
|
+
AWS::SQS::Queue.send :include, Sidekiq::Sqs::AwsSdk::BatchSendFailurePatch
|
28
30
|
|
29
31
|
# Can't figure how to include/extend and not get a private method...
|
30
32
|
def Sidekiq.sqs
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Sidekiq
|
2
|
+
module Sqs
|
3
|
+
module AwsSdk
|
4
|
+
module BatchSendFailurePatch
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
remove_method :batch_failures
|
9
|
+
end
|
10
|
+
|
11
|
+
def batch_failures entries, response
|
12
|
+
response[:failed].inject([]) do |failures, failure|
|
13
|
+
|
14
|
+
entry = entries.find{|e| e[:id] == failure[:id] }
|
15
|
+
|
16
|
+
details = {
|
17
|
+
:error_code => failure[:code],
|
18
|
+
:error_message => failure[:message],
|
19
|
+
:sender_fault => failure[:sender_fault],
|
20
|
+
:message => entry[:message_body] # We need this for retrying
|
21
|
+
}
|
22
|
+
|
23
|
+
if handle = entry[:receipt_handle]
|
24
|
+
details[:receipt_handle] = handle
|
25
|
+
end
|
26
|
+
|
27
|
+
failures << details
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/sidekiq-sqs/client.rb
CHANGED
@@ -6,6 +6,25 @@ module Sidekiq
|
|
6
6
|
module Client
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
|
+
RETRYABLE_ERRORS = %w(
|
10
|
+
AWS.SimpleQueueService.InternalError
|
11
|
+
InternalError
|
12
|
+
RequestThrottled
|
13
|
+
ServiceUnavailable
|
14
|
+
)
|
15
|
+
|
16
|
+
class BulkInsertionError < StandardError
|
17
|
+
attr_reader :failed
|
18
|
+
|
19
|
+
def initialize(message, failed = [])
|
20
|
+
super(message)
|
21
|
+
@failed = failed
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Retryable < StandardError
|
26
|
+
end
|
27
|
+
|
9
28
|
included do
|
10
29
|
class << self
|
11
30
|
remove_method :push
|
@@ -31,22 +50,71 @@ module Sidekiq
|
|
31
50
|
pushed ? normed['jid'] : nil
|
32
51
|
end
|
33
52
|
|
53
|
+
MAX_BULK_RETRIES = 5
|
34
54
|
def push_bulk(items)
|
55
|
+
queue_name, payloads = format_items(items)
|
56
|
+
queue = queue_or_create(queue_name)
|
57
|
+
|
58
|
+
failures, can_retry = bulk_send_to_sqs(queue, payloads)
|
59
|
+
|
60
|
+
retries = 0
|
61
|
+
begin
|
62
|
+
if can_retry.size > 0
|
63
|
+
failed, can_retry = bulk_send_to_sqs(queue, can_retry)
|
64
|
+
failures.concat failed
|
65
|
+
|
66
|
+
raise Retryable if can_retry.size > 0
|
67
|
+
end
|
68
|
+
rescue Retryable
|
69
|
+
sleep retries ** 2
|
70
|
+
retry if (retries += 1) < MAX_BULK_RETRIES
|
71
|
+
end
|
72
|
+
|
73
|
+
if failures.size > 0
|
74
|
+
raise BulkInsertionError.new("Some messages failed to insert", failed)
|
75
|
+
end
|
76
|
+
|
77
|
+
failures.empty? ? payloads.size : nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def format_items(items)
|
35
81
|
normed = normalize_item(items)
|
36
82
|
payloads = items['args'].map do |args|
|
37
83
|
_, payload = process_single(items['class'], normed.merge('args' => args, 'jid' => SecureRandom.hex(12)))
|
38
84
|
payload
|
39
85
|
end.compact
|
40
86
|
|
41
|
-
|
42
|
-
|
87
|
+
[normed['queue'], payloads]
|
88
|
+
end
|
43
89
|
|
44
|
-
|
45
|
-
|
46
|
-
|
90
|
+
def bulk_send_to_sqs(queue, formatted_items)
|
91
|
+
failures = []
|
92
|
+
can_retry = []
|
93
|
+
formatted_items.each_slice(10) do |items|
|
94
|
+
failed, retryable = send_batch_to_sqs(queue, items)
|
95
|
+
|
96
|
+
failures.concat failed
|
97
|
+
can_retry.concat retryable
|
47
98
|
end
|
48
99
|
|
49
|
-
|
100
|
+
[failures, can_retry]
|
101
|
+
end
|
102
|
+
|
103
|
+
def send_batch_to_sqs(queue, formatted_items)
|
104
|
+
failures, retryables = [], []
|
105
|
+
|
106
|
+
begin
|
107
|
+
queue.batch_send(formatted_items)
|
108
|
+
rescue AWS::SQS::Errors::BatchSendError => error
|
109
|
+
retryable, failed = error.failures.partition do |failure|
|
110
|
+
RETRYABLE_ERRORS.include?(failure[:error_code])
|
111
|
+
end
|
112
|
+
|
113
|
+
failures.concat failed
|
114
|
+
retryables.concat retryable
|
115
|
+
end
|
116
|
+
|
117
|
+
[failures, retryables]
|
50
118
|
end
|
51
119
|
|
52
120
|
def queue_or_create(queue)
|
data/lib/sidekiq-sqs/version.rb
CHANGED
@@ -10,6 +10,10 @@ class StubClient
|
|
10
10
|
def self.process_single(*args)
|
11
11
|
end
|
12
12
|
|
13
|
+
def self.normalize_item(*args)
|
14
|
+
Hash.new
|
15
|
+
end
|
16
|
+
|
13
17
|
include Sidekiq::Sqs::Client
|
14
18
|
end
|
15
19
|
|
@@ -24,6 +28,104 @@ describe Sidekiq::Sqs::Client do
|
|
24
28
|
end
|
25
29
|
end
|
26
30
|
|
31
|
+
describe ".bulk_send_to_sqs" do
|
32
|
+
let(:retryable) do
|
33
|
+
{:error_code => 'ServiceUnavailable', :message_body => "blarg"}
|
34
|
+
end
|
35
|
+
let(:failed) do
|
36
|
+
{:error_code => "GFYS", :message_body => "and your little dog, too"}
|
37
|
+
end
|
38
|
+
it "dispatches to .send_batch_to_sqs in groups of 10" do
|
39
|
+
items = 1.upto(20).to_a
|
40
|
+
subject.expects(:send_batch_to_sqs).with(:queue, 1.upto(10).to_a).returns([[], []])
|
41
|
+
subject.expects(:send_batch_to_sqs).with(:queue, 11.upto(20).to_a).returns([[], []])
|
42
|
+
|
43
|
+
subject.bulk_send_to_sqs(:queue, items)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "aggregates failed and retryable messages" do
|
47
|
+
items = 1.upto(20).to_a
|
48
|
+
subject.expects(:send_batch_to_sqs).with(:queue, 1.upto(10).to_a).returns([[failed], []])
|
49
|
+
subject.expects(:send_batch_to_sqs).with(:queue, 11.upto(20).to_a).returns([[], [retryable]])
|
50
|
+
|
51
|
+
subject.bulk_send_to_sqs(:queue, items).should eq([[failed], [retryable]])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe ".send_batch_to_sqs" do
|
56
|
+
let(:queue) { stub }
|
57
|
+
let(:retryable) do
|
58
|
+
{:error_code => 'ServiceUnavailable', :message_body => "blarg"}
|
59
|
+
end
|
60
|
+
let(:failed) do
|
61
|
+
{:error_code => "GFYS", :message_body => "and your little dog, too"}
|
62
|
+
end
|
63
|
+
|
64
|
+
it "dispatches to the queue" do
|
65
|
+
queue.expects(:batch_send).with(:items)
|
66
|
+
|
67
|
+
subject.send_batch_to_sqs(queue, :items).should eq([[], []])
|
68
|
+
end
|
69
|
+
|
70
|
+
it "aggregates errors correctly" do
|
71
|
+
queue.expects(:batch_send).with(:items).raises(
|
72
|
+
AWS::SQS::Errors::BatchSendError.new(
|
73
|
+
[:sent],
|
74
|
+
[failed, retryable]
|
75
|
+
)
|
76
|
+
)
|
77
|
+
|
78
|
+
subject.send_batch_to_sqs(queue, :items).should eq([
|
79
|
+
[failed], [retryable]
|
80
|
+
])
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe ".push_bulk" do
|
85
|
+
let(:queue) { stub }
|
86
|
+
let(:retries) { [] }
|
87
|
+
let(:fails) { [] }
|
88
|
+
|
89
|
+
before do
|
90
|
+
subject.stubs(queue_or_create: queue)
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
it "needs more tests"
|
95
|
+
|
96
|
+
context "when some messages fail to insert" do
|
97
|
+
before do
|
98
|
+
subject.expects(:bulk_send_to_sqs).with(queue, :payloads).returns([fails, retries])
|
99
|
+
end
|
100
|
+
|
101
|
+
context "and all are retryable" do
|
102
|
+
before do
|
103
|
+
retries.push :error
|
104
|
+
|
105
|
+
subject.expects(:format_items).with(:items).returns([:queue, :payloads])
|
106
|
+
end
|
107
|
+
|
108
|
+
it "retries" do
|
109
|
+
subject.expects(:bulk_send_to_sqs).with(queue, [:error]).returns([[], []])
|
110
|
+
|
111
|
+
subject.push_bulk(:items)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context "and there are non-tryable failures" do
|
116
|
+
before do
|
117
|
+
fails.push :fail
|
118
|
+
|
119
|
+
subject.expects(:format_items).with(:items).returns([:queue, :payloads])
|
120
|
+
end
|
121
|
+
|
122
|
+
it "raises an error" do
|
123
|
+
expect { subject.push_bulk :items }.to raise_error(Sidekiq::Sqs::Client::BulkInsertionError)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
27
129
|
describe ".queue_or_create" do
|
28
130
|
let(:queue) { 'queue' }
|
29
131
|
let(:queues) { stub }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-sqs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
12
|
+
date: 2012-10-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -154,6 +154,7 @@ files:
|
|
154
154
|
- README.md
|
155
155
|
- Rakefile
|
156
156
|
- lib/sidekiq-sqs.rb
|
157
|
+
- lib/sidekiq-sqs/aws-sdk/batch_send_failure_patch.rb
|
157
158
|
- lib/sidekiq-sqs/client.rb
|
158
159
|
- lib/sidekiq-sqs/fetcher.rb
|
159
160
|
- lib/sidekiq-sqs/manager.rb
|