1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
# frozen_string_literal: true
# rubocop:todo all
require 'spec_helper'
describe 'Cursor reaping' do
# JRuby does reap cursors but GC.start does not force GC to run like it does
# in MRI, I don't currently know how to force GC to run in JRuby
require_mri
# Uncomment for debugging this test.
=begin
around(:all) do |example|
saved_level = Mongo::Logger.logger.level
Mongo::Logger.logger.level = Logger::DEBUG
begin
example.run
ensure
Mongo::Logger.logger.level = saved_level
end
end
=end
let(:subscriber) { Mrss::EventSubscriber.new }
let(:client) do
authorized_client.with(max_pool_size: 10).tap do |client|
client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
end
end
let(:collection) { client['cursor_reaping_spec'] }
before do
data = [{a: 1}] * 10
authorized_client['cursor_reaping_spec'].delete_many
authorized_client['cursor_reaping_spec'].insert_many(data)
end
context 'a no-timeout cursor' do
it 'reaps nothing when we do not query' do
# this is a base line test to ensure that the reaps in the other test
# aren't done on some global cursor
expect(Mongo::Operation::KillCursors).not_to receive(:new)
# just the scope, no query is happening
collection.find.batch_size(2).no_cursor_timeout
events = subscriber.started_events.select do |event|
event.command['killCursors']
end
expect(events).to be_empty
end
def abandon_cursors
[].tap do |cursor_ids|
# scopes are weird, having this result in a let block
# makes it not garbage collected
10.times do
scope = collection.find.batch_size(2).no_cursor_timeout
# Begin iteration, creating the cursor
scope.each.first
scope.cursor.should_not be nil
cursor_ids << scope.cursor.id
end
end
end
# this let block is a kludge to avoid copy pasting all of this code
let(:cursor_id_and_kill_event) do
expect(Mongo::Operation::KillCursors).to receive(:new).at_least(:once).and_call_original
cursor_ids = abandon_cursors
cursor_ids.each do |cursor_id|
expect(cursor_id).to be_a(Integer)
expect(cursor_id > 0).to be true
end
GC.start
sleep 1
# force periodic executor to run because its frequency is not configurable
client.cluster.instance_variable_get('@periodic_executor').execute
started_event = subscriber.started_events.detect do |event|
event.command['killCursors']
end
started_event.should_not be nil
found_cursor_id = nil
started_event = subscriber.started_events.detect do |event|
found = false
if event.command['killCursors']
cursor_ids.each do |cursor_id|
if event.command['cursors'].map { |c| Utils.int64_value(c) }.include?(cursor_id)
found_cursor_id = cursor_id
found = true
break
end
end
end
found
end
if started_event.nil?
p subscriber.started_events
end
started_event.should_not be nil
succeeded_event = subscriber.succeeded_events.detect do |event|
event.command_name == 'killCursors' && event.request_id == started_event.request_id
end
expect(succeeded_event).not_to be_nil
expect(succeeded_event.reply['ok']).to eq 1
[found_cursor_id, succeeded_event]
end
it 'is reaped' do
cursor_id_and_kill_event
end
context 'newer servers' do
min_server_fcv '3.2'
it 'is really killed' do
cursor_id, event = cursor_id_and_kill_event
expect(event.reply['cursorsKilled']).to eq([cursor_id])
expect(event.reply['cursorsNotFound']).to be_empty
expect(event.reply['cursorsAlive']).to be_empty
expect(event.reply['cursorsUnknown']).to be_empty
end
end
end
end
|