Skip to content

Commit ee6301c

Browse files
p-mongop
authored andcommitted
Fix RUBY-1988 Ensure cursor IDs are always encoded as 64-bit integers in BSON when using killCursors (#1560)
1 parent ffc8ca8 commit ee6301c

File tree

5 files changed

+43
-27
lines changed

5 files changed

+43
-27
lines changed

lib/mongo/cursor/builder/kill_cursors_command.rb

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ def specification
5454
private
5555

5656
def kill_cursors_command
57-
{ :killCursors => collection_name, :cursors => [ cursor.id ] }
57+
{
58+
killCursors: collection_name,
59+
cursors: [ BSON::Int64.new(cursor.id) ],
60+
}
5861
end
5962

6063
class << self
@@ -65,23 +68,31 @@ class << self
6568
# KillCursorsCommand.update_cursors(spec, ids)
6669
#
6770
# @return [ Hash ] The specification.
68-
# @return [ Array ] The ids to update with.
71+
# @return [ Array<Integer> ] The ids to update with.
6972
#
7073
# @since 2.3.0
7174
def update_cursors(spec, ids)
72-
spec[:selector].merge!(cursors: spec[:selector][:cursors] & ids)
75+
# Ruby 2.5+ can & BSON::Int64 instances.
76+
# Ruby 2.4 and earlier cannot.
77+
# Convert stored ids to Ruby integers for compatibility with
78+
# older Rubies.
79+
ids = get_cursors_list(spec) & ids
80+
ids = ids.map do |cursor_id|
81+
BSON::Int64.new(cursor_id)
82+
end
83+
spec[:selector].merge!(cursors: ids)
7384
end
7485

7586
# Get the list of cursor ids from a spec generated by this Builder.
7687
#
7788
# @example Get the list of cursor ids.
7889
# KillCursorsCommand.cursors(spec)
7990
#
80-
# @return [ Hash ] The specification.
91+
# @return [ Array<Integer> ] The cursor ids.
8192
#
8293
# @since 2.3.0
8394
def get_cursors_list(spec)
84-
spec[:selector][:cursors]
95+
spec[:selector][:cursors].map(&:value)
8596
end
8697
end
8798
end

lib/mongo/cursor/builder/op_kill_cursors.rb

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@ def initialize(cursor)
4848
#
4949
# @since 2.2.0
5050
def specification
51-
{ :coll_name => collection_name, :db_name => database.name, :cursor_ids => [ cursor.id ] }
51+
{
52+
coll_name: collection_name,
53+
db_name: database.name,
54+
cursor_ids: [ BSON::Int64.new(cursor.id) ],
55+
}
5256
end
5357

5458
class << self
@@ -59,23 +63,31 @@ class << self
5963
# OpKillCursors.update_cursors(spec, ids)
6064
#
6165
# @return [ Hash ] The specification.
62-
# @return [ Array ] The ids to update with.
66+
# @return [ Array<Integer> ] The ids to update with.
6367
#
6468
# @since 2.3.0
6569
def update_cursors(spec, ids)
66-
spec.merge!(cursor_ids: spec[:cursor_ids] & ids)
70+
# Ruby 2.5+ can & BSON::Int64 instances.
71+
# Ruby 2.4 and earlier cannot.
72+
# Convert stored ids to Ruby integers for compatibility with
73+
# older Rubies.
74+
ids = get_cursors_list(spec) & ids
75+
ids = ids.map do |cursor_id|
76+
BSON::Int64.new(cursor_id)
77+
end
78+
spec.merge!(cursor_ids: ids)
6779
end
6880

6981
# Get the list of cursor ids from a spec generated by this Builder.
7082
#
7183
# @example Get the list of cursor ids.
7284
# OpKillCursors.cursors(spec)
7385
#
74-
# @return [ Hash ] The specification.
86+
# @return [ Array<Integer> ] The cursor ids.
7587
#
7688
# @since 2.3.0
7789
def get_cursors_list(spec)
78-
spec[:cursor_ids]
90+
spec[:cursor_ids].map(&:value)
7991
end
8092
end
8193
end

lib/mongo/protocol/kill_cursors.rb

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def payload
5252
command_name: 'killCursors',
5353
database_name: @database,
5454
command: upconverter.command,
55-
request_id: request_id
55+
request_id: request_id,
5656
)
5757
end
5858

@@ -85,16 +85,6 @@ def payload
8585
# @since 2.1.0
8686
class Upconverter
8787

88-
# The kill cursors constant.
89-
#
90-
# @since 2.2.0
91-
KILL_CURSORS = 'killCursors'.freeze
92-
93-
# The cursors constant.
94-
#
95-
# @since 2.2.0
96-
CURSORS = 'cursors'.freeze
97-
9888
# @return [ String ] collection The name of the collection.
9989
attr_reader :collection
10090

@@ -125,8 +115,11 @@ def initialize(collection, cursor_ids)
125115
# @since 2.1.0
126116
def command
127117
document = BSON::Document.new
128-
document.store(KILL_CURSORS, collection)
129-
document.store(CURSORS, cursor_ids)
118+
document.store('killCursors', collection)
119+
store_ids = cursor_ids.map do |cursor_id|
120+
BSON::Int64.new(cursor_id)
121+
end
122+
document.store('cursors', store_ids)
130123
document
131124
end
132125
end

spec/integration/cursor_reaping_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
client.cluster.instance_variable_get('@periodic_executor').execute
6060

6161
started_event = EventSubscriber.started_events.detect do |event|
62-
event.command['killCursors'] && event.command['cursors'].include?(cursor_id)
62+
event.command['killCursors'] && event.command['cursors'].map(&:value).include?(cursor_id)
6363
end
6464

6565
expect(started_event).not_to be_nil

spec/mongo/cursor_spec.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@
318318
Mongo::Collection::View.new(
319319
authorized_collection,
320320
{},
321-
:batch_size => 2
321+
:batch_size => 2,
322322
)
323323
end
324324

@@ -329,9 +329,9 @@
329329

330330
it 'schedules a kill cursors op' do
331331
cluster.instance_variable_get(:@periodic_executor).flush
332-
expect {
332+
expect do
333333
cursor.to_a
334-
}.to raise_exception(Mongo::Error::OperationFailure, /[cC]ursor.*not found/)
334+
end.to raise_exception(Mongo::Error::OperationFailure, /[cC]ursor.*not found/)
335335
end
336336

337337
context 'when the cursor is unregistered before the kill cursors operations are executed' do

0 commit comments

Comments
 (0)