summaryrefslogtreecommitdiff
path: root/jstests/replsets/tenant_migration_oplog_view.js
blob: 7738ebee2ef85c6396f1065675c086f19548f253 (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
/**
 * Tests that the oplog view on a tenant migration donor contains the information necessary to
 * reproduce the retryable writes oplog chain.
 *
 * @tags: [
 *   incompatible_with_macos,
 *   incompatible_with_windows_tls,
 *   requires_majority_read_concern,
 *   requires_persistence,
 *   serverless,
 * ]
 */
(function() {
"use strict";

load("jstests/replsets/libs/tenant_migration_test.js");

const kGarbageCollectionDelayMS = 5 * 1000;
const donorRst = new ReplSetTest({
    name: "donorRst",
    nodes: 1,
    nodeOptions: {
        setParameter: {
            // Set the delay before a donor state doc is garbage collected to be short to speed
            // up the test.
            tenantMigrationGarbageCollectionDelayMS: kGarbageCollectionDelayMS,
            ttlMonitorSleepSecs: 1,
        }
    }
});

donorRst.startSet();
donorRst.initiate();

const tenantMigrationTest = new TenantMigrationTest({name: jsTestName(), donorRst});
const dbName = "test";
const collName = "collection";

const donorPrimary = tenantMigrationTest.getDonorPrimary();
const rsConn = new Mongo(donorRst.getURL());
const oplog = donorPrimary.getDB("local")["oplog.rs"];
const migrationOplogView = donorPrimary.getDB("local")["system.tenantMigration.oplogView"];
const session = rsConn.startSession({retryWrites: true});
const collection = session.getDatabase(dbName)[collName];

{
    // Assert an oplog entry representing a retryable write only projects fields defined in the
    // view. In this case, only `prevOpTime` will be projected since the following retryable write
    // does not have a `preImageOpTime` or `postImageOpTime`.
    assert.commandWorked(collection.insert({_id: "retryableWrite1"}));

    const oplogEntry = oplog.find({"o._id": "retryableWrite1"}).next();
    jsTestLog({"oplog entry": oplogEntry, "view": migrationOplogView.exists()});
    assert(oplogEntry.hasOwnProperty("txnNumber"));
    assert(oplogEntry.hasOwnProperty("prevOpTime"));
    assert(oplogEntry.hasOwnProperty("stmtId"));

    // Ensure only the fields we expect are present in the view.
    const viewEntry = migrationOplogView.find({ts: oplogEntry["ts"]}).next();
    jsTestLog({"view entry": viewEntry});
    // The following two fields are filtered out of the view.
    assert(!viewEntry.hasOwnProperty("txnNumber"));
    assert(!viewEntry.hasOwnProperty("stmtId"));
    // The following two fields are not present in the original oplog entry, so they will not be
    // added to the view entry.
    assert(!viewEntry.hasOwnProperty("postImageOpTime"));
    assert(!viewEntry.hasOwnProperty("preImageOpTime"));

    assert(viewEntry.hasOwnProperty("ns"));
    assert(viewEntry.hasOwnProperty("ts"));
    assert(viewEntry.hasOwnProperty("prevOpTime"));
}

{
    // Assert that an oplog entry that belongs to a transaction will project its 'o.applyOps.ns'
    // field. This is used to filter transactions that belong to the tenant.
    const txnSession = rsConn.startSession();
    const txnDb = txnSession.getDatabase(dbName);
    const txnColl = txnDb.getCollection(collName);
    assert.commandWorked(txnColl.insert({_id: 'insertDoc'}));

    txnSession.startTransaction({writeConcern: {w: "majority"}});
    assert.commandWorked(txnColl.insert({_id: 'transaction0'}));
    assert.commandWorked(txnColl.insert({_id: 'transaction1'}));
    assert.commandWorked(txnSession.commitTransaction_forTesting());

    const txnEntryOnDonor =
        donorPrimary.getCollection("config.transactions").find({state: "committed"}).toArray()[0];
    jsTestLog(`Txn entry on donor: ${tojson(txnEntryOnDonor)}`);

    const viewEntry = migrationOplogView.find({ts: txnEntryOnDonor.lastWriteOpTime.ts}).next();
    jsTestLog(`Transaction view entry: ${tojson(viewEntry)}`);

    // The following fields are filtered out of the view.
    assert(!viewEntry.hasOwnProperty("txnNumber"));
    assert(!viewEntry.hasOwnProperty("state"));
    assert(!viewEntry.hasOwnProperty("preImageOpTime"));
    assert(!viewEntry.hasOwnProperty("postImageOpTime"));
    assert(!viewEntry.hasOwnProperty("stmtId"));

    // Verify that the view entry has the following fields.
    assert(viewEntry.hasOwnProperty("ns"));
    assert(viewEntry.hasOwnProperty("ts"));
    assert(viewEntry.hasOwnProperty("applyOpsNs"));

    // Assert that 'applyOpsNs' contains the namespace of the inserts.
    assert.eq(viewEntry.applyOpsNs, `${dbName}.${collName}`);
}

donorRst.stopSet();
tenantMigrationTest.stop();
})();