summaryrefslogtreecommitdiff
path: root/jstests/core/batch_size.js
blob: 7beda6fe3491a718fbc2a621a803cc4400901810 (plain)
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
// TODO SERVER-50737: remove sbe_incompatible tag
// @tags: [
//   assumes_balancer_off,
//   requires_getmore,
//   sbe_incompatible,
// ]

// Test subtleties of batchSize and limit.

(function() {
"use strict";
load("jstests/libs/fixture_helpers.js");  // For FixtureHelpers.
var t = db.jstests_batch_size;
t.drop();

for (var i = 0; i < 4; i++) {
    t.save({_id: i, a: i});
}

function runIndexedTests() {
    // With limit, indexed.
    assert.eq(2, t.find().limit(2).itcount());
    assert.eq(2, t.find().sort({a: 1}).limit(2).itcount());

    // With batchSize, indexed.
    // SERVER-12438: If there is an index that provides the sort, then a plan with an unindexed
    // sort should never be used.  Consequently, batchSize will NOT be a hard limit in this
    // case.  WARNING: the behavior described above may change in the future.
    assert.eq(4, t.find().batchSize(2).itcount());
    assert.eq(4, t.find().sort({a: 1}).batchSize(2).itcount());
}

// Without batch size or limit, unindexed.
assert.eq(4, t.find().itcount());
assert.eq(4, t.find().sort({a: 1}).itcount());

// With limit, unindexed.
assert.eq(2, t.find().limit(2).itcount());
assert.eq(2, t.find().sort({a: 1}).limit(2).itcount());

assert.eq(4, t.find().batchSize(2).itcount());
assert.eq(4, t.find().sort({a: 1}).batchSize(2).itcount());

// With negative batchSize. A negative batchSize value instructs the server
// to return just a single batch of results.
assert.eq(1, t.find().batchSize(-1).itcount());
assert.eq(2, t.find().batchSize(-2).itcount());

// Run the tests with the index twice in order to double check plan caching.
t.createIndex({a: 1});
for (var i = 0; i < 2; i++) {
    runIndexedTests();
}

// The next tests make sure that we obey limit and batchSize properly when the sort could be
// either indexed or unindexed.
t.drop();
t.createIndex({a: 1});
t.createIndex({b: 1});

for (var i = 0; i < 100; i++) {
    t.save({_id: i, a: i, b: 1});
}

// Without a hint. Do it twice to make sure caching is ok.
for (var i = 0; i < 2; i++) {
    assert.eq(15, t.find({a: {$gte: 85}}).sort({b: 1}).batchSize(2).itcount());
    assert.eq(6, t.find({a: {$gte: 85}}).sort({b: 1}).limit(6).itcount());
}

// Hinting 'a'.
assert.eq(15, t.find({a: {$gte: 85}}).sort({b: 1}).hint({a: 1}).batchSize(2).itcount());
assert.eq(6, t.find({a: {$gte: 85}}).sort({b: 1}).hint({a: 1}).limit(6).itcount());

// Hinting 'b'.
assert.eq(15, t.find({a: {$gte: 85}}).sort({b: 1}).hint({b: 1}).batchSize(2).itcount());
assert.eq(6, t.find({a: {$gte: 85}}).sort({b: 1}).hint({b: 1}).limit(6).itcount());

// With explain.
var explain = t.find({a: {$gte: 85}}).sort({b: 1}).batchSize(2).explain("executionStats");
assert.eq(15, explain.executionStats.nReturned);
explain = t.find({a: {$gte: 85}}).sort({b: 1}).limit(6).explain("executionStats");
if (FixtureHelpers.isMongos(db)) {
    // If we're talking to a mongos, we expect at most one batch from each shard.
    assert.eq(FixtureHelpers.numberOfShardsForCollection(t) * 6, explain.executionStats.nReturned);
} else {
    assert.eq(6, explain.executionStats.nReturned);
}

// Double check that we're not scanning more stuff than we have to.  In order to get the sort
// using index 'a', we should need to scan about 50 keys and 50 documents.
var explain = t.find({a: {$gte: 50}}).sort({b: 1}).hint({a: 1}).limit(6).explain("executionStats");
assert.lte(explain.executionStats.totalKeysExamined, 60);
assert.lte(explain.executionStats.totalDocsExamined, 60);
if (FixtureHelpers.isMongos(db)) {
    // If we're talking to a mongos, we expect at most one batch from each shard.
    assert.eq(FixtureHelpers.numberOfShardsForCollection(t) * 6, explain.executionStats.nReturned);
} else {
    assert.eq(6, explain.executionStats.nReturned);
}

// -------

// During plan ranking, we treat ntoreturn as a limit. This prevents us from buffering too much
// data in a blocking sort stage during plan ranking.
t.drop();

// Generate big string to use in the object - 1MB+ String
var bigStr = "ABCDEFGHIJKLMNBOPQRSTUVWXYZ012345687890";
while (bigStr.length < 1000000) {
    bigStr = bigStr + "::" + bigStr;
}

// Insert enough documents to exceed the 32 MB in-memory sort limit.
const nDocs = 40 * FixtureHelpers.numberOfShardsForCollection(t);
for (var i = 0; i < nDocs; i++) {
    var doc = {x: 1, y: 1, z: i, big: bigStr};
    t.insert(doc);
}

// Two indices needed in order to trigger plan ranking. Neither index provides the sort order.
t.createIndex({x: 1});
t.createIndex({y: 1});

// We should only buffer 3 docs in memory.
var cursor = t.find({x: 1, y: 1}).sort({z: -1}).limit(3);
assert.eq(nDocs - 1, cursor.next().z);
assert.eq(nDocs - 2, cursor.next().z);
assert.eq(nDocs - 3, cursor.next().z);
assert(!cursor.hasNext());
}());