Skip to content

Commit 90340ab

Browse files
authored
RUBY-1587 Properly handle error labels sent by server (#1163)
1 parent 3ea25e0 commit 90340ab

File tree

7 files changed

+152
-14
lines changed

7 files changed

+152
-14
lines changed

lib/mongo/error.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def change_stream_resumable?
9696
TRANSIENT_TRANSACTION_ERROR_LABEL = 'TransientTransactionError'.freeze
9797

9898
def initialize(msg = nil)
99-
@labels = []
99+
@labels ||= []
100100
super(msg)
101101
end
102102

@@ -114,6 +114,18 @@ def label?(label)
114114
@labels.include?(label)
115115
end
116116

117+
# Gets the set of labels associated with the error.
118+
#
119+
# @example
120+
# error.labels
121+
#
122+
# @return [ Array ] The set of labels.
123+
#
124+
# @since 2.7.0
125+
def labels
126+
@labels.dup
127+
end
128+
117129
private
118130

119131
def add_label(label)

lib/mongo/error/operation_failure.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ def initialize(message = nil, result = nil, options = {})
181181
@result = result
182182
@code = options[:code]
183183
@code_name = options[:code_name]
184+
@labels = options[:labels]
184185
super(message)
185186
end
186187
end

lib/mongo/error/parser.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ class Parser
6262
# @since 2.6.0
6363
attr_reader :code_name
6464

65+
# @return [ Array ] labels The set of labels associated with the error.
66+
# @since 2.7.0
67+
attr_reader :labels
68+
6569
# Create the new parser with the returned document.
6670
#
6771
# @example Create the new parser.
@@ -88,6 +92,7 @@ def parse!
8892
document[WRITE_CONCERN_ERROR]) if document[WRITE_CONCERN_ERROR]
8993
parse_flag(@message)
9094
parse_code
95+
parse_labels
9196
end
9297

9398
def parse_single(message, key, doc = document)
@@ -147,6 +152,10 @@ def parse_code
147152
end
148153
end
149154
end
155+
156+
def parse_labels
157+
@labels = document['errorLabels'] || []
158+
end
150159
end
151160
end
152161
end

lib/mongo/operation/result.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ def raise_operation_failure
267267
raise Error::OperationFailure.new(
268268
parser.message,
269269
self,
270-
:code => parser.code, :code_name => parser.code_name)
270+
:code => parser.code, :code_name => parser.code_name, :labels => parser.labels)
271271
end
272272
private :raise_operation_failure
273273

@@ -312,6 +312,18 @@ def cluster_time
312312
first_document && first_document[CLUSTER_TIME]
313313
end
314314

315+
# Gets the set of error labels associated with the result.
316+
#
317+
# @example Get the labels.
318+
# result.labels
319+
#
320+
# @return [ Array ] labels The set of labels.
321+
#
322+
# @since 2.7.0
323+
def labels
324+
@labels ||= parser.labels
325+
end
326+
315327
private
316328

317329
def aggregate_returned_count

spec/mongo/error/operation_failure_spec.rb

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,74 @@
149149
end
150150
end
151151
end
152+
153+
describe '#labels' do
154+
155+
context 'when the result is nil' do
156+
157+
subject do
158+
described_class.new('not master (10107)', nil,
159+
:code => 10107, :code_name => 'NotMaster')
160+
end
161+
162+
it 'has no labels' do
163+
expect(subject.labels).to eq([])
164+
end
165+
end
166+
167+
context 'when the result is not nil' do
168+
169+
let(:reply_document) do
170+
{
171+
'code' => 251,
172+
'codeName' => 'NoSuchTransaction',
173+
'errorLabels' => labels,
174+
}
175+
end
176+
177+
let(:reply) do
178+
Mongo::Protocol::Reply.new.tap do |r|
179+
# Because this was not created by Mongo::Protocol::Reply::deserialize, we need to manually
180+
# initialize the fields.
181+
r.instance_variable_set(:@documents, [reply_document])
182+
r.instance_variable_set(:@flags, [])
183+
end
184+
end
185+
186+
let(:result) do
187+
Mongo::Operation::Result.new(reply)
188+
end
189+
190+
subject do
191+
begin
192+
result.send(:raise_operation_failure)
193+
rescue => e
194+
e
195+
end
196+
end
197+
198+
context 'when the error has no labels' do
199+
200+
let(:labels) do
201+
[]
202+
end
203+
204+
it 'has the correct labels' do
205+
expect(subject.labels).to eq(labels)
206+
end
207+
end
208+
209+
210+
context 'when the error has labels' do
211+
212+
let(:labels) do
213+
[ Mongo::Error::TRANSIENT_TRANSACTION_ERROR_LABEL ]
214+
end
215+
216+
it 'has the correct labels' do
217+
expect(subject.labels).to eq(labels)
218+
end
219+
end
220+
end
221+
end
152222
end

spec/mongo/error/parser_spec.rb

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@
9595
end
9696
end
9797
end
98-
98+
9999
describe '#code' do
100100
let(:parser) do
101101
described_class.new(document)
@@ -105,7 +105,7 @@
105105
let(:document) do
106106
{ 'ok' => 0, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
107107
end
108-
108+
109109
it 'returns the code' do
110110
expect(parser.code).to eq(10107)
111111
end
@@ -115,7 +115,7 @@
115115
let(:document) do
116116
{ 'ok' => 0, 'errmsg' => 'not master' }
117117
end
118-
118+
119119
it 'returns nil' do
120120
expect(parser.code).to eq(nil)
121121
end
@@ -143,7 +143,7 @@
143143
end
144144
end
145145
end
146-
146+
147147
describe '#code_name' do
148148
let(:parser) do
149149
described_class.new(document)
@@ -153,7 +153,7 @@
153153
let(:document) do
154154
{ 'ok' => 0, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
155155
end
156-
156+
157157
it 'returns the code name' do
158158
expect(parser.code_name).to eq('NotMaster')
159159
end
@@ -163,7 +163,7 @@
163163
let(:document) do
164164
{ 'ok' => 0, 'errmsg' => 'not master' }
165165
end
166-
166+
167167
it 'returns nil' do
168168
expect(parser.code_name).to eq(nil)
169169
end
@@ -192,7 +192,7 @@
192192
end
193193
end
194194
end
195-
195+
196196
describe '#document' do
197197
let(:parser) do
198198
described_class.new(document)
@@ -201,12 +201,12 @@
201201
let(:document) do
202202
{ 'ok' => 0, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
203203
end
204-
204+
205205
it 'returns the document' do
206206
expect(parser.document).to eq(document)
207207
end
208208
end
209-
209+
210210
describe '#replies' do
211211
let(:parser) do
212212
described_class.new(document)
@@ -216,10 +216,44 @@
216216
let(:document) do
217217
{ 'ok' => 0, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
218218
end
219-
219+
220220
it 'returns nil' do
221221
expect(parser.replies).to eq(nil)
222222
end
223223
end
224224
end
225+
226+
describe '#labels' do
227+
let(:parser) do
228+
described_class.new(document)
229+
end
230+
231+
let(:document) do
232+
{
233+
'code' => 251,
234+
'codeName' => 'NoSuchTransaction',
235+
'errorLabels' => labels,
236+
}
237+
end
238+
239+
context 'when there are no labels' do
240+
let(:labels) do
241+
[]
242+
end
243+
244+
it 'has the correct labels' do
245+
expect(parser.labels).to eq(labels)
246+
end
247+
end
248+
249+
context 'when there are labels' do
250+
let(:labels) do
251+
[ Mongo::Error::TRANSIENT_TRANSACTION_ERROR_LABEL ]
252+
end
253+
254+
it 'has the correct labels' do
255+
expect(parser.labels).to eq(labels)
256+
end
257+
end
258+
end
225259
end

spec/support/transactions/operation.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,12 @@ def execute(collection, session0, session1)
114114
{
115115
'errorCodeName' => err_doc['codeName'] || err_doc['writeConcernError']['codeName'],
116116
'errorContains' => e.message,
117-
'errorLabels' => (e.instance_variable_get(:@labels) || []) + (err_doc['errorLabels'] || [])
117+
'errorLabels' => e.labels
118118
}
119119
rescue Mongo::Error => e
120120
{
121121
'errorContains' => e.message,
122-
'errorLabels' => e.instance_variable_get(:@labels) || []
122+
'errorLabels' => e.labels
123123
}
124124
end
125125

0 commit comments

Comments
 (0)