summaryrefslogtreecommitdiff
path: root/jstests/core/txns/disallow_operations_on_prepared_transaction.js
blob: 13d423ab4c19dbc38b83e1dfbef6fa95a014c803 (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
/**
 * Test calling various operations on a prepared transaction. Only commit, abort and prepare should
 * be allowed to be called on a prepared transaction. All other cases should fail with
 * PreparedTransactionInProgress.
 *
 * @tags: [uses_transactions, uses_prepare_transaction]
 */

(function() {
"use strict";
load("jstests/core/txns/libs/prepare_helpers.js");

const dbName = "test";
const collName = "disallow_operations_on_prepared_transaction";
const testDB = db.getSiblingDB(dbName);
const testColl = testDB.getCollection(collName);

testDB.runCommand({drop: collName, writeConcern: {w: "majority"}});
assert.commandWorked(testDB.runCommand({create: collName, writeConcern: {w: "majority"}}));

const session = db.getMongo().startSession({causalConsistency: false});
const sessionDB = session.getDatabase(dbName);
const sessionColl = sessionDB.getCollection(collName);

jsTestLog("Test that you can call prepareTransaction on a prepared transaction.");
session.startTransaction();
assert.commandWorked(sessionColl.insert({_id: 1}));
let firstTimestamp = PrepareHelpers.prepareTransaction(session);
let secondTimestamp = PrepareHelpers.prepareTransaction(session);
assert.eq(firstTimestamp, secondTimestamp);
assert.commandWorked(session.abortTransaction_forTesting());

jsTestLog("Test that you can call commitTransaction on a prepared transaction.");
session.startTransaction();
assert.commandWorked(sessionColl.insert({_id: 2}));
let prepareTimestamp = PrepareHelpers.prepareTransaction(session);
assert.commandWorked(PrepareHelpers.commitTransaction(session, prepareTimestamp));

jsTestLog("Test that you can call abortTransaction on a prepared transaction.");
session.startTransaction();
assert.commandWorked(sessionColl.insert({_id: 3}));
PrepareHelpers.prepareTransaction(session);
assert.commandWorked(session.abortTransaction_forTesting());

session.startTransaction();
assert.commandWorked(sessionColl.insert({_id: 4}));
PrepareHelpers.prepareTransaction(session);

jsTestLog("Test that you can't run an aggregation on a prepared transaction.");
assert.commandFailedWithCode(assert.throws(function() {
                                              sessionColl.aggregate({$match: {}});
                                          }),
                                          ErrorCodes.PreparedTransactionInProgress);

jsTestLog("Test that you can't run delete on a prepared transaction.");
var res = assert.commandFailedWithCode(sessionColl.remove({_id: 4}),
                                       ErrorCodes.PreparedTransactionInProgress);
assert.eq(res.errorLabels, ["TransientTransactionError"]);

jsTestLog("Test that you can't run distinct on a prepared transaction.");
assert.commandFailedWithCode(assert.throws(function() {
                                              sessionColl.distinct("_id");
                                          }),
                                          ErrorCodes.PreparedTransactionInProgress);

// This fails with ConflictingOperationInProgress instead of PreparedTransactionInProgress
// because doTxn is always runs with startTransaction = true.
jsTestLog("Test that you can't run doTxn on a prepared transaction.");
assert.commandFailedWithCode(sessionDB.runCommand({
    doTxn: [{op: "u", ns: testColl.getFullName(), o2: {_id: 0}, o: {$set: {a: 5}}}],
    txnNumber: NumberLong(session.getTxnNumber_forTesting()),
    stmtId: NumberInt(1),
    autocommit: false
}),
                             ErrorCodes.OperationNotSupportedInTransaction);

jsTestLog("Test that you can't run find on a prepared transaction.");
assert.commandFailedWithCode(assert.throws(function() {
                                              sessionColl.find({}).toArray();
                                          }),
                                          ErrorCodes.PreparedTransactionInProgress);

jsTestLog("Test that you can't run findandmodify on a prepared transaction.");
assert.commandFailedWithCode(sessionDB.runCommand({
    findandmodify: collName,
    remove: true,
    txnNumber: NumberLong(session.getTxnNumber_forTesting()),
    stmtId: NumberInt(1),
    autocommit: false
}),
                             ErrorCodes.PreparedTransactionInProgress);

jsTestLog("Test that you can't run findAndModify on a prepared transaction.");
assert.commandFailedWithCode(
    assert.throws(function() {
                     sessionColl.findAndModify({query: {_id: 4}, remove: true});
                 }),
                 ErrorCodes.PreparedTransactionInProgress);

jsTestLog("Test that you can't run geoSearch on a prepared transaction.");
assert.commandFailedWithCode(
    sessionDB.runCommand({geoSearch: collName, near: [0, 0], search: {a: 1}}),
    ErrorCodes.PreparedTransactionInProgress);

jsTestLog("Test that you can't insert on a prepared transaction.");
res = assert.commandFailedWithCode(sessionColl.insert({_id: 5}),
                                   ErrorCodes.PreparedTransactionInProgress);
assert.eq(res.errorLabels, ["TransientTransactionError"]);

jsTestLog("Test that you can't run update on a prepared transaction.");
res = assert.commandFailedWithCode(sessionColl.update({_id: 4}, {a: 1}),
                                   ErrorCodes.PreparedTransactionInProgress);
assert.eq(res.errorLabels, ["TransientTransactionError"]);
assert.commandWorked(session.abortTransaction_forTesting());

jsTestLog("Test that you can't run getMore on a prepared transaction.");
session.startTransaction();
res = assert.commandWorked(sessionDB.runCommand({find: collName, batchSize: 1}));
assert(res.hasOwnProperty("cursor"), tojson(res));
assert(res.cursor.hasOwnProperty("id"), tojson(res));
PrepareHelpers.prepareTransaction(session);
assert.commandFailedWithCode(sessionDB.runCommand({getMore: res.cursor.id, collection: collName}),
                             ErrorCodes.PreparedTransactionInProgress);

jsTestLog("Test that you can't run killCursors on a prepared transaction.");
assert.commandFailedWithCode(
    sessionDB.runCommand({killCursors: collName, cursors: [res.cursor.id]}),
    ErrorCodes.PreparedTransactionInProgress);
assert.commandWorked(session.abortTransaction_forTesting());

session.endSession();
}());