Skip to content

Commit 4d7be76

Browse files
author
Emily Giurleo
authored
RUBY-2317 Consider CursorNotFound to be a resumable change stream error (#2022)
* RUBY-2317 Treat CursorNotFound as a resumable change stream error * simplify implementation * fixed change stream resume spec
1 parent 089c51f commit 4d7be76

File tree

4 files changed

+118
-6
lines changed

4 files changed

+118
-6
lines changed

lib/mongo/error/operation_failure.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ def write_retryable_code?
166166
# @since 2.6.0
167167
def change_stream_resumable?
168168
if @result && @result.is_a?(Mongo::Operation::GetMore::Result)
169+
# CursorNotFound exceptions are always resumable because the server
170+
# is not aware of the cursor id, and thus cannot determine if
171+
# the cursor is a change stream and cannot add the
172+
# ResumableChangeStreamError label.
173+
return true if code == 43
174+
169175
# Connection description is not populated for unacknowledged writes.
170176
if connection_description.max_wire_version >= 9
171177
label?('ResumableChangeStreamError')

spec/mongo/collection/view/change_stream_resume_spec.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,17 +196,17 @@
196196
change_stream.to_enum
197197
end
198198

199-
it 'propagates cursor not found error' do
199+
it 'resumes on a cursor not found error' do
200200
original_cursor_id = cursor.id
201201

202202
client.use(:admin).command({
203203
killCursors: collection.name,
204204
cursors: [cursor.id]
205205
})
206206

207-
lambda do
207+
expect do
208208
enum.next
209-
end.should raise_error(Mongo::Error::OperationFailure, /cursor.*not found/)
209+
end.not_to raise_error
210210
end
211211
end
212212

@@ -218,17 +218,17 @@
218218
collection.insert_one(a:2)
219219
end
220220

221-
it 'propagates cursor not found error' do
221+
it 'resumes on a cursor not found error' do
222222
original_cursor_id = cursor.id
223223

224224
client.use(:admin).command({
225225
killCursors: collection.name,
226226
cursors: [cursor.id]
227227
})
228228

229-
lambda do
229+
expect do
230230
change_stream.try_next
231-
end.should raise_error(Mongo::Error::OperationFailure, /cursor.*not found/)
231+
end.not_to raise_error
232232
end
233233
end
234234
end

spec/mongo/error/operation_failure_spec.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,46 @@
227227
end
228228
end
229229

230+
context 'when the error code is 43 (CursorNotFound)' do
231+
let(:error) { Mongo::Error::OperationFailure.new(nil, result, code: 43, code_name: 'CursorNotFound') }
232+
let(:result) do
233+
Mongo::Operation::GetMore::Result.new(
234+
Mongo::Protocol::Message.new, description)
235+
end
236+
237+
context 'wire protocol < 9' do
238+
let(:description) do
239+
Mongo::Server::Description.new('',
240+
'minWireVersion' => 0,
241+
'maxWireVersion' => 8,
242+
)
243+
end
244+
245+
it 'returns true' do
246+
# CursorNotFound exceptions are resumable even if they don't have
247+
# a ResumableChangeStreamError label because the server is not aware
248+
# of the cursor id, and thus cannot determine if it is a change stream.
249+
expect(error.change_stream_resumable?).to be true
250+
end
251+
end
252+
253+
context 'wire protocol >= 9' do
254+
let(:description) do
255+
Mongo::Server::Description.new('',
256+
'minWireVersion' => 0,
257+
'maxWireVersion' => 9,
258+
)
259+
end
260+
261+
it 'returns true' do
262+
# CursorNotFound exceptions are resumable even if they don't have
263+
# a ResumableChangeStreamError label because the server is not aware
264+
# of the cursor id, and thus cannot determine if it is a change stream.
265+
expect(error.change_stream_resumable?).to be true
266+
end
267+
end
268+
end
269+
230270
context 'not a getMore response' do
231271
let(:result) do
232272
Mongo::Operation::Result.new(

spec/spec_tests/data/change_streams/change-streams-resume-whitelist.yml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,3 +1105,69 @@ tests:
11051105
fullDocument:
11061106
x:
11071107
$numberInt: "1"
1108+
-
1109+
# CursorNotFound is special-cased to be resumable regardless of server versions or error labels, so this test has
1110+
# no maxWireVersion.
1111+
description: "change stream resumes after CursorNotFound"
1112+
minServerVersion: "4.2"
1113+
failPoint:
1114+
configureFailPoint: failCommand
1115+
mode: { times: 1 }
1116+
data:
1117+
failCommands: ["getMore"]
1118+
errorCode: 43
1119+
closeConnection: false
1120+
target: collection
1121+
topology:
1122+
- replicaset
1123+
- sharded
1124+
changeStreamPipeline: []
1125+
changeStreamOptions: {}
1126+
operations:
1127+
-
1128+
database: *database_name
1129+
collection: *collection_name
1130+
name: insertOne
1131+
arguments:
1132+
document:
1133+
x: 1
1134+
expectations:
1135+
-
1136+
command_started_event:
1137+
command:
1138+
aggregate: *collection_name
1139+
cursor: {}
1140+
pipeline:
1141+
-
1142+
$changeStream: {}
1143+
command_name: aggregate
1144+
database_name: *database_name
1145+
-
1146+
command_started_event:
1147+
command:
1148+
getMore: 42
1149+
collection: *collection_name
1150+
command_name: getMore
1151+
database_name: *database_name
1152+
-
1153+
command_started_event:
1154+
command:
1155+
aggregate: *collection_name
1156+
cursor: {}
1157+
pipeline:
1158+
-
1159+
$changeStream: {}
1160+
command_name: aggregate
1161+
database_name: *database_name
1162+
result:
1163+
success:
1164+
-
1165+
_id: "42"
1166+
documentKey: "42"
1167+
operationType: insert
1168+
ns:
1169+
db: *database_name
1170+
coll: *collection_name
1171+
fullDocument:
1172+
x:
1173+
$numberInt: "1"

0 commit comments

Comments
 (0)