summaryrefslogtreecommitdiff
path: root/jstests/sharding/change_streams_delete_in_txn_produces_correct_doc_key.js
blob: 634b8b39d1e63f0b40cafa4a78eb6ca64fb2b407 (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
// Tests that 'delete' events within a multi-document transaction do not include the full document
// but only the shard key and _id in the 'documentKey' field. Exercises the fix for SERVER-45987.
// @tags: [
//   multiversion_incompatible,
//   uses_transactions,
// ]

(function() {
"use strict";

load("jstests/libs/collection_drop_recreate.js");  // For assert[Drop|Create]Collection.
load("jstests/libs/change_stream_util.js");        // For ChangeStreamTest.

// Create a new sharded cluster. For this test, in addition to testing 'delete' events within a
// multi-document transaction, we only test the 'documentKey' field contains the shard key in a
// sharded cluster, so we only need a single shard.
const s = new ShardingTest({shards: 1});
const dbName = "test";
const collName = "change_streams_delete_in_txn_produces_correct_doc_key";
const ns = dbName + "." + collName;
const db = s.getDB(dbName);

/**
 * Test function which is used to test three types of delete-related commands, deleteOne(),
 * deleteMany() and findAndModify(). Ensure only documentKey instead of a full document will be
 * logged on entries for deletes in multi-document transactions, and also ensure that we can resume
 * the change stream from these delete events.
 */
function testDeleteInMultiDocTxn({collName, deleteCommand, expectedChanges}) {
    // Initialize the collection.
    const coll = assertDropAndRecreateCollection(db, collName);
    // Enable sharding on DB.
    assert.commandWorked(db.adminCommand({enableSharding: dbName}));
    // Shard the collection using field 'a' as the shard key.
    assert.commandWorked(db.adminCommand({shardCollection: ns, key: {a: 1, _id: 1}}));

    assert.commandWorked(coll.insertMany([
        {_id: 1, a: 0, fullDoc: "It's a full document!"},
        {_id: 2, a: 0},
        {_id: 3, a: 0},
        {_id: 4, a: 1}
    ]));

    // Open a change stream on the test collection.
    const cst = new ChangeStreamTest(db);
    let cursor = cst.startWatchingChanges({pipeline: [{$changeStream: {}}], collection: coll});

    // Start a transaction in which to perform the delete operation(s).
    const session = db.getMongo().startSession();
    const sessionDb = session.getDatabase(db.getName());
    const sessionColl = sessionDb[collName];
    session.startTransaction();

    // Run the given 'deleteCommand' function to perform the delete(s).
    deleteCommand(sessionColl);

    // Commit the transaction so that the events become visible to the change stream.
    assert.commandWorked(session.commitTransaction_forTesting());

    // Verify that the stream returns the expected sequence of changes.
    const changes = cst.assertNextChangesEqual({cursor: cursor, expectedChanges: expectedChanges});

    // Test the change stream can be resumed after a delete event from within the transaction.
    cursor = cst.startWatchingChanges({
        pipeline: [{$changeStream: {resumeAfter: changes[changes.length - 1]._id}}],
        collection: coll
    });
    assert.commandWorked(coll.insert({a: 1, _id: 5}));
    assert.docEq(cst.getOneChange(cursor).documentKey, {a: 1, _id: 5});

    cst.cleanUp();
}

jsTestLog("Testing deleteOne() in a transaction.");
testDeleteInMultiDocTxn({
    collName: collName,
    deleteCommand: function(sessionColl) {
        assert.commandWorked(sessionColl.deleteOne({_id: 1}));
    },
    expectedChanges: [
        {
            documentKey: {a: 0, _id: 1},
            ns: {db: db.getName(), coll: collName},
            operationType: "delete",
        },
    ],
});

jsTestLog("Testing deleteMany() in a transaction.");
testDeleteInMultiDocTxn({
    collName: collName,
    deleteCommand: function(sessionColl) {
        assert.commandWorked(sessionColl.deleteMany({a: 0, _id: {$gt: 0}}));
    },
    expectedChanges: [
        {
            documentKey: {a: 0, _id: 1},
            ns: {db: db.getName(), coll: collName},
            operationType: "delete",
        },
        {
            documentKey: {a: 0, _id: 2},
            ns: {db: db.getName(), coll: collName},
            operationType: "delete",
        },
        {
            documentKey: {a: 0, _id: 3},
            ns: {db: db.getName(), coll: collName},
            operationType: "delete",
        },
    ],
});

jsTestLog("Testing findAndModify() in a transaction.");
testDeleteInMultiDocTxn({
    collName: collName,
    deleteCommand: function(sessionColl) {
        sessionColl.findAndModify({query: {a: 0, _id: 1}, remove: true});
    },
    expectedChanges: [
        {
            documentKey: {a: 0, _id: 1},
            ns: {db: db.getName(), coll: collName},
            operationType: "delete",
        },
    ],
});

s.stop();
}());