2018-03-16 16:42:15 +01:00
|
|
|
// Tests of invalidate entries for a $changeStream on a whole database.
|
|
|
|
(function() {
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
load("jstests/libs/change_stream_util.js"); // For ChangeStreamTest.
|
|
|
|
load('jstests/replsets/libs/two_phase_drops.js'); // For 'TwoPhaseDropCollectionTest'.
|
|
|
|
load("jstests/libs/collection_drop_recreate.js"); // For assert[Drop|Create]Collection.
|
|
|
|
|
2018-04-02 16:34:28 +02:00
|
|
|
const testDB = db.getSiblingDB(jsTestName());
|
|
|
|
let cst = new ChangeStreamTest(testDB);
|
2018-03-16 16:42:15 +01:00
|
|
|
|
|
|
|
// Write a document to the collection and test that the change stream returns it
|
|
|
|
// and getMore command closes the cursor afterwards.
|
2018-04-02 16:34:28 +02:00
|
|
|
let coll = assertDropAndRecreateCollection(testDB, "change_stream_whole_db_invalidations");
|
2018-03-16 16:42:15 +01:00
|
|
|
|
|
|
|
let aggCursor = cst.startWatchingChanges({pipeline: [{$changeStream: {}}], collection: 1});
|
|
|
|
|
|
|
|
// Create oplog entries of type insert, update, and delete.
|
|
|
|
assert.writeOK(coll.insert({_id: 1}));
|
|
|
|
assert.writeOK(coll.update({_id: 1}, {$set: {a: 1}}));
|
|
|
|
assert.writeOK(coll.remove({_id: 1}));
|
|
|
|
// Drop the collection.
|
2018-04-02 16:34:28 +02:00
|
|
|
assert.commandWorked(testDB.runCommand({drop: coll.getName()}));
|
2018-03-16 16:42:15 +01:00
|
|
|
// We should get 4 oplog entries of type insert, update, delete, and invalidate. The cursor
|
|
|
|
// should be closed.
|
|
|
|
let change = cst.getOneChange(aggCursor);
|
|
|
|
assert.eq(change.operationType, "insert", tojson(change));
|
|
|
|
change = cst.getOneChange(aggCursor);
|
|
|
|
assert.eq(change.operationType, "update", tojson(change));
|
|
|
|
change = cst.getOneChange(aggCursor);
|
|
|
|
assert.eq(change.operationType, "delete", tojson(change));
|
|
|
|
cst.assertNextChangesEqual({
|
|
|
|
cursor: aggCursor,
|
|
|
|
expectedChanges: [{operationType: "invalidate"}],
|
|
|
|
expectInvalidate: true
|
|
|
|
});
|
|
|
|
|
2018-04-02 16:34:28 +02:00
|
|
|
const collAgg =
|
|
|
|
assertDropAndRecreateCollection(testDB, "change_stream_whole_db_agg_invalidations");
|
2018-03-16 16:42:15 +01:00
|
|
|
|
|
|
|
// Get a valid resume token that the next change stream can use.
|
2018-04-03 17:55:52 +02:00
|
|
|
aggCursor = cst.startWatchingChanges({pipeline: [{$changeStream: {}}], collection: 1});
|
2018-03-16 16:42:15 +01:00
|
|
|
|
|
|
|
assert.writeOK(collAgg.insert({_id: 1}, {writeConcern: {w: "majority"}}));
|
|
|
|
|
|
|
|
change = cst.getOneChange(aggCursor, false);
|
|
|
|
const resumeToken = change._id;
|
|
|
|
|
|
|
|
// It should not possible to resume a change stream after a collection drop, even if the
|
|
|
|
// invalidate has not been received.
|
2018-04-02 16:34:28 +02:00
|
|
|
assertDropCollection(testDB, collAgg.getName());
|
2018-03-16 16:42:15 +01:00
|
|
|
// Wait for two-phase drop to complete, so that the UUID no longer exists.
|
|
|
|
assert.soon(function() {
|
2018-04-02 16:34:28 +02:00
|
|
|
return !TwoPhaseDropCollectionTest.collectionIsPendingDropInDatabase(testDB,
|
|
|
|
collAgg.getName());
|
2018-03-16 16:42:15 +01:00
|
|
|
});
|
2018-04-03 17:55:52 +02:00
|
|
|
assert.commandFailedWithCode(
|
|
|
|
testDB.runCommand(
|
|
|
|
{aggregate: 1, pipeline: [{$changeStream: {resumeAfter: resumeToken}}], cursor: {}}),
|
|
|
|
40615);
|
2018-03-16 16:42:15 +01:00
|
|
|
|
|
|
|
// Test that invalidation entries for other databases are filtered out.
|
2018-04-02 16:34:28 +02:00
|
|
|
const otherDB = testDB.getSiblingDB("change_stream_whole_db_invalidations_other");
|
2018-03-16 16:42:15 +01:00
|
|
|
const otherDBColl = otherDB["change_stream_whole_db_invalidations_other"];
|
|
|
|
assert.writeOK(otherDBColl.insert({_id: 0}));
|
|
|
|
|
|
|
|
// Create collection on the database being watched.
|
2018-04-02 16:34:28 +02:00
|
|
|
coll = assertDropAndRecreateCollection(testDB, "change_stream_whole_db_invalidations");
|
2018-03-16 16:42:15 +01:00
|
|
|
|
|
|
|
aggCursor = cst.startWatchingChanges({pipeline: [{$changeStream: {}}], collection: 1});
|
|
|
|
|
|
|
|
// Drop the collection on the other database, this should *not* invalidate the change stream.
|
|
|
|
assertDropCollection(otherDB, otherDBColl.getName());
|
|
|
|
|
|
|
|
// Insert into the collection in the watched database, and verify the change stream is able to
|
|
|
|
// pick it up.
|
|
|
|
assert.writeOK(coll.insert({_id: 1}));
|
|
|
|
change = cst.getOneChange(aggCursor);
|
|
|
|
assert.eq(change.operationType, "insert", tojson(change));
|
|
|
|
assert.eq(change.documentKey._id, 1);
|
|
|
|
|
|
|
|
// Dropping a collection should invalidate the change stream.
|
2018-04-02 16:34:28 +02:00
|
|
|
assertDropCollection(testDB, coll.getName());
|
2018-03-16 16:42:15 +01:00
|
|
|
cst.assertNextChangesEqual({
|
|
|
|
cursor: aggCursor,
|
|
|
|
expectedChanges: [{operationType: "invalidate"}],
|
|
|
|
expectInvalidate: true
|
|
|
|
});
|
|
|
|
|
|
|
|
// Renaming a collection should invalidate the change stream.
|
2018-04-02 16:34:28 +02:00
|
|
|
assertCreateCollection(testDB, coll.getName());
|
|
|
|
assertDropCollection(testDB, "renamed_coll");
|
2018-03-16 16:42:15 +01:00
|
|
|
aggCursor = cst.startWatchingChanges({pipeline: [{$changeStream: {}}], collection: 1});
|
|
|
|
assert.writeOK(coll.renameCollection("renamed_coll"));
|
|
|
|
cst.assertNextChangesEqual({
|
|
|
|
cursor: aggCursor,
|
|
|
|
expectedChanges: [{operationType: "invalidate"}],
|
|
|
|
expectInvalidate: true
|
|
|
|
});
|
|
|
|
|
2018-04-02 16:34:28 +02:00
|
|
|
// Dropping a 'system' collection should invalidate the change stream.
|
|
|
|
// Create a view to ensure that the 'system.views' collection exists.
|
|
|
|
assert.commandWorked(
|
|
|
|
testDB.runCommand({create: "view1", viewOn: coll.getName(), pipeline: []}));
|
2018-03-16 16:42:15 +01:00
|
|
|
aggCursor = cst.startWatchingChanges({pipeline: [{$changeStream: {}}], collection: 1});
|
2018-04-02 16:34:28 +02:00
|
|
|
assertDropCollection(testDB, "system.views");
|
|
|
|
cst.assertNextChangesEqual({
|
|
|
|
cursor: aggCursor,
|
|
|
|
expectedChanges: [{operationType: "invalidate"}],
|
|
|
|
expectInvalidate: true
|
|
|
|
});
|
2018-03-16 16:42:15 +01:00
|
|
|
|
|
|
|
cst.cleanUp();
|
|
|
|
}());
|