Skip to content

Commit 1c20b64

Browse files
neilshwekyp
andauthored
RUBY-2682 RUBY-2443 test that driver closes cursors left open by server when result set is fully iterated (#2378)
* calculate remaining number of documents in result set for OP_MSG servers * debugging * RUBY-2682 cleaned up/added number_returned tests * RUBY-2682 add test for nonzero cursor id * RUBY-2682 fix msg test * RUBY-2682 change number_returned default * RUBY-2682 dont raise error in number_returned * RUBY-2682 fix spec test for consistency (RUBY-2443) * RUBY-2682 fix number_returned without batch test * prohibit unexpected uses of returned_count * adjust unit test Co-authored-by: Oleg Pudeyev <[email protected]>
1 parent ea7704f commit 1c20b64

File tree

7 files changed

+116
-7
lines changed

7 files changed

+116
-7
lines changed

lib/mongo/cursor.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,14 @@ def fully_iterated?
383383

384384
private
385385

386+
def batch_size_for_get_more
387+
if batch_size && use_limit?
388+
[batch_size, @remaining].min
389+
else
390+
batch_size
391+
end
392+
end
393+
386394
def exhausted?
387395
limited? ? @remaining <= 0 : false
388396
end
@@ -405,7 +413,7 @@ def get_more_operation
405413
cursor_id: id,
406414
# 3.2+ servers use batch_size, 3.0- servers use to_return.
407415
# TODO should to_return be calculated in the operation layer?
408-
batch_size: batch_size,
416+
batch_size: batch_size_for_get_more,
409417
to_return: to_return,
410418
max_time_ms: if view.respond_to?(:max_await_time_ms) &&
411419
view.max_await_time_ms &&

lib/mongo/operation/map_reduce/result.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,22 @@ def cursor_id
131131
0
132132
end
133133

134+
# Get the number of documents returned by the server in this batch.
135+
#
136+
# Map/Reduce operation returns documents inline without using
137+
# cursors; as such, the standard Mongo::Reply#returned_count does
138+
# not work correctly for Map/Reduce.
139+
#
140+
# Note that the Map/Reduce operation is limited to max BSON document
141+
# size (16 MB) in its inline result set.
142+
#
143+
# @return [ Integer ] The number of documents returned.
144+
#
145+
# @api public
146+
def returned_count
147+
reply.documents.length
148+
end
149+
134150
private
135151

136152
def first_document

lib/mongo/operation/result.rb

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,10 +238,7 @@ def reply
238238
end
239239
end
240240

241-
# Get the count of documents returned by the server.
242-
#
243-
# @example Get the number returned.
244-
# result.returned_count
241+
# Get the number of documents returned by the server in this batch.
245242
#
246243
# @return [ Integer ] The number of documents returned.
247244
#

lib/mongo/protocol/msg.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,24 @@ def maybe_add_server_api(server_api)
298298
Msg.new(@flags, @options, main_document, *@sequences)
299299
end
300300

301+
# Returns the number of documents returned from the server.
302+
#
303+
# The Msg instance must be for a server reply and the reply must return
304+
# an active cursor (either a newly created one or one whose iteration is
305+
# continuing via getMore).
306+
#
307+
# @return [ Integer ] Number of returned documents.
308+
def number_returned
309+
if doc = documents.first
310+
if cursor = doc['cursor']
311+
if batch = cursor['firstBatch'] || cursor['nextBatch']
312+
return batch.length
313+
end
314+
end
315+
end
316+
raise NotImplementedError, "number_returned is only defined for cursor replies"
317+
end
318+
301319
private
302320

303321
# Validate that the documents in this message are all smaller than the

spec/mongo/cursor_spec.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,36 @@
543543
end
544544
end
545545
end
546+
547+
context 'when the result set is iterated fully and the cursor id is non-zero' do
548+
min_server_fcv '5.0'
549+
550+
let(:documents) do
551+
(1..5).map{ |i| { field: "test#{i}" }}
552+
end
553+
554+
let(:view) { collection.find(test:{'$gte'=>BSON::MinKey.new}).sort(test:1).limit(5).batch_size(4) }
555+
556+
before do
557+
view.to_a
558+
end
559+
560+
it 'schedules a get more command' do
561+
get_more_commands = subscriber.started_events.select { |e| e.command_name == 'getMore' }
562+
expect(get_more_commands.length).to be 1
563+
end
564+
565+
it 'has a non-zero cursor id on successful get more' do
566+
get_more_commands = subscriber.succeeded_events.select { |e| e.command_name == 'getMore' }
567+
expect(get_more_commands.length).to be 1
568+
expect(get_more_commands[0].reply['cursor']['id']).to_not be 0
569+
end
570+
571+
it 'schedules a kill cursors command' do
572+
get_more_commands = subscriber.started_events.select { |e| e.command_name == 'killCursors' }
573+
expect(get_more_commands.length).to be 1
574+
end
575+
end
546576
end
547577

548578
describe '#inspect' do

spec/mongo/protocol/msg_spec.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,4 +525,45 @@
525525
end
526526
end
527527
end
528+
529+
describe '#number_returned' do
530+
531+
let(:batch) do
532+
(1..2).map{ |i| { field: "test#{i}" }}
533+
end
534+
535+
context 'when the msg contains a find document' do
536+
537+
let(:find_document) { { "cursor" => { "firstBatch" => batch } } }
538+
539+
let(:find_message) do
540+
described_class.new(flags, options, find_document, *sequences)
541+
end
542+
543+
it 'returns the correct number_returned' do
544+
expect(find_message.number_returned).to eq(2)
545+
end
546+
end
547+
548+
context 'when the msg contains a getmore document' do
549+
let(:next_document) { { "cursor" => { "nextBatch" => batch } } }
550+
551+
let(:next_message) do
552+
described_class.new(flags, options, next_document, *sequences)
553+
end
554+
555+
it 'returns the correct number_returned' do
556+
expect(next_message.number_returned).to eq(2)
557+
end
558+
end
559+
560+
context 'when the msg contains a document without first/nextBatch' do
561+
562+
it 'raises NotImplementedError' do
563+
lambda do
564+
message.number_returned
565+
end.should raise_error(NotImplementedError, /number_returned is only defined for cursor replies/)
566+
end
567+
end
568+
end
528569
end

spec/spec_tests/data/unified/valid-pass/poc-command-monitoring.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@ tests:
6969
command:
7070
getMore: { $$type: [ int, long ] }
7171
collection: *collection0Name
72-
# https://jira.mongodb.org/browse/RUBY-2443
73-
batchSize: 3
72+
batchSize: 1
7473
commandName: getMore
7574
databaseName: *database0Name
7675
- commandSucceededEvent:

0 commit comments

Comments
 (0)