Skip to content

Commit 927883b

Browse files
committed
SERVER-42114 Allow single RS transactions to run on the config database in sharded clusters
1 parent 42ad6f9 commit 927883b

13 files changed

+139
-45
lines changed

buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_txns_passthrough.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,6 @@ selector:
2828
- jstests/core/txns/abort_transaction_thread_does_not_block_on_locks.js
2929
- jstests/core/txns/kill_op_on_txn_expiry.js
3030

31-
# Writes to the local database are not allowed through mongos.
32-
# TODO SERVER-28756: Mongos CSRS write retry logic drops txnNumbers.
33-
- jstests/core/txns/banned_txn_dbs.js
34-
3531
# Uses hangAfterCollectionInserts failpoint not available on mongos.
3632
- jstests/core/txns/speculative_snapshot_includes_all_writes.js
3733

@@ -40,6 +36,7 @@ selector:
4036
- jstests/core/txns/non_transactional_operations_on_session_with_transaction.js
4137

4238
exclude_with_any_tags:
39+
- assumes_against_mongod_not_mongos
4340
- does_not_support_causal_consistency
4441
# Transactions are not allowed to operate on capped collections.
4542
- requires_capped

buildscripts/resmokeconfig/suites/sharded_collections_causally_consistent_jscore_txns_passthrough.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ selector:
2424
- jstests/core/txns/abort_transaction_thread_does_not_block_on_locks.js
2525
- jstests/core/txns/kill_op_on_txn_expiry.js
2626

27-
# Writes to the local database are not allowed through mongos.
28-
# TODO SERVER-28756: Mongos CSRS write retry logic drops txnNumbers.
29-
- jstests/core/txns/banned_txn_dbs.js
30-
3127
# Uses hangAfterCollectionInserts failpoint not available on mongos.
3228
- jstests/core/txns/speculative_snapshot_includes_all_writes.js
3329

buildscripts/resmokeconfig/suites/sharded_jscore_txns.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,11 @@ selector:
2828
- jstests/core/txns/abort_transaction_thread_does_not_block_on_locks.js
2929
- jstests/core/txns/kill_op_on_txn_expiry.js
3030

31-
# Writes to the local database are not allowed through mongos.
32-
# TODO SERVER-28756: Mongos CSRS write retry logic drops txnNumbers.
33-
- jstests/core/txns/banned_txn_dbs.js
34-
3531
# Uses hangAfterCollectionInserts failpoint not available on mongos.
3632
- jstests/core/txns/speculative_snapshot_includes_all_writes.js
3733

3834
exclude_with_any_tags:
35+
- assumes_against_mongod_not_mongos
3936
# Transactions are not allowed to operate on capped collections.
4037
- requires_capped
4138
# Prepare is not a command on mongos.

buildscripts/resmokeconfig/suites/sharded_jscore_txns_sharded_collections.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ selector:
2424
- jstests/core/txns/abort_transaction_thread_does_not_block_on_locks.js
2525
- jstests/core/txns/kill_op_on_txn_expiry.js
2626

27-
# Writes to the local database are not allowed through mongos.
28-
# TODO SERVER-28756: Mongos CSRS write retry logic drops txnNumbers.
29-
- jstests/core/txns/banned_txn_dbs.js
30-
3127
# Uses hangAfterCollectionInserts failpoint not available on mongos.
3228
- jstests/core/txns/speculative_snapshot_includes_all_writes.js
3329

buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ selector:
149149
- jstests/sharding/database_and_shard_versioning_all_commands.js
150150
- jstests/sharding/shard1.js
151151
- jstests/sharding/track_unsharded_collections_create_collection.js
152+
- jstests/sharding/banned_txn_databases_sharded.js
152153
# Enable if SERVER-41813 is backported or 4.4 becomes last-stable
153154
- jstests/sharding/invalid_system_views_sharded_collection.js
154155

jstests/core/txns/banned_txn_dbs.js renamed to jstests/core/txns/banned_txn_dbs_unsharded.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Tests that reads and writes to the config, admin, and local databases are forbidden within
2-
// transactions.
3-
// @tags: [uses_transactions]
2+
// transactions on non-sharded clusters. Behavior on sharded clusters is tested separately.
3+
// @tags: [assumes_against_mongod_not_mongos, assumes_unsharded_collection, uses_transactions]
44
(function() {
55
"use strict";
66

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* Tests that:
3+
* 1. Reads and writes to the admin database are forbidden within single replica set transactions
4+
* on sharded clusters.
5+
* 2. Read and writes to the config database are forbidden from mongos within single replica set
6+
* transactions on sharded clusters.
7+
* 3. Reads and writes to the config.transactions namespace are forbidden within single replica set
8+
* transactions on sharded clusters, BUT read and writes to other namespaces in the config
9+
* database are allowed.
10+
*
11+
* @tags: [requires_find_command]
12+
*/
13+
14+
(function() {
15+
"use strict";
16+
17+
load("jstests/sharding/libs/sharded_transactions_helpers.js");
18+
19+
const st = new ShardingTest({shards: 1});
20+
const mongosSession = st.s.startSession();
21+
const shardSession = st.shard0.getDB("test").getMongo().startSession();
22+
const collName = "banned_txn_dbs";
23+
24+
jsTest.log("Verify that read and write operations within transactions are forbidden for the " +
25+
"admin database within a transaction.");
26+
27+
const adminColl = shardSession.getDatabase("admin")[collName];
28+
29+
shardSession.startTransaction();
30+
let error = assert.throws(() => adminColl.find().itcount());
31+
assert.commandFailedWithCode(error, ErrorCodes.OperationNotSupportedInTransaction);
32+
assert.commandFailedWithCode(shardSession.abortTransaction_forTesting(),
33+
ErrorCodes.NoSuchTransaction);
34+
35+
shardSession.startTransaction();
36+
assert.commandFailedWithCode(adminColl.insert({}), ErrorCodes.OperationNotSupportedInTransaction);
37+
assert.commandFailedWithCode(shardSession.abortTransaction_forTesting(),
38+
ErrorCodes.NoSuchTransaction);
39+
40+
jsTestLog("Verify that read and write operations within transactions are forbidden for the " +
41+
"config database when accessed through mongos.");
42+
43+
const mongosConfigDB = mongosSession.getDatabase("config");
44+
const clusterColls = [
45+
mongosConfigDB["test"],
46+
mongosConfigDB["actionlog"],
47+
mongosConfigDB["transaction_coords"],
48+
mongosConfigDB["transactions"]
49+
];
50+
51+
mongosSession.startTransaction();
52+
clusterColls.forEach((coll) => {
53+
error = assert.throws(() => coll.find().itcount());
54+
assert.commandFailedWithCode(error, ErrorCodes.OperationNotSupportedInTransaction);
55+
});
56+
57+
mongosSession.endSession();
58+
59+
jsTestLog("Verify that read operations within transactions work fine for the config database " +
60+
"when not config.transactions (and directly accessed through the shard).");
61+
62+
const configDB = shardSession.getDatabase("config");
63+
const shardColls = [configDB["test"], configDB["actionlog"], configDB["transaction_coords"]];
64+
65+
shardSession.startTransaction();
66+
shardColls.forEach((coll) => {
67+
coll.find().itcount();
68+
});
69+
70+
jsTestLog("Verify that read operations will not work for the config.transactions namespace.");
71+
72+
const shardCollTransactions = configDB["transactions"];
73+
error = assert.throws(() => shardCollTransactions.find().itcount());
74+
assert.commandFailedWithCode(error, ErrorCodes.OperationNotSupportedInTransaction);
75+
76+
shardSession.endSession();
77+
st.stop();
78+
}());

src/mongo/db/commands.cpp

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -431,26 +431,38 @@ bool CommandHelpers::uassertShouldAttemptParse(OperationContext* opCtx,
431431
}
432432

433433

434-
Status CommandHelpers::canUseTransactions(StringData dbName, StringData cmdName) {
435-
if (cmdName == "count"_sd) {
436-
return {ErrorCodes::OperationNotSupportedInTransaction,
437-
"Cannot run 'count' in a multi-document transaction. Please see "
438-
"http://dochub.mongodb.org/core/transaction-count for a recommended alternative."};
439-
}
440-
441-
if (txnCmdWhitelist.find(cmdName) == txnCmdWhitelist.cend()) {
442-
return {ErrorCodes::OperationNotSupportedInTransaction,
443-
str::stream() << "Cannot run '" << cmdName << "' in a multi-document transaction."};
444-
}
445-
446-
if (dbName == "config"_sd || dbName == "local"_sd ||
447-
(dbName == "admin"_sd && txnAdminCommands.find(cmdName) == txnAdminCommands.cend())) {
448-
return {ErrorCodes::OperationNotSupportedInTransaction,
449-
str::stream() << "Cannot run command against the '" << dbName
450-
<< "' database in a transaction"};
434+
void CommandHelpers::canUseTransactions(const NamespaceString& nss,
435+
StringData cmdName,
436+
bool allowTransactionsOnConfigDatabase) {
437+
438+
uassert(ErrorCodes::OperationNotSupportedInTransaction,
439+
"Cannot run 'count' in a multi-document transaction. Please see "
440+
"http://dochub.mongodb.org/core/transaction-count for a recommended alternative.",
441+
cmdName != "count"_sd);
442+
443+
uassert(ErrorCodes::OperationNotSupportedInTransaction,
444+
str::stream() << "Cannot run '" << cmdName << "' in a multi-document transaction.",
445+
txnCmdWhitelist.find(cmdName) != txnCmdWhitelist.cend());
446+
447+
const auto dbName = nss.db();
448+
449+
uassert(ErrorCodes::OperationNotSupportedInTransaction,
450+
str::stream() << "Cannot run command against the '" << dbName
451+
<< "' database in a transaction.",
452+
dbName != NamespaceString::kLocalDb &&
453+
(dbName != NamespaceString::kAdminDb ||
454+
txnAdminCommands.find(cmdName) != txnAdminCommands.cend()));
455+
456+
if (allowTransactionsOnConfigDatabase) {
457+
uassert(ErrorCodes::OperationNotSupportedInTransaction,
458+
"Cannot run command against the config.transactions namespace in a transaction"
459+
"on a sharded cluster.",
460+
nss != NamespaceString::kSessionTransactionsTableNamespace);
461+
} else {
462+
uassert(ErrorCodes::OperationNotSupportedInTransaction,
463+
"Cannot run command against the config database in a transaction.",
464+
dbName != "config"_sd);
451465
}
452-
453-
return Status::OK();
454466
}
455467

456468
constexpr StringData CommandHelpers::kHelpFieldName;

src/mongo/db/commands.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,12 @@ struct CommandHelpers {
211211
const OpMsgRequest& request);
212212

213213
/**
214-
* Returns OK if command is allowed to run under a transaction in the given database.
214+
* Verifies that command is allowed to run under a transaction in the given database or
215+
* namespace, and throws if that verification doesn't pass.
215216
*/
216-
static Status canUseTransactions(StringData dbName, StringData cmdName);
217+
static void canUseTransactions(const NamespaceString& nss,
218+
StringData cmdName,
219+
bool allowTransactionsOnConfigDatabase);
217220

218221
static constexpr StringData kHelpFieldName = "help"_sd;
219222

src/mongo/db/service_entry_point_common.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,15 @@ void execCommandDatabase(OperationContext* opCtx,
664664
str::stream() << "Invalid database name: '" << dbname << "'",
665665
NamespaceString::validDBName(dbname, NamespaceString::DollarInDbNameBehavior::Allow));
666666

667-
validateSessionOptions(sessionOptions, command->getName(), dbname);
667+
668+
const auto allowTransactionsOnConfigDatabase =
669+
(serverGlobalParams.clusterRole == ClusterRole::ConfigServer ||
670+
serverGlobalParams.clusterRole == ClusterRole::ShardServer);
671+
672+
validateSessionOptions(sessionOptions,
673+
command->getName(),
674+
invocation->ns(),
675+
allowTransactionsOnConfigDatabase);
668676

669677
std::unique_ptr<MaintenanceModeSetter> mmSetter;
670678

0 commit comments

Comments
 (0)