236 lines
6.9 KiB
JavaScript
236 lines
6.9 KiB
JavaScript
// Cannot implicitly shard accessed collections because of following errmsg: A single
|
|
// update/delete on a sharded collection must contain an exact match on _id or contain the shard
|
|
// key.
|
|
// @tags: [assumes_unsharded_collection, requires_getmore, requires_non_retryable_writes]
|
|
|
|
// Tests for invalidation during a getmore. This behavior is storage-engine dependent.
|
|
// See SERVER-16675.
|
|
(function() {
|
|
"use strict";
|
|
|
|
var t = db.getmore_invalidated_documents;
|
|
|
|
var count;
|
|
var cursor;
|
|
var nextDoc;
|
|
var x;
|
|
var y;
|
|
|
|
// Case #1: Text search with deletion invalidation.
|
|
t.drop();
|
|
assert.commandWorked(t.ensureIndex({a: "text"}));
|
|
assert.commandWorked(t.insert({_id: 1, a: "bar"}));
|
|
assert.commandWorked(t.insert({_id: 2, a: "bar"}));
|
|
assert.commandWorked(t.insert({_id: 3, a: "bar"}));
|
|
|
|
cursor = t.find({$text: {$search: "bar"}}).batchSize(2);
|
|
cursor.next();
|
|
cursor.next();
|
|
|
|
assert.commandWorked(t.remove({_id: 3}));
|
|
|
|
// We should get back the document or not (depending on the storage engine / concurrency model).
|
|
// Either is fine as long as we don't crash.
|
|
count = cursor.itcount();
|
|
assert(count === 0 || count === 1);
|
|
|
|
// Case #2: Text search with mutation invalidation.
|
|
t.drop();
|
|
assert.commandWorked(t.ensureIndex({a: "text"}));
|
|
assert.commandWorked(t.insert({_id: 1, a: "bar"}));
|
|
assert.commandWorked(t.insert({_id: 2, a: "bar"}));
|
|
assert.commandWorked(t.insert({_id: 3, a: "bar"}));
|
|
|
|
cursor = t.find({$text: {$search: "bar"}}).batchSize(2);
|
|
cursor.next();
|
|
cursor.next();
|
|
|
|
// Update the next matching doc so that it no longer matches.
|
|
assert.commandWorked(t.update({_id: 3}, {$set: {a: "nomatch"}}));
|
|
|
|
// Either the cursor should skip the result that no longer matches, or we should get back the
|
|
// old
|
|
// version of the doc.
|
|
assert(!cursor.hasNext() || cursor.next()["a"] === "bar");
|
|
|
|
// Case #3: Merge sort with deletion invalidation.
|
|
t.drop();
|
|
assert.commandWorked(t.ensureIndex({a: 1, b: 1}));
|
|
assert.commandWorked(t.insert({a: 1, b: 1}));
|
|
assert.commandWorked(t.insert({a: 1, b: 2}));
|
|
assert.commandWorked(t.insert({a: 2, b: 3}));
|
|
assert.commandWorked(t.insert({a: 2, b: 4}));
|
|
|
|
cursor = t.find({a: {$in: [1, 2]}}).sort({b: 1}).batchSize(2);
|
|
cursor.next();
|
|
cursor.next();
|
|
|
|
assert.commandWorked(t.remove({a: 2, b: 3}));
|
|
|
|
count = cursor.itcount();
|
|
assert(count === 1 || count === 2);
|
|
|
|
// Case #4: Merge sort with mutation invalidation.
|
|
t.drop();
|
|
assert.commandWorked(t.ensureIndex({a: 1, b: 1}));
|
|
assert.commandWorked(t.insert({a: 1, b: 1}));
|
|
assert.commandWorked(t.insert({a: 1, b: 2}));
|
|
assert.commandWorked(t.insert({a: 2, b: 3}));
|
|
assert.commandWorked(t.insert({a: 2, b: 4}));
|
|
|
|
cursor = t.find({a: {$in: [1, 2]}}).sort({b: 1}).batchSize(2);
|
|
cursor.next();
|
|
cursor.next();
|
|
|
|
assert.commandWorked(t.update({a: 2, b: 3}, {$set: {a: 6}}));
|
|
|
|
// Either the cursor should skip the result that no longer matches, or we should get back the
|
|
// old
|
|
// version of the doc.
|
|
assert(cursor.hasNext());
|
|
assert(cursor.next()["a"] === 2);
|
|
if (cursor.hasNext()) {
|
|
assert(cursor.next()["a"] === 2);
|
|
}
|
|
assert(!cursor.hasNext());
|
|
|
|
// Case #5: 2d near with deletion invalidation.
|
|
t.drop();
|
|
t.ensureIndex({geo: "2d"});
|
|
for (x = -1; x < 1; x++) {
|
|
for (y = -1; y < 1; y++) {
|
|
assert.commandWorked(t.insert({geo: [x, y]}));
|
|
}
|
|
}
|
|
|
|
cursor = t.find({geo: {$near: [0, 0], $maxDistance: 5}}).batchSize(2);
|
|
cursor.next();
|
|
cursor.next();
|
|
|
|
// Drop all documents in the collection.
|
|
assert.commandWorked(t.remove({}));
|
|
|
|
// Both MMAP v1 and doc-locking storage engines should force fetch the doc (it will be buffered
|
|
// because it is the same distance from the center point as a doc already returned).
|
|
assert(cursor.hasNext());
|
|
|
|
// Case #6: 2dsphere near with deletion invalidation.
|
|
t.drop();
|
|
t.ensureIndex({geo: "2dsphere"});
|
|
for (x = -1; x < 1; x++) {
|
|
for (y = -1; y < 1; y++) {
|
|
assert.commandWorked(t.insert({geo: [x, y]}));
|
|
}
|
|
}
|
|
|
|
cursor = t.find({geo: {$nearSphere: [0, 0], $maxDistance: 5}}).batchSize(2);
|
|
cursor.next();
|
|
cursor.next();
|
|
|
|
// Drop all documents in the collection.
|
|
assert.commandWorked(t.remove({}));
|
|
|
|
// Both MMAP v1 and doc-locking storage engines should force fetch the doc (it will be buffered
|
|
// because it is the same distance from the center point as a doc already returned).
|
|
assert(cursor.hasNext());
|
|
|
|
// Case #7: 2dsphere near with deletion invalidation (again).
|
|
t.drop();
|
|
t.ensureIndex({geo: "2dsphere"});
|
|
for (x = 0; x < 6; x++) {
|
|
assert.commandWorked(t.insert({geo: [x, x]}));
|
|
}
|
|
|
|
cursor = t.find({geo: {$nearSphere: [0, 0], $maxDistance: 10}}).batchSize(2);
|
|
cursor.next();
|
|
cursor.next();
|
|
|
|
// Drop all documents in the collection.
|
|
assert.commandWorked(t.remove({}));
|
|
|
|
// We might force-fetch or we might skip over the deleted documents, depending on the internals
|
|
// of the geo near search. Just make sure that we can exhaust the cursor without crashing.
|
|
assert.gte(cursor.itcount(), 0);
|
|
|
|
// Case #8: 2d near with mutation invalidation.
|
|
t.drop();
|
|
t.ensureIndex({geo: "2d"});
|
|
for (x = -1; x < 1; x++) {
|
|
for (y = -1; y < 1; y++) {
|
|
assert.commandWorked(t.insert({geo: [x, y]}));
|
|
}
|
|
}
|
|
|
|
cursor = t.find({geo: {$near: [0, 0], $maxDistance: 5}}).batchSize(2);
|
|
cursor.next();
|
|
cursor.next();
|
|
|
|
// Update all documents in the collection to have position [15, 15].
|
|
assert.commandWorked(t.update({}, {$set: {geo: [15, 15]}}, false, true));
|
|
|
|
// The old version of the document should be returned (the update should not be reflected in the
|
|
// results of the near search).
|
|
nextDoc = cursor.next();
|
|
printjson(nextDoc);
|
|
assert.neq([15, 15], nextDoc.geo);
|
|
assert(nextDoc.geo[0] === 0 || nextDoc.geo[1] === 0);
|
|
|
|
// Case #9: 2dsphere near with mutation invalidation.
|
|
t.drop();
|
|
t.ensureIndex({geo: "2dsphere"});
|
|
for (x = -1; x < 1; x++) {
|
|
for (y = -1; y < 1; y++) {
|
|
assert.commandWorked(t.insert({geo: [x, y]}));
|
|
}
|
|
}
|
|
|
|
cursor = t.find({geo: {$nearSphere: [0, 0], $maxDistance: 5}}).batchSize(2);
|
|
cursor.next();
|
|
cursor.next();
|
|
|
|
// Update all documents in the collection to have position [15, 15].
|
|
assert.commandWorked(t.update({}, {$set: {geo: [15, 15]}}, false, true));
|
|
|
|
// The old version of the document should be returned (the update should not be reflected in the
|
|
// results of the near search).
|
|
nextDoc = cursor.next();
|
|
printjson(nextDoc);
|
|
assert.neq([15, 15], nextDoc.geo);
|
|
assert(nextDoc.geo[0] === 0 || nextDoc.geo[1] === 0);
|
|
|
|
// Case #10: sort with deletion invalidation.
|
|
t.drop();
|
|
t.ensureIndex({a: 1});
|
|
t.insert({a: 1, b: 2});
|
|
t.insert({a: 3, b: 3});
|
|
t.insert({a: 2, b: 1});
|
|
|
|
cursor = t.find({a: {$in: [1, 2, 3]}}).sort({b: 1}).batchSize(2);
|
|
cursor.next();
|
|
cursor.next();
|
|
|
|
assert.commandWorked(t.remove({a: 2}));
|
|
|
|
if (cursor.hasNext()) {
|
|
assert.eq(cursor.next().b, 3);
|
|
}
|
|
|
|
// Case #11: sort with mutation invalidation.
|
|
t.drop();
|
|
t.ensureIndex({a: 1});
|
|
t.insert({a: 1, b: 2});
|
|
t.insert({a: 3, b: 3});
|
|
t.insert({a: 2, b: 1});
|
|
|
|
cursor = t.find({a: {$in: [1, 2, 3]}}).sort({b: 1}).batchSize(2);
|
|
cursor.next();
|
|
cursor.next();
|
|
|
|
assert.commandWorked(t.update({a: 2}, {$set: {a: 4}}));
|
|
|
|
count = cursor.itcount();
|
|
if (cursor.hasNext()) {
|
|
assert.eq(cursor.next().b, 3);
|
|
}
|
|
})();
|