summaryrefslogtreecommitdiff
path: root/jstests/replsets/speculative_read_transaction.js
blob: df2a4cdca931bb2e241a7f286fae0e5cbc0d3a4a (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
/**
 * Test that read-only transactions are executed speculatively, and that once they are committed
 * with a majority write concern, the data read is indeed majority-committed.
 *
 * @tags: [uses_transactions, requires_majority_read_concern]
 */
(function() {
"use strict";
load("jstests/libs/write_concern_util.js");  // For stopServerReplication

const dbName = "test";
const collName = "speculative_read_transaction";

const rst = new ReplSetTest({name: collName, nodes: [{}, {rsConfig: {priority: 0}}]});
rst.startSet();
rst.initiate();

const primary = rst.getPrimary();
const secondary = rst.getSecondary();
const testDB = primary.getDB(dbName);
const coll = testDB[collName];

function runTest(sessionOptions) {
    testDB.runCommand({drop: collName, writeConcern: {w: "majority"}});

    // Do an initial write so we have something to update.
    assert.commandWorked(coll.insert([{_id: 0, x: 0}], {w: "majority"}));
    rst.awaitLastOpCommitted();

    // Stop replication on the secondary so the majority commit never moves forward.
    stopServerReplication(secondary);

    // Do a local update in another client.
    // The transaction should see this, due to speculative behavior.
    const otherclient = new Mongo(primary.host);
    assert.commandWorked(otherclient.getDB(dbName)[collName].update({_id: 0}, {x: 1}, {w: 1}));

    // Initiate a session on the primary.
    const session = testDB.getMongo().startSession(sessionOptions);
    const sessionDb = session.getDatabase(dbName);
    const sessionColl = sessionDb.getCollection(collName);

    // Abort does not wait for write concern.
    jsTestLog("Starting majority-abort transaction");
    session.startTransaction({readConcern: {level: "snapshot"}, writeConcern: {w: "majority"}});
    assert.eq(sessionColl.findOne({_id: 0}), {_id: 0, x: 1});
    assert.commandWorked(session.abortTransaction_forTesting());

    // This transaction should complete because it does not use majority write concern.
    jsTestLog("Starting non-majority commit transaction");
    session.startTransaction({readConcern: {level: "snapshot"}, writeConcern: {w: 1}});
    assert.eq(sessionColl.findOne({_id: 0}), {_id: 0, x: 1});
    assert.commandWorked(session.commitTransaction_forTesting());

    // This transaction should not complete because it uses snapshot read concern, majority
    // write concern and the commit point is not advancing.
    jsTestLog("Starting majority-commit snapshot-read transaction");
    session.startTransaction(
        {readConcern: {level: "snapshot"}, writeConcern: {w: "majority", wtimeout: 5000}});
    assert.eq(sessionColl.findOne({_id: 0}), {_id: 0, x: 1});
    assert.commandFailedWithCode(session.commitTransaction_forTesting(),
                                 ErrorCodes.WriteConcernFailed);

    // Allow the majority commit point to advance to allow the failed write concern to clear.
    restartServerReplication(secondary);
    rst.awaitReplication();
    stopServerReplication(secondary);

    // Do another local update from another client
    assert.commandWorked(otherclient.getDB(dbName)[collName].update({_id: 0}, {x: 2}, {w: 1}));

    // This transaction should not complete because it uses local read concern upconverted to
    // snapshot.
    // TODO(SERVER-34881): Once default read concern is speculative majority, local read
    //                     concern should not wait for the majority commit point to advance.
    jsTestLog("Starting majority-commit local-read transaction");
    session.startTransaction(
        {readConcern: {level: "local"}, writeConcern: {w: "majority", wtimeout: 5000}});
    assert.eq(sessionColl.findOne({_id: 0}), {_id: 0, x: 2});
    assert.commandFailedWithCode(session.commitTransaction_forTesting(),
                                 ErrorCodes.WriteConcernFailed);

    // Allow the majority commit point to advance to allow the failed write concern to clear.
    restartServerReplication(secondary);
    rst.awaitReplication();
    stopServerReplication(secondary);

    // Do another local update from another client
    assert.commandWorked(otherclient.getDB(dbName)[collName].update({_id: 0}, {x: 3}, {w: 1}));

    // This transaction should not complete because it uses majority read concern, majority
    // write concern, and the commit point is not advancing.
    jsTestLog("Starting majority-commit majority-read transaction");
    session.startTransaction(
        {readConcern: {level: "majority"}, writeConcern: {w: "majority", wtimeout: 5000}});
    assert.eq(sessionColl.findOne({_id: 0}), {_id: 0, x: 3});
    assert.commandFailedWithCode(session.commitTransaction_forTesting(),
                                 ErrorCodes.WriteConcernFailed);

    // Restart server replication to allow majority commit point to advance.
    restartServerReplication(secondary);

    session.endSession();
}
runTest({causalConsistency: false});
runTest({causalConsistency: true});

rst.stopSet();
}());