summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/rollback_wt_drop.js
blob: a87e54a1c0976f3cfe202ad076d3d728c1eab3b4 (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/**
 * Tests that a collection drop can be rolled back.
 * @tags: [
 *   requires_replication,
 *   requires_wiredtiger,
 * ]
 */
(function() {
'use strict';

load('jstests/replsets/libs/rollback_test.js');

// Returns list of collections in database, including pending drops.
// Assumes all collections fit in first batch of results.
function listCollections(database) {
    return assert
        .commandWorked(database.runCommand({listCollections: 1, includePendingDrops: true}))
        .cursor.firstBatch;
}

// Operations that will be present on both nodes, before the common point.
const collName = 'test.t';
const renameTargetCollName = 'test.x';
const noOpsToRollbackCollName = 'test.k';
let CommonOps = (node) => {
    const coll = node.getCollection(collName);
    const mydb = coll.getDB();
    assert.commandWorked(mydb.createCollection(coll.getName()));
    assert.commandWorked(coll.createIndex({a: 1}));
    assert.commandWorked(coll.insert({_id: 0, a: 0}));

    // Replicate a drop.
    const replicatedDropCollName = 'w';
    const collToDrop = mydb.getCollection(replicatedDropCollName);
    assert.commandWorked(mydb.createCollection(collToDrop.getName()));
    assert(collToDrop.drop());
    TwoPhaseDropCollectionTest.waitForDropToComplete(mydb, replicatedDropCollName);

    // This collection will be dropped during a rename.
    const renameTargetColl = node.getCollection(renameTargetCollName);
    assert.commandWorked(mydb.createCollection(renameTargetColl.getName()));
    assert.commandWorked(renameTargetColl.createIndex({b: 1}));
    assert.commandWorked(renameTargetColl.insert({_id: 8, b: 8}));
    assert.commandWorked(renameTargetColl.insert({_id: 9, b: 9}));

    // This collection will be dropped without any CRUD ops to rollback.
    const noOpsToRollbackColl = node.getCollection(noOpsToRollbackCollName);
    assert.commandWorked(mydb.createCollection(noOpsToRollbackColl.getName()));
    assert.commandWorked(noOpsToRollbackColl.createIndex({c: 1}));
    assert.commandWorked(noOpsToRollbackColl.insert({_id: 20, c: 20}));
    assert.commandWorked(noOpsToRollbackColl.insert({_id: 21, c: 21}));
};

// Operations that will be performed on the rollback node past the common point.
let RollbackOps = (node) => {
    const coll = node.getCollection(collName);

    // Rollback algorithm may refer to dropped collection if it has to undo an insert.
    assert.commandWorked(coll.insert({_id: 1, a: 1}));

    const mydb = coll.getDB();
    const collectionsBeforeDrop = listCollections(mydb);
    assert(coll.drop());
    const collectionsAfterDrop = listCollections(mydb);
    const supportsPendingDrops = mydb.serverStatus().storageEngine.supportsPendingDrops;
    jsTestLog('supportsPendingDrops = ' + supportsPendingDrops);
    if (!supportsPendingDrops) {
        assert.eq(collectionsAfterDrop.length,
                  collectionsBeforeDrop.length,
                  'listCollections did not report the same number of collections in database ' +
                      mydb.getName() + ' after dropping collection ' + coll.getFullName() +
                      '. Before: ' + tojson(collectionsBeforeDrop) +
                      '. After: ' + tojson(collectionsAfterDrop));
    } else {
        assert.lt(collectionsAfterDrop.length,
                  collectionsBeforeDrop.length,
                  'listCollections did not report fewer collections in database ' + mydb.getName() +
                      ' after dropping collection ' + coll.getFullName() + '. Before: ' +
                      tojson(collectionsBeforeDrop) + '. After: ' + tojson(collectionsAfterDrop));
        assert.gt(mydb.serverStatus().storageEngine.dropPendingIdents,
                  0,
                  'There is no drop pending ident in the storage engine.');
    }

    const renameTargetColl = node.getCollection(renameTargetCollName);
    assert.commandWorked(renameTargetColl.insert({_id: 10, b: 10}));
    assert.commandWorked(renameTargetColl.insert({_id: 11, b: 11}));
    const renameSourceColl = mydb.getCollection('z');
    assert.commandWorked(mydb.createCollection(renameSourceColl.getName()));
    assert.commandWorked(renameSourceColl.renameCollection(renameTargetColl.getName(), true));

    const noOpsToRollbackColl = node.getCollection(noOpsToRollbackCollName);
    assert(noOpsToRollbackColl.drop());

    // This collection will not exist after rollback.
    const tempColl = node.getCollection('test.a');
    assert.commandWorked(mydb.createCollection(tempColl.getName()));
    assert.commandWorked(tempColl.insert({_id: 100, y: 100}));
    assert(tempColl.drop());
};

// Set up Rollback Test.
const rollbackTest = new RollbackTest();
CommonOps(rollbackTest.getPrimary());

const rollbackNode = rollbackTest.transitionToRollbackOperations();
RollbackOps(rollbackNode);

{
    // Check collection drop oplog entry.
    const replTest = rollbackTest.getTestFixture();
    const ops = replTest.dumpOplog(rollbackNode, {ns: 'test.$cmd', 'o.drop': 't'});
    assert.eq(1, ops.length);
    const op = ops[0];
    assert(op.hasOwnProperty('o2'), 'expected o2 field in drop oplog entry: ' + tojson(op));
    assert(op.o2.hasOwnProperty('numRecords'), 'expected count in drop oplog entry: ' + tojson(op));
    assert.eq(2, op.o2.numRecords, 'incorrect count in drop oplog entry: ' + tojson(op));
}

// Check collection rename oplog entry.
{
    const replTest = rollbackTest.getTestFixture();
    const ops = replTest.dumpOplog(
        rollbackNode, {ns: 'test.$cmd', 'o.renameCollection': 'test.z', 'o.to': 'test.x'});
    assert.eq(1, ops.length);
    const op = ops[0];
    assert(op.hasOwnProperty('o2'), 'expected o2 field in rename oplog entry: ' + tojson(op));
    assert(op.o2.hasOwnProperty('numRecords'),
           'expected count in rename oplog entry: ' + tojson(op));
    assert.eq(4, op.o2.numRecords, 'incorrect count in rename oplog entry: ' + tojson(op));
}

// Wait for rollback to finish.
rollbackTest.transitionToSyncSourceOperationsBeforeRollback();
rollbackTest.transitionToSyncSourceOperationsDuringRollback();
rollbackTest.transitionToSteadyStateOperations();

// Check collection count.
const primary = rollbackTest.getPrimary();
const coll = primary.getCollection(collName);
assert.eq(1, coll.find().itcount());
assert.eq(1, coll.count());
const renameTargetColl = primary.getCollection(renameTargetCollName);
assert.eq(2, renameTargetColl.find().itcount());
assert.eq(2, renameTargetColl.count());
const noOpsToRollbackColl = primary.getCollection(noOpsToRollbackCollName);
assert.eq(2, noOpsToRollbackColl.find().itcount());
assert.eq(2, noOpsToRollbackColl.count());

rollbackTest.stop();
})();