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
|
/**
* Test that starting transactions and running commitTransaction and abortTransaction commands are
* not allowed on replica set secondaries.
*
* @tags: [uses_transactions]
*/
(function() {
"use strict";
const dbName = "test";
const collName = "transactions_on_secondaries_not_allowed";
const rst = new ReplSetTest({name: collName, nodes: 2});
rst.startSet({verbose: 3});
// We want a stable topology, so make the secondary unelectable.
let config = rst.getReplSetConfig();
config.members[1].priority = 0;
rst.initiate(config);
const primary = rst.getPrimary();
const secondary = rst.getSecondary();
const secondaryTestDB = secondary.getDB(dbName);
// Do an initial write so we have something to find.
const initialDoc = {
_id: 0
};
assert.commandWorked(primary.getDB(dbName)[collName].insert(initialDoc));
rst.awaitLastOpCommitted();
// Disable the best-effort check for primary-ness in the service entry point, so that we
// exercise the real check for primary-ness in TransactionParticipant::beginOrContinue.
assert.commandWorked(secondary.adminCommand(
{configureFailPoint: "skipCheckingForNotPrimaryInCommandDispatch", mode: "alwaysOn"}));
// Initiate a session on the secondary.
const sessionOptions = {
causalConsistency: false,
retryWrites: true
};
const session = secondaryTestDB.getMongo().startSession(sessionOptions);
const sessionDb = session.getDatabase(dbName);
/**
* Test starting a transaction and issuing a commitTransaction command.
*/
jsTestLog("Start a read-only transaction on the secondary.");
session.startTransaction({readConcern: {level: "snapshot"}});
// Try to read a document (the first statement in the transaction) and verify that this fails.
assert.commandFailedWithCode(sessionDb.runCommand({find: collName}), ErrorCodes.NotWritablePrimary);
// The check for "NotWritablePrimary" supercedes the check for "NoSuchTransaction" in this case.
jsTestLog("Make sure we are not allowed to run the commitTransaction command on the secondary.");
assert.commandFailedWithCode(session.commitTransaction_forTesting(), ErrorCodes.NotWritablePrimary);
/**
* Test starting a transaction and issuing an abortTransaction command.
*/
jsTestLog("Start a different read-only transaction on the secondary.");
session.startTransaction({readConcern: {level: "snapshot"}});
// Try to read a document (the first statement in the transaction) and verify that this fails.
assert.commandFailedWithCode(sessionDb.runCommand({find: collName}), ErrorCodes.NotWritablePrimary);
// The check for "NotWritablePrimary" supercedes the check for "NoSuchTransaction" in this case.
jsTestLog("Make sure we are not allowed to run the abortTransaction command on the secondary.");
assert.commandFailedWithCode(session.abortTransaction_forTesting(), ErrorCodes.NotWritablePrimary);
/**
* Test starting a retryable write.
*/
jsTestLog("Start a retryable write");
assert.commandFailedWithCode(sessionDb.foo.insert({_id: 0}), ErrorCodes.NotWritablePrimary);
/**
* Test starting a read with txnNumber, but without autocommit. This fails in general because
* txnNumber isn't supported for the find command outside of transactions, but we check that
* this fails on a secondary.
*/
jsTestLog("Start a read with txnNumber but without autocommit");
assert.commandFailedWithCode(sessionDb.runCommand({find: 'foo', txnNumber: NumberLong(10)}), 50768);
session.endSession();
rst.stopSet(undefined, false, {skipValidation: true});
}());
|