summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Schultz <william.schultz@mongodb.com>2019-07-02 17:22:53 -0400
committerWilliam Schultz <william.schultz@mongodb.com>2019-08-13 16:58:17 -0400
commita9df903dfdd072f270021f34af4d59de87fb30ea (patch)
tree9e0e82b640fe22dd9ee82cbd90b0b9ccff571a96
parent4605de0e64aa4e57487e620f91e5de52615a1941 (diff)
downloadmongo-a9df903dfdd072f270021f34af4d59de87fb30ea.tar.gz
SERVER-39576 Remove the 'doTxn' command
(cherry picked from commit 285c39607ee33aea0d6aff15cfaca37d0956ab50)
-rw-r--r--buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough_auth.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/logical_session_cache_sharding_100ms_refresh_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/logical_session_cache_sharding_10sec_refresh_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/logical_session_cache_sharding_1sec_refresh_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/logical_session_cache_sharding_default_refresh_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/multi_shard_local_read_write_multi_stmt_txn_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_kill_primary_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_stepdown_primary_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/multi_stmt_txn_jscore_passthrough_with_migration.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/secondary_reads_passthrough.yml4
-rw-r--r--buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_txns_passthrough.yml4
-rw-r--r--buildscripts/resmokeconfig/suites/sharded_collections_causally_consistent_jscore_txns_passthrough.yml4
-rw-r--r--buildscripts/resmokeconfig/suites/sharded_collections_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/sharded_jscore_txns.yml4
-rw-r--r--buildscripts/resmokeconfig/suites/sharded_jscore_txns_sharded_collections.yml4
-rw-r--r--buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/sharding_jscore_op_query_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/sharding_jscore_passthrough.yml1
-rw-r--r--jstests/auth/lib/commands_lib.js429
-rw-r--r--jstests/core/bypass_doc_validation.js15
-rw-r--r--jstests/core/collation.js49
-rw-r--r--jstests/core/json_schema/misc_validation.js20
-rw-r--r--jstests/core/txns/commands_not_allowed_in_txn.js32
-rw-r--r--jstests/core/txns/disallow_operations_on_prepared_transaction.js11
-rw-r--r--jstests/core/txns/do_txn_atomicity.js88
-rw-r--r--jstests/core/txns/do_txn_basic.js339
-rw-r--r--jstests/core/txns/statement_ids_accepted.js2
-rw-r--r--jstests/core/views/views_all_commands.js14
-rw-r--r--jstests/libs/override_methods/read_and_write_concern_helpers.js3
-rw-r--r--src/mongo/db/commands.cpp2
-rw-r--r--src/mongo/db/commands/SConscript1
-rw-r--r--src/mongo/db/commands/do_txn_cmd.cpp172
-rw-r--r--src/mongo/db/commands/oplog_application_checks.h4
-rw-r--r--src/mongo/db/initialize_operation_session_info.cpp16
-rw-r--r--src/mongo/db/repl/SConscript20
-rw-r--r--src/mongo/db/repl/do_txn.cpp313
-rw-r--r--src/mongo/db/repl/do_txn.h57
-rw-r--r--src/mongo/db/repl/do_txn_test.cpp335
-rw-r--r--src/mongo/db/transaction_validation.cpp2
42 files changed, 3 insertions, 1957 deletions
diff --git a/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough.yml
index 370617c2300..746dc36c311 100644
--- a/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough.yml
@@ -30,7 +30,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # doTxn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough_auth.yml b/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough_auth.yml
index ed50202405b..620ad2fe5be 100644
--- a/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough_auth.yml
+++ b/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough_auth.yml
@@ -43,7 +43,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # doTxn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_100ms_refresh_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_100ms_refresh_jscore_passthrough.yml
index 3a76ef4b0b0..72da949cc48 100644
--- a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_100ms_refresh_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_100ms_refresh_jscore_passthrough.yml
@@ -23,7 +23,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # doTxn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_10sec_refresh_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_10sec_refresh_jscore_passthrough.yml
index 36ca87d9587..a806ec5ac57 100644
--- a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_10sec_refresh_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_10sec_refresh_jscore_passthrough.yml
@@ -23,7 +23,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # doTxn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_1sec_refresh_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_1sec_refresh_jscore_passthrough.yml
index e383e9771d3..efb7ae3d0a1 100644
--- a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_1sec_refresh_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_1sec_refresh_jscore_passthrough.yml
@@ -23,7 +23,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # doTxn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_default_refresh_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_default_refresh_jscore_passthrough.yml
index b21d11671b3..95177f30486 100644
--- a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_default_refresh_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_default_refresh_jscore_passthrough.yml
@@ -23,7 +23,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # doTxn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/multi_shard_local_read_write_multi_stmt_txn_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/multi_shard_local_read_write_multi_stmt_txn_jscore_passthrough.yml
index 8b6001fb74e..e056e160377 100644
--- a/buildscripts/resmokeconfig/suites/multi_shard_local_read_write_multi_stmt_txn_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/multi_shard_local_read_write_multi_stmt_txn_jscore_passthrough.yml
@@ -24,7 +24,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # doTxn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_jscore_passthrough.yml
index 693a29f76ba..0e7b56025cb 100644
--- a/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_jscore_passthrough.yml
@@ -24,7 +24,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # doTxn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_kill_primary_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_kill_primary_jscore_passthrough.yml
index d4e3cf62f58..3cfa0050da3 100644
--- a/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_kill_primary_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_kill_primary_jscore_passthrough.yml
@@ -24,7 +24,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # doTxn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_stepdown_primary_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_stepdown_primary_jscore_passthrough.yml
index 68bca7241a3..e8d10941693 100644
--- a/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_stepdown_primary_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/multi_shard_multi_stmt_txn_stepdown_primary_jscore_passthrough.yml
@@ -24,7 +24,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # doTxn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/multi_stmt_txn_jscore_passthrough_with_migration.yml b/buildscripts/resmokeconfig/suites/multi_stmt_txn_jscore_passthrough_with_migration.yml
index f950cc7360c..aa97b5220b7 100644
--- a/buildscripts/resmokeconfig/suites/multi_stmt_txn_jscore_passthrough_with_migration.yml
+++ b/buildscripts/resmokeconfig/suites/multi_stmt_txn_jscore_passthrough_with_migration.yml
@@ -24,7 +24,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # doTxn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/secondary_reads_passthrough.yml b/buildscripts/resmokeconfig/suites/secondary_reads_passthrough.yml
index d7899ef4d7f..10587dffdc6 100644
--- a/buildscripts/resmokeconfig/suites/secondary_reads_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/secondary_reads_passthrough.yml
@@ -54,10 +54,6 @@ selector:
- jstests/core/ord.js
- jstests/core/tailable_cursor_invalidation.js
- jstests/core/tailable_skip_limit.js
- # doTxn uses a different session so the operationTime of the default session
- # will not be advanced by doTxn. Therefore, operations in the default
- # session cannot be guaranteed to get executed after doTxn.
- - jstests/core/bypass_doc_validation.js
- jstests/core/collation.js
# This test runs very slow operations which take a long time to replicate in a 5 node linear
# chain. This can lead to awaitReplication timeouts when doing consistency checks.
diff --git a/buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_passthrough.yml
index 2446c3b4422..369301b0e9e 100644
--- a/buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_passthrough.yml
@@ -26,7 +26,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # doTxn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_txns_passthrough.yml b/buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_txns_passthrough.yml
index d1eb538e77b..0dbfd4f8b9f 100644
--- a/buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_txns_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_txns_passthrough.yml
@@ -23,10 +23,6 @@ selector:
# cluster.
- jstests/core/txns/transactions_block_ddl.js
- # TODO SERVER-36120: No doTxn command on mongos.
- - jstests/core/txns/do_txn_atomicity.js
- - jstests/core/txns/do_txn_basic.js
-
# transactionLifetimeLimitSeconds parameter is not available in mongos.
- jstests/core/txns/abort_expired_transaction.js
- jstests/core/txns/abort_transaction_thread_does_not_block_on_locks.js
diff --git a/buildscripts/resmokeconfig/suites/sharded_collections_causally_consistent_jscore_txns_passthrough.yml b/buildscripts/resmokeconfig/suites/sharded_collections_causally_consistent_jscore_txns_passthrough.yml
index 34d30da7c30..9e6a3d0fe42 100644
--- a/buildscripts/resmokeconfig/suites/sharded_collections_causally_consistent_jscore_txns_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/sharded_collections_causally_consistent_jscore_txns_passthrough.yml
@@ -19,10 +19,6 @@ selector:
# cluster.
- jstests/core/txns/transactions_block_ddl.js
- # TODO SERVER-36120: No doTxn command on mongos.
- - jstests/core/txns/do_txn_atomicity.js
- - jstests/core/txns/do_txn_basic.js
-
# TODO SERVER-36121: Set the transactionLifetimeLimitSeconds parameter, which is not on mongos.
- jstests/core/txns/abort_expired_transaction.js
- jstests/core/txns/abort_transaction_thread_does_not_block_on_locks.js
diff --git a/buildscripts/resmokeconfig/suites/sharded_collections_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/sharded_collections_jscore_passthrough.yml
index e4eb9547279..9a3028493b0 100644
--- a/buildscripts/resmokeconfig/suites/sharded_collections_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/sharded_collections_jscore_passthrough.yml
@@ -24,7 +24,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # doTxn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/sharded_jscore_txns.yml b/buildscripts/resmokeconfig/suites/sharded_jscore_txns.yml
index d1df18601af..5d4482a396b 100644
--- a/buildscripts/resmokeconfig/suites/sharded_jscore_txns.yml
+++ b/buildscripts/resmokeconfig/suites/sharded_jscore_txns.yml
@@ -23,10 +23,6 @@ selector:
# cluster.
- jstests/core/txns/transactions_block_ddl.js
- # TODO SERVER-36120: No doTxn command on mongos.
- - jstests/core/txns/do_txn_atomicity.js
- - jstests/core/txns/do_txn_basic.js
-
# transactionLifetimeLimitSeconds parameter is not available in mongos.
- jstests/core/txns/abort_expired_transaction.js
- jstests/core/txns/abort_transaction_thread_does_not_block_on_locks.js
diff --git a/buildscripts/resmokeconfig/suites/sharded_jscore_txns_sharded_collections.yml b/buildscripts/resmokeconfig/suites/sharded_jscore_txns_sharded_collections.yml
index f1cac78a54a..88f46be33fd 100644
--- a/buildscripts/resmokeconfig/suites/sharded_jscore_txns_sharded_collections.yml
+++ b/buildscripts/resmokeconfig/suites/sharded_jscore_txns_sharded_collections.yml
@@ -19,10 +19,6 @@ selector:
# cluster.
- jstests/core/txns/transactions_block_ddl.js
- # TODO SERVER-36120: No doTxn command on mongos.
- - jstests/core/txns/do_txn_atomicity.js
- - jstests/core/txns/do_txn_basic.js
-
# transactionLifetimeLimitSeconds parameter is not available in mongos.
- jstests/core/txns/abort_expired_transaction.js
- jstests/core/txns/abort_transaction_thread_does_not_block_on_locks.js
diff --git a/buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml
index 40869e6bd0d..228dcdec60d 100644
--- a/buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml
@@ -21,7 +21,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # do_txn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/sharding_jscore_op_query_passthrough.yml b/buildscripts/resmokeconfig/suites/sharding_jscore_op_query_passthrough.yml
index 81960174824..40b2193ec12 100644
--- a/buildscripts/resmokeconfig/suites/sharding_jscore_op_query_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/sharding_jscore_op_query_passthrough.yml
@@ -24,7 +24,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # doTxn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/buildscripts/resmokeconfig/suites/sharding_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/sharding_jscore_passthrough.yml
index f22560496b3..54393249267 100644
--- a/buildscripts/resmokeconfig/suites/sharding_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/sharding_jscore_passthrough.yml
@@ -24,7 +24,6 @@ selector:
- jstests/core/dbhash.js # dbhash.
- jstests/core/dbhash2.js # dbhash.
- jstests/core/diagdata.js # Command not supported in mongos
- - jstests/core/do_txn*.js # do_txn
- jstests/core/dropdb_race.js # syncdelay.
- jstests/core/fsync.js # uses fsync.
- jstests/core/geo_haystack*.js # geoSearch.
diff --git a/jstests/auth/lib/commands_lib.js b/jstests/auth/lib/commands_lib.js
index 71c2fb17b61..dbde9b9f138 100644
--- a/jstests/auth/lib/commands_lib.js
+++ b/jstests/auth/lib/commands_lib.js
@@ -3248,435 +3248,6 @@ var authCommandsLib = {
]
},
{
- testname: "doTxn_precondition",
- command: {
- doTxn: [{
- "op": "i",
- "ns": firstDbName + ".x",
- "o": {"_id": ObjectId("57dc3d7da4fce4358afa85b8"), "data": 5}
- }],
- preCondition: [{ns: firstDbName + ".x", q: {x: 5}, res: []}],
- txnNumber: NumberLong(0),
- lsid: {id: UUID()}
- },
- skipSharded: true,
- skipUnlessReplicaSet: true,
- setup: function(db) {
- db.getSisterDB(firstDbName).x.save({});
- },
- teardown: function(db) {
- db.getSisterDB(firstDbName).x.drop();
- },
- testcases: [
- {
- runOnDb: adminDbName,
- privileges: [
- {resource: {db: firstDbName, collection: "x"}, actions: ["find"]},
- {resource: {db: firstDbName, collection: "x"}, actions: ["insert"]},
- ],
- },
- ]
- },
- {
- testname: "doTxn_insert",
- command: {
- doTxn: [{
- "op": "i",
- "ns": firstDbName + ".x",
- "o": {"_id": ObjectId("57dc3d7da4fce4358afa85b8"), "data": 5}
- }],
- txnNumber: NumberLong(0),
- lsid: {id: UUID()}
- },
- skipSharded: true,
- skipUnlessReplicaSet: true,
- setup: function(db) {
- db.getSisterDB(firstDbName).x.save({});
- },
- teardown: function(db) {
- db.getSisterDB(firstDbName).x.drop();
- },
- testcases: [
- {
- runOnDb: adminDbName,
- roles: roles_write,
- privileges: [
- {resource: {db: firstDbName, collection: "x"}, actions: ["insert"]},
- ],
- },
- ]
- },
- {
- testname: "doTxn_insert_UUID",
- command: function(state) {
- return {
- doTxn: [{
- "op": "i",
- "ns": state.collName,
- "ui": state.uuid,
- "o": {"_id": ObjectId("57dc3d7da4fce4358afa85b8"), "data": 5}
- }],
- txnNumber: NumberLong(0),
- lsid: {id: UUID()}
- };
- },
- skipSharded: true,
- skipUnlessReplicaSet: true,
- setup: function(db) {
- var sibling = db.getSisterDB(firstDbName);
- sibling.runCommand({create: "x"});
-
- return {
- collName: sibling.x.getFullName(),
- uuid: getUUIDFromListCollections(sibling, sibling.x.getName())
- };
- },
- teardown: function(db) {
- db.getSisterDB(firstDbName).x.drop();
- },
- testcases: [
- {
- runOnDb: adminDbName,
- roles: {root: 1, restore: 1, __system: 1},
- privileges: [
- {resource: {db: firstDbName, collection: "x"}, actions: ["insert"]},
- {resource: {cluster: true}, actions: ["useUUID"]}
- ],
- },
- ]
- },
- {
- testname: "doTxn_insert_with_nonexistent_UUID",
- command: function(state) {
- return {
- doTxn: [{
- "op": "i",
- "ns": state.collName,
- // Given a nonexistent UUID. The command should fail.
- "ui": UUID("71f1d1d7-68ca-493e-a7e9-f03c94e2e960"),
- "o": {"_id": ObjectId("57dc3d7da4fce4358afa85b8"), "data": 5}
- }],
- txnNumber: NumberLong(0),
- lsid: {id: UUID()}
- };
- },
- skipSharded: true,
- skipUnlessReplicaSet: true,
- setup: function(db) {
- var sibling = db.getSisterDB(firstDbName);
- sibling.runCommand({create: "x"});
-
- return {
- collName: sibling.x.getFullName(),
- uuid: getUUIDFromListCollections(sibling, sibling.x.getName())
- };
- },
- teardown: function(db) {
- db.getSisterDB(firstDbName).x.drop();
- },
- testcases: [
- {
- // It would be an sanity check failure rather than a auth check
- // failure.
- expectFail: true,
- runOnDb: adminDbName,
- roles: {root: 1, restore: 1, __system: 1},
- privileges: [
- {resource: {db: firstDbName, collection: "x"}, actions: ["insert"]},
- {resource: {cluster: true}, actions: ["useUUID"]}
- ],
- },
- ]
- },
- {
- testname: "doTxn_insert_UUID_failure",
- command: function(state) {
- return {
- doTxn: [{
- "op": "i",
- "ns": state.collName,
- "ui": state.uuid,
- "o": {"_id": ObjectId("57dc3d7da4fce4358afa85b8"), "data": 5}
- }],
- txnNumber: NumberLong(0),
- lsid: {id: UUID()}
- };
- },
- skipSharded: true,
- skipUnlessReplicaSet: true,
- setup: function(db) {
- var sibling = db.getSisterDB(firstDbName);
- sibling.runCommand({create: "x"});
-
- return {
- collName: sibling.x.getFullName(),
- uuid: getUUIDFromListCollections(sibling, sibling.x.getName())
- };
- },
- teardown: function(db) {
- db.getSisterDB(firstDbName).x.drop();
- },
- testcases: [
- {
- expectAuthzFailure: true,
- runOnDb: adminDbName,
- privileges: [
- {resource: {db: firstDbName, collection: "x"}, actions: ["insert"]},
- // Don't have useUUID privilege.
- ],
- },
- ]
- },
- {
- testname: "doTxn_insert_UUID_with_wrong_ns",
- command: function(state) {
- return {
- doTxn: [{
- "op": "i",
- "ns":
- firstDbName + ".y", // Specify wrong name but correct uuid. Should work.
- "ui": state.x_uuid, // The insert should on x
- "o": {"_id": ObjectId("57dc3d7da4fce4358afa85b8"), "data": 5}
- }],
- txnNumber: NumberLong(0),
- lsid: {id: UUID()}
- };
- },
- skipSharded: true,
- skipUnlessReplicaSet: true,
- setup: function(db) {
- db.getSisterDB(firstDbName).x.drop();
- db.getSisterDB(firstDbName).y.drop();
- var sibling = db.getSisterDB(firstDbName);
- sibling.runCommand({create: "x"});
- sibling.runCommand({create: "y"});
- return {x_uuid: getUUIDFromListCollections(sibling, sibling.x.getName())};
- },
- teardown: function(db) {
- db.getSisterDB(firstDbName).x.drop();
- },
- testcases: [
- {
- runOnDb: adminDbName,
- privileges: [
- {
- resource: {db: firstDbName, collection: "x"},
- actions: ["createCollection", "insert"]
- },
- {resource: {db: firstDbName, collection: "y"}, actions: ["createCollection"]},
- {resource: {cluster: true}, actions: ["useUUID", "forceUUID"]}
- ],
- },
- ]
- },
- {
- testname: "doTxn_insert_UUID_with_wrong_ns_failure",
- command: function(state) {
- return {
- doTxn: [{
- "op": "i",
- "ns":
- firstDbName + ".y", // Specify wrong name but correct uuid. Should work.
- "ui": state.x_uuid, // The insert should on x
- "o": {"_id": ObjectId("57dc3d7da4fce4358afa85b8"), "data": 5}
- }],
- txnNumber: NumberLong(0),
- lsid: {id: UUID()}
- };
- },
- skipSharded: true,
- skipUnlessReplicaSet: true,
- setup: function(db) {
- db.getSisterDB(firstDbName).x.drop();
- db.getSisterDB(firstDbName).y.drop();
- var sibling = db.getSisterDB(firstDbName);
- sibling.runCommand({create: "x"});
- sibling.runCommand({create: "y"});
- return {x_uuid: getUUIDFromListCollections(sibling, sibling.x.getName())};
- },
- teardown: function(db) {
- db.getSisterDB(firstDbName).x.drop();
- },
- testcases: [
- {
- expectAuthzFailure: true,
- runOnDb: adminDbName,
- privileges: [
- {resource: {db: firstDbName, collection: "x"}, actions: ["createCollection"]},
- {
- resource: {db: firstDbName, collection: "y"},
- actions: ["createCollection", "insert"]
- },
- {resource: {cluster: true}, actions: ["useUUID", "forceUUID"]}
- ],
- },
- ]
- },
- {
- testname: "doTxn_upsert",
- command: {
- doTxn: [{
- "op": "u",
- "ns": firstDbName + ".x",
- "o2": {"_id": 1},
- "o": {"_id": 1, "data": 8}
- }],
- txnNumber: NumberLong(0),
- lsid: {id: UUID()}
- },
- skipSharded: true,
- skipUnlessReplicaSet: true,
- setup: function(db) {
- db.getSisterDB(firstDbName).x.save({_id: 1, data: 1});
- },
- teardown: function(db) {
- db.getSisterDB(firstDbName).x.drop();
- },
- testcases: [
- {
- runOnDb: adminDbName,
- roles: Object.merge(roles_write, {restore: 0}, true),
- privileges: [
- {resource: {db: firstDbName, collection: "x"}, actions: ["update", "insert"]},
- ],
- },
- ]
- },
- {
- testname: "doTxn_update",
- command: {
- doTxn: [{
- "op": "u",
- "ns": firstDbName + ".x",
- "o2": {"_id": 1},
- "o": {"_id": 1, "data": 8}
- }],
- txnNumber: NumberLong(0),
- lsid: {id: UUID()}
- },
- skipSharded: true,
- skipUnlessReplicaSet: true,
- setup: function(db) {
- db.getSisterDB(firstDbName).x.save({_id: 1, data: 1});
- },
- teardown: function(db) {
- db.getSisterDB(firstDbName).x.drop();
- },
- testcases: [
- {
- runOnDb: adminDbName,
- roles: Object.merge(roles_write, {restore: 0}, true),
- privileges: [
- {resource: {db: firstDbName, collection: "x"}, actions: ["update"]},
- ],
- },
- ]
- },
- {
- testname: "doTxn_update_UUID",
- command: function(state) {
- return {
- doTxn: [{
- "op": "u",
- "ns": state.collName,
- "ui": state.uuid,
- "o2": {"_id": 1},
- "o": {"_id": 1, "data": 8}
- }],
- txnNumber: NumberLong(0),
- lsid: {id: UUID()}
- };
- },
- skipSharded: true,
- skipUnlessReplicaSet: true,
- setup: function(db) {
- var sibling = db.getSisterDB(firstDbName);
- sibling.x.save({_id: 1, data: 1});
-
- return {
- collName: sibling.x.getFullName(),
- uuid: getUUIDFromListCollections(sibling, sibling.x.getName())
- };
- },
- teardown: function(db) {
- db.getSisterDB(firstDbName).x.drop();
- },
- testcases: [
- {
- runOnDb: adminDbName,
- roles: {root: 1, __system: 1},
- privileges: [
- {resource: {db: firstDbName, collection: "x"}, actions: ["update"]},
- {resource: {cluster: true}, actions: ["useUUID"]}
- ],
- },
- ]
- },
- {
- testname: "doTxn_update_UUID_failure",
- command: function(state) {
- return {
- doTxn: [{
- "op": "u",
- "ns": state.collName,
- "ui": state.uuid,
- "o2": {"_id": 1},
- "o": {"_id": 1, "data": 8}
- }],
- txnNumber: NumberLong(0),
- lsid: {id: UUID()}
- };
- },
- skipSharded: true,
- skipUnlessReplicaSet: true,
- setup: function(db) {
- var sibling = db.getSisterDB(firstDbName);
- sibling.x.save({_id: 1, data: 1});
-
- return {
- collName: sibling.x.getFullName(),
- uuid: getUUIDFromListCollections(sibling, sibling.x.getName())
- };
- },
- teardown: function(db) {
- db.getSisterDB(firstDbName).x.drop();
- },
- testcases: [
- {
- expectAuthzFailure: true,
- runOnDb: adminDbName,
- privileges: [
- {resource: {db: firstDbName, collection: "x"}, actions: ["update"]},
- ],
- },
- ]
- },
- {
- testname: "doTxn_delete",
- command: {
- doTxn: [{"op": "d", "ns": firstDbName + ".x", "o": {"_id": 1}}],
- txnNumber: NumberLong(0),
- lsid: {id: UUID()}
- },
- skipSharded: true,
- skipUnlessReplicaSet: true,
- setup: function(db) {
- db.getSisterDB(firstDbName).x.save({_id: 1, data: 1});
- },
- teardown: function(db) {
- db.getSisterDB(firstDbName).x.drop();
- },
- testcases: [
- {
- runOnDb: adminDbName,
- roles: Object.merge(roles_write, {restore: 0}, true),
- privileges: [
- {resource: {db: firstDbName, collection: "x"}, actions: ["remove"]},
- ],
- },
- ]
- },
- {
testname: "drop",
command: {drop: "x"},
setup: function(db) {
diff --git a/jstests/core/bypass_doc_validation.js b/jstests/core/bypass_doc_validation.js
index 854b96031ba..dcf1a0d28dc 100644
--- a/jstests/core/bypass_doc_validation.js
+++ b/jstests/core/bypass_doc_validation.js
@@ -11,7 +11,6 @@
*
* - aggregation with $out
* - applyOps (when not sharded)
- * - doTxn (when not sharded)
* - findAndModify
* - insert
* - mapReduce
@@ -62,20 +61,6 @@ function runBypassDocumentValidationTest(validator) {
assert.eq(1, coll.count({_id: 9}));
}
- // Test doTxn with a simple insert if a replica set, not on mongos and the storage engine
- // is WiredTiger.
- if (FixtureHelpers.isReplSet(db) && !isMongos && isWiredTiger(db)) {
- const session = db.getMongo().startSession();
- const sessionDb = session.getDatabase(myDb.getName());
- const op = [{op: 'i', ns: coll.getFullName(), o: {_id: 10}}];
- assertFailsValidation(sessionDb.runCommand(
- {doTxn: op, bypassDocumentValidation: false, txnNumber: NumberLong("0")}));
- assert.eq(0, coll.count({_id: 10}));
- assert.commandWorked(sessionDb.runCommand(
- {doTxn: op, bypassDocumentValidation: true, txnNumber: NumberLong("1")}));
- assert.eq(1, coll.count({_id: 10}));
- }
-
// Test the aggregation command with a $out stage.
const outputCollName = 'bypass_output_coll';
const outputColl = myDb[outputCollName];
diff --git a/jstests/core/collation.js b/jstests/core/collation.js
index 99623d18b7e..0118ffb3a5a 100644
--- a/jstests/core/collation.js
+++ b/jstests/core/collation.js
@@ -1821,55 +1821,6 @@ if (!isMongos) {
assert.eq(8, coll.findOne({_id: "foo"}).x);
}
-// doTxn
-if (FixtureHelpers.isReplSet(db) && !isMongos && isWiredTiger(db)) {
- const session = db.getMongo().startSession();
- const sessionDb = session.getDatabase(db.getName());
-
- // Use majority write concern to clear the drop-pending that can cause lock conflicts with
- // transactions.
- coll.drop({writeConcern: {w: "majority"}});
-
- assert.commandWorked(
- db.createCollection("collation", {collation: {locale: "en_US", strength: 2}}));
- assert.writeOK(coll.insert({_id: "foo", x: 5, str: "bar"}));
-
- // preCondition.q respects collection default collation.
- assert.commandFailed(sessionDb.runCommand({
- doTxn: [{op: "u", ns: coll.getFullName(), o2: {_id: "foo"}, o: {$set: {x: 6}}}],
- preCondition: [{ns: coll.getFullName(), q: {_id: "not foo"}, res: {str: "bar"}}],
- txnNumber: NumberLong("0")
- }));
- assert.eq(5, coll.findOne({_id: "foo"}).x);
- assert.commandWorked(sessionDb.runCommand({
- doTxn: [{op: "u", ns: coll.getFullName(), o2: {_id: "foo"}, o: {$set: {x: 6}}}],
- preCondition: [{ns: coll.getFullName(), q: {_id: "FOO"}, res: {str: "bar"}}],
- txnNumber: NumberLong("1")
- }));
- assert.eq(6, coll.findOne({_id: "foo"}).x);
-
- // preCondition.res respects collection default collation.
- assert.commandFailed(sessionDb.runCommand({
- doTxn: [{op: "u", ns: coll.getFullName(), o2: {_id: "foo"}, o: {$set: {x: 7}}}],
- preCondition: [{ns: coll.getFullName(), q: {_id: "foo"}, res: {str: "not bar"}}],
- txnNumber: NumberLong("2")
- }));
- assert.eq(6, coll.findOne({_id: "foo"}).x);
- assert.commandWorked(sessionDb.runCommand({
- doTxn: [{op: "u", ns: coll.getFullName(), o2: {_id: "foo"}, o: {$set: {x: 7}}}],
- preCondition: [{ns: coll.getFullName(), q: {_id: "foo"}, res: {str: "BAR"}}],
- txnNumber: NumberLong("3")
- }));
- assert.eq(7, coll.findOne({_id: "foo"}).x);
-
- // <operation>.o2 respects collection default collation.
- assert.commandWorked(sessionDb.runCommand({
- doTxn: [{op: "u", ns: coll.getFullName(), o2: {_id: "FOO"}, o: {$set: {x: 8}}}],
- txnNumber: NumberLong("4")
- }));
- assert.eq(8, coll.findOne({_id: "foo"}).x);
-}
-
// Test that the collection created with the "cloneCollectionAsCapped" command inherits the
// default collation of the corresponding collection. We skip running this command in a sharded
// cluster because it isn't supported by mongos.
diff --git a/jstests/core/json_schema/misc_validation.js b/jstests/core/json_schema/misc_validation.js
index 7ac2fb60fb5..fbd15e7b31a 100644
--- a/jstests/core/json_schema/misc_validation.js
+++ b/jstests/core/json_schema/misc_validation.js
@@ -10,7 +10,6 @@
* - update
* - findAndModify
* - applyOps
- * - doTxn
* - $elemMatch projection
*
* @tags: [
@@ -333,24 +332,5 @@ if (!isMongos) {
// transactions.
coll.drop({writeConcern: {w: "majority"}});
assert.writeOK(coll.insert({_id: 1, a: true}));
-
- if (FixtureHelpers.isReplSet(db) && !isMongos && isWiredTiger(db)) {
- // Test $jsonSchema in the precondition checking for doTxn.
- const session = db.getMongo().startSession();
- const sessionDb = session.getDatabase(testDB.getName());
- res = sessionDb.adminCommand({
- doTxn: [
- {op: "u", ns: coll.getFullName(), o2: {_id: 1}, o: {$set: {a: false}}},
- ],
- preCondition: [{
- ns: coll.getFullName(),
- q: {$jsonSchema: {properties: {a: {type: "boolean"}}}},
- res: {a: true}
- }],
- txnNumber: NumberLong("0")
- });
- assert.commandWorked(res);
- assert.eq(1, res.applied);
- }
}
}());
diff --git a/jstests/core/txns/commands_not_allowed_in_txn.js b/jstests/core/txns/commands_not_allowed_in_txn.js
index 41260615d91..f0f96bed437 100644
--- a/jstests/core/txns/commands_not_allowed_in_txn.js
+++ b/jstests/core/txns/commands_not_allowed_in_txn.js
@@ -131,38 +131,6 @@ if (!isMongos) {
commands.forEach(testCommand);
//
-// Test that doTxn is not allowed at positions after the first in transactions.
-//
-
-// There is no doTxn command on mongos.
-if (!isMongos) {
- assert.commandWorked(sessionDb.runCommand({
- find: collName,
- readConcern: {level: "snapshot"},
- txnNumber: NumberLong(++txnNumber),
- stmtId: NumberInt(0),
- startTransaction: true,
- autocommit: false
- }));
- assert.commandFailedWithCode(sessionDb.runCommand({
- doTxn: [{op: "u", ns: testColl.getFullName(), o2: {_id: 0}, o: {$set: {a: 5}}}],
- txnNumber: NumberLong(txnNumber),
- stmtId: NumberInt(1),
- autocommit: false
- }),
- ErrorCodes.OperationNotSupportedInTransaction);
-
- // It is still possible to commit the transaction. The rejected command does not abort the
- // transaction.
- assert.commandWorked(sessionDb.adminCommand({
- commitTransaction: 1,
- txnNumber: NumberLong(txnNumber),
- stmtId: NumberInt(2),
- autocommit: false
- }));
-}
-
-//
// Test that a find command with the read-once cursor option is not allowed in a transaction.
//
assert.commandFailedWithCode(sessionDb.runCommand({
diff --git a/jstests/core/txns/disallow_operations_on_prepared_transaction.js b/jstests/core/txns/disallow_operations_on_prepared_transaction.js
index 13d423ab4c1..0e6a8453aa9 100644
--- a/jstests/core/txns/disallow_operations_on_prepared_transaction.js
+++ b/jstests/core/txns/disallow_operations_on_prepared_transaction.js
@@ -63,17 +63,6 @@ assert.commandFailedWithCode(assert.throws(function() {
}),
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();
diff --git a/jstests/core/txns/do_txn_atomicity.js b/jstests/core/txns/do_txn_atomicity.js
deleted file mode 100644
index 72d2591dbb6..00000000000
--- a/jstests/core/txns/do_txn_atomicity.js
+++ /dev/null
@@ -1,88 +0,0 @@
-// @tags: [uses_transactions]
-
-// Tests that doTxn is atomic for CRUD operations
-(function() {
-'use strict';
-
-var session = db.getMongo().startSession();
-var sessionDb = session.getDatabase("test");
-var txnNumber = 0;
-
-var t = db.doTxn;
-t.drop({writeConcern: {w: "majority"}});
-assert.writeOK(t.insert({_id: 1}));
-
-// Operations including commands are not allowed and should be rejected completely.
-assert.commandFailedWithCode(sessionDb.adminCommand({
- doTxn: [
- {op: 'i', ns: t.getFullName(), o: {_id: ObjectId(), x: 1}},
- {op: 'c', ns: "invalid", o: {create: "t"}},
- ],
- txnNumber: NumberLong(txnNumber++)
-}),
- ErrorCodes.InvalidOptions);
-assert.eq(t.count({x: 1}), 0);
-
-// Operations only including CRUD commands should be atomic, so the next insert will fail.
-assert.commandFailedWithCode(sessionDb.adminCommand({
- doTxn: [
- {op: 'i', ns: t.getFullName(), o: {_id: ObjectId(), x: 1}},
- {op: 'i', ns: "invalid", o: {_id: ObjectId(), x: 1}},
- ],
- txnNumber: NumberLong(txnNumber++)
-}),
- ErrorCodes.InvalidNamespace);
-assert.eq(t.count({x: 1}), 0);
-
-// Operations on non-existent databases cannot be atomic.
-var newDBName = "do_txn_atomicity";
-var newDB = sessionDb.getSiblingDB(newDBName);
-assert.commandWorked(newDB.dropDatabase());
-// Updates on a non-existent database no longer implicitly create collections and will fail with
-// a NamespaceNotFound error.
-assert.commandFailedWithCode(newDB.runCommand({
- doTxn: [{op: "u", ns: newDBName + ".foo", o: {_id: 5, x: 17}, o2: {_id: 5, x: 16}}],
- txnNumber: NumberLong(txnNumber++)
-}),
- ErrorCodes.NamespaceNotFound);
-
-var sawTooManyLocksError = false;
-
-function applyWithManyLocks(n) {
- let cappedOps = [];
- let multiOps = [];
-
- for (let i = 0; i < n; i++) {
- // Write to a capped collection, as that may require a lock for serialization.
- let cappedName = "capped" + n + "-" + i;
- assert.commandWorked(newDB.createCollection(cappedName, {capped: true, size: 100}));
- cappedOps.push({op: 'i', ns: newDBName + "." + cappedName, o: {_id: 0}});
-
- // Make an index multi-key, as that may require a lock for updating the catalog.
- let multiName = "multi" + n + "-" + i;
- assert.commandWorked(newDB[multiName].createIndex({x: 1}));
- multiOps.push({op: 'i', ns: newDBName + "." + multiName, o: {_id: 0, x: [0, 1]}});
- }
-
- let res = [cappedOps, multiOps].map(
- (doTxn) => newDB.runCommand({doTxn: doTxn, txnNumber: NumberLong(txnNumber++)}));
- sawTooManyLocksError |= res.some((res) => res.code === ErrorCodes.TooManyLocks);
- // Transactions involving just two collections should succeed.
- if (n <= 2)
- res.every((res) => res.ok);
- // All transactions should either completely succeed or completely fail.
- assert(res.every((res) => res.results.every((result) => result == res.ok)));
- assert(res.every((res) => !res.ok || res.applied == n));
-}
-
-// Try requiring different numbers of collection accesses in a single operation to cover
-// all edge cases, so we run out of available locks in different code paths such as during
-// oplog application.
-applyWithManyLocks(1);
-applyWithManyLocks(2);
-
-for (let i = 9; i < 20; i++) {
- applyWithManyLocks(i);
-}
-assert(!sawTooManyLocksError, "test should not exhaust the max number of locks held at once");
-})();
diff --git a/jstests/core/txns/do_txn_basic.js b/jstests/core/txns/do_txn_basic.js
deleted file mode 100644
index 1b0cc100644..00000000000
--- a/jstests/core/txns/do_txn_basic.js
+++ /dev/null
@@ -1,339 +0,0 @@
-// @tags: [uses_transactions]
-
-(function() {
-"use strict";
-
-const t = db.do_txn1;
-
-var session = db.getMongo().startSession();
-db = session.getDatabase("test");
-var txnNumber = 0;
-
-// Use majority write concern to clear the drop-pending that can cause lock conflicts with
-// transactions.
-t.drop({writeConcern: {w: "majority"}});
-
-//
-// Input validation tests
-//
-
-jsTestLog("Empty array of operations.");
-assert.commandFailedWithCode(db.adminCommand({doTxn: [], txnNumber: NumberLong(txnNumber++)}),
- ErrorCodes.InvalidOptions,
- 'doTxn should fail on empty array of operations');
-
-jsTestLog("Non-array type for operations.");
-assert.commandFailedWithCode(
- db.adminCommand({doTxn: "not an array", txnNumber: NumberLong(txnNumber++)}),
- ErrorCodes.TypeMismatch,
- 'doTxn should fail on non-array type for operations');
-
-jsTestLog("Missing 'op' field in an operation.");
-assert.commandFailedWithCode(
- db.adminCommand(
- {doTxn: [{ns: t.getFullName(), o: {_id: 0}}], txnNumber: NumberLong(txnNumber++)}),
- ErrorCodes.FailedToParse,
- 'doTxn should fail on operation without "op" field');
-
-jsTestLog("Non-string 'op' field in an operation.");
-assert.commandFailedWithCode(db.adminCommand({
- doTxn: [{op: 12345, ns: t.getFullName(), o: {_id: 0}}],
- txnNumber: NumberLong(txnNumber++)
-}),
- ErrorCodes.FailedToParse,
- 'doTxn should fail on operation with non-string "op" field');
-
-jsTestLog("Empty 'op' field value in an operation.");
-assert.commandFailedWithCode(
- db.adminCommand(
- {doTxn: [{op: '', ns: t.getFullName(), o: {_id: 0}}], txnNumber: NumberLong(txnNumber++)}),
- ErrorCodes.FailedToParse,
- 'doTxn should fail on operation with empty "op" field value');
-
-jsTestLog("Missing 'ns' field in an operation.");
-assert.commandFailedWithCode(
- db.adminCommand({doTxn: [{op: 'u', o: {_id: 0}}], txnNumber: NumberLong(txnNumber++)}),
- ErrorCodes.FailedToParse,
- 'doTxn should fail on operation without "ns" field');
-
-jsTestLog("Missing 'o' field in an operation.");
-assert.commandFailedWithCode(
- db.adminCommand({doTxn: [{op: 'u', ns: t.getFullName()}], txnNumber: NumberLong(txnNumber++)}),
- ErrorCodes.FailedToParse,
- 'doTxn should fail on operation without "o" field');
-
-jsTestLog("Non-string 'ns' field in an operation.");
-assert.commandFailedWithCode(
- db.adminCommand(
- {doTxn: [{op: 'u', ns: 12345, o: {_id: 0}}], txnNumber: NumberLong(txnNumber++)}),
- ErrorCodes.FailedToParse,
- 'doTxn should fail on operation with non-string "ns" field');
-
-jsTestLog("Missing dbname in 'ns' field.");
-assert.commandFailedWithCode(
- db.adminCommand(
- {doTxn: [{op: 'd', ns: t.getName(), o: {_id: 1}}], txnNumber: NumberLong(txnNumber++)}),
- ErrorCodes.InvalidNamespace,
- 'doTxn should fail with a missing dbname in the "ns" field value');
-
-jsTestLog("Empty 'ns' field value.");
-assert.commandFailed(
- db.adminCommand({doTxn: [{op: 'u', ns: '', o: {_id: 0}}], txnNumber: NumberLong(txnNumber++)}),
- 'doTxn should fail with empty "ns" field value');
-
-jsTestLog("Valid 'ns' field value in unknown operation type 'x'.");
-assert.commandFailedWithCode(
- db.adminCommand(
- {doTxn: [{op: 'x', ns: t.getFullName(), o: {_id: 0}}], txnNumber: NumberLong(txnNumber++)}),
- ErrorCodes.FailedToParse,
- 'doTxn should fail on unknown operation type "x" with valid "ns" value');
-
-jsTestLog("Illegal operation type 'n' (no-op).");
-assert.commandFailedWithCode(
- db.adminCommand(
- {doTxn: [{op: 'n', ns: t.getFullName(), o: {_id: 0}}], txnNumber: NumberLong(txnNumber++)}),
- ErrorCodes.InvalidOptions,
- 'doTxn should fail on "no op" operations.');
-
-jsTestLog("Illegal operation type 'c' (command).");
-assert.commandFailedWithCode(db.adminCommand({
- doTxn: [{op: 'c', ns: t.getCollection('$cmd').getFullName(), o: {applyOps: []}}],
- txnNumber: NumberLong(txnNumber++)
-}),
- ErrorCodes.InvalidOptions,
- 'doTxn should fail on commands.');
-
-jsTestLog("No transaction number in an otherwise valid operation.");
-assert.commandFailedWithCode(
- db.adminCommand({doTxn: [{"op": "i", "ns": t.getFullName(), "o": {_id: 5, x: 17}}]}),
- ErrorCodes.InvalidOptions,
- 'doTxn should fail when no transaction number is given.');
-
-jsTestLog("Session IDs and transaction numbers on sub-ops are not allowed");
-jsTestLog("doTxn should fail when inner transaction contains session id.");
-var lsid = {id: UUID()};
-res = assert.commandFailedWithCode(db.runCommand({
- doTxn: [{
- op: "i",
- ns: t.getFullName(),
- o: {_id: 7, x: 24},
- lsid: lsid,
- txnNumber: NumberLong(1),
- }],
- txnNumber: NumberLong(txnNumber++)
-}),
- ErrorCodes.FailedToParse,
- 'doTxn should fail when inner transaction contains session id.');
-
-jsTestLog("doTxn should fail when inner transaction contains transaction number.");
-res = assert.commandFailedWithCode(
- db.runCommand({
- doTxn: [{
- op: "u",
- ns: t.getFullName(),
- o2: {_id: 7},
- o: {$set: {x: 25}},
- txnNumber: NumberLong(1),
- }],
- txnNumber: NumberLong(txnNumber++)
- }),
- ErrorCodes.FailedToParse,
- 'doTxn should fail when inner transaction contains transaction number.');
-
-jsTestLog("doTxn should fail when inner transaction contains statement id.");
-res =
- assert.commandFailedWithCode(db.runCommand({
- doTxn: [{
- op: "d",
- ns: t.getFullName(),
- o: {_id: 7},
- stmtId: 0,
- }],
- txnNumber: NumberLong(txnNumber++)
- }),
- ErrorCodes.FailedToParse,
- 'doTxn should fail when inner transaction contains statement id.');
-
-jsTestLog("Malformed operation with unexpected field 'x'.");
-assert.commandFailedWithCode(db.adminCommand({
- doTxn: [{op: 'i', ns: t.getFullName(), o: {_id: 0}, x: 1}],
- txnNumber: NumberLong(txnNumber++)
-}),
- ErrorCodes.FailedToParse,
- 'doTxn should fail on malformed operations.');
-
-assert.eq(0, t.find().count(), "Non-zero amount of documents in collection to start");
-
-/**
- * Test function for running CRUD operations on non-existent namespaces using various
- * combinations of invalid namespaces (collection/database)
- *
- * Leave 'expectedErrorCode' undefined if this command is expected to run successfully.
- */
-function testCrudOperationOnNonExistentNamespace(optype, o, o2, expectedErrorCode) {
- expectedErrorCode = expectedErrorCode || ErrorCodes.OK;
- const t2 = db.getSiblingDB('do_txn1_no_such_db').getCollection('t');
- [t, t2].forEach(coll => {
- const op = {op: optype, ns: coll.getFullName(), o: o, o2: o2};
- const cmd = {doTxn: [op], txnNumber: NumberLong(txnNumber++)};
- jsTestLog('Testing doTxn on non-existent namespace: ' + tojson(cmd));
- if (expectedErrorCode === ErrorCodes.OK) {
- assert.commandWorked(db.adminCommand(cmd));
- } else {
- assert.commandFailedWithCode(db.adminCommand(cmd), expectedErrorCode);
- }
- });
-}
-
-// Insert, delete, and update operations on non-existent collections/databases should return
-// NamespaceNotFound.
-jsTestLog("testCrudOperationOnNonExistentNamespace");
-testCrudOperationOnNonExistentNamespace('i', {_id: 0}, {}, ErrorCodes.NamespaceNotFound);
-testCrudOperationOnNonExistentNamespace('d', {_id: 0}, {}, ErrorCodes.NamespaceNotFound);
-testCrudOperationOnNonExistentNamespace('u', {x: 0}, {_id: 0}, ErrorCodes.NamespaceNotFound);
-
-jsTestLog("Valid insert");
-assert.commandWorked(db.createCollection(t.getName()));
-var a = assert.commandWorked(db.adminCommand({
- doTxn: [{"op": "i", "ns": t.getFullName(), "o": {_id: 5, x: 17}}],
- txnNumber: NumberLong(txnNumber++)
-}));
-assert.eq(1, t.find().count(), "Valid insert failed");
-assert.eq(true, a.results[0], "Bad result value for valid insert");
-
-jsTestLog("Duplicate insert");
-a = assert.commandFailedWithCode(db.adminCommand({
- doTxn: [{"op": "i", "ns": t.getFullName(), "o": {_id: 5, x: 17}}],
- txnNumber: NumberLong(txnNumber++)
-}),
- ErrorCodes.DuplicateKey);
-assert.eq(
- 1, t.find().count(), "The number of documents changed despite the duplicate insert failing");
-assert.eq(false, a.results[0], "Bad result value for duplicate insert");
-
-var o = {_id: 5, x: 17};
-assert.eq(o, t.findOne(), "Mismatching document inserted.");
-
-jsTestLog("doTxn should fail on insert of object with empty array element");
-// 'o' field is an empty array.
-assert.commandFailed(
- db.adminCommand(
- {doTxn: [{op: 'i', ns: t.getFullName(), o: []}], txnNumber: NumberLong(txnNumber++)}),
- 'doTxn should fail on insert of object with empty array element');
-
-jsTestLog("two valid updates");
-var res = assert.commandWorked(db.runCommand({
- doTxn: [
- {op: "u", ns: t.getFullName(), o2: {_id: 5}, o: {$set: {x: 18}}},
- {op: "u", ns: t.getFullName(), o2: {_id: 5}, o: {$set: {x: 19}}}
- ],
- txnNumber: NumberLong(txnNumber++)
-}));
-
-o.x++;
-o.x++;
-
-assert.eq(1, t.find().count(), "Updates increased number of documents");
-assert.eq(o, t.findOne(), "Document doesn't match expected");
-assert.eq(true, res.results[0], "Bad result value for valid update");
-assert.eq(true, res.results[1], "Bad result value for valid update");
-
-jsTestLog("preCondition fully matches");
-res = assert.commandWorked(db.runCommand({
- doTxn: [
- {op: "u", ns: t.getFullName(), o2: {_id: 5}, o: {$set: {x: 20}}},
- {op: "u", ns: t.getFullName(), o2: {_id: 5}, o: {$set: {x: 21}}}
- ],
- preCondition: [{ns: t.getFullName(), q: {_id: 5}, res: {x: 19}}],
- txnNumber: NumberLong(txnNumber++)
-}));
-
-o.x++;
-o.x++;
-
-assert.eq(1, t.find().count(), "Updates increased number of documents");
-assert.eq(o, t.findOne(), "Document doesn't match expected");
-assert.eq(true, res.results[0], "Bad result value for valid update");
-assert.eq(true, res.results[1], "Bad result value for valid update");
-
-jsTestLog("preCondition doesn't match ns");
-res = assert.commandFailed(db.runCommand({
- doTxn: [
- {op: "u", ns: t.getFullName(), o2: {_id: 5}, o: {$set: {x: 22}}},
- {op: "u", ns: t.getFullName(), o2: {_id: 5}, o: {$set: {x: 23}}}
- ],
- preCondition: [{ns: "foo.otherName", q: {_id: 5}, res: {x: 21}}],
- txnNumber: NumberLong(txnNumber++)
-}));
-
-assert.eq(o, t.findOne(), "preCondition didn't match, but ops were still applied");
-
-jsTestLog("preCondition doesn't match query");
-res = assert.commandFailed(db.runCommand({
- doTxn: [
- {op: "u", ns: t.getFullName(), o2: {_id: 5}, o: {$set: {x: 22}}},
- {op: "u", ns: t.getFullName(), o2: {_id: 5}, o: {$set: {x: 23}}}
- ],
- preCondition: [{ns: t.getFullName(), q: {_id: 5}, res: {x: 19}}],
- txnNumber: NumberLong(txnNumber++)
-}));
-
-assert.eq(o, t.findOne(), "preCondition didn't match, but ops were still applied");
-
-jsTestLog("upsert disallowed");
-res = assert.commandFailed(db.runCommand({
- doTxn: [
- {op: "u", ns: t.getFullName(), o2: {_id: 5}, o: {$set: {x: 22}}},
- {op: "u", ns: t.getFullName(), o2: {_id: 6}, o: {$set: {x: 23}}}
- ],
- txnNumber: NumberLong(txnNumber++)
-}));
-
-assert.eq(false, res.results[0], "Op required upsert, which should be disallowed.");
-assert.eq(false, res.results[1], "Op required upsert, which should be disallowed.");
-
-// When applying a "u" (update) op, we default to 'UpdateNode' update semantics, and $set
-// operations add new fields in lexicographic order.
-jsTestLog("$set field addition order");
-res = assert.commandWorked(db.adminCommand({
- doTxn: [
- {"op": "i", "ns": t.getFullName(), "o": {_id: 6}},
- {"op": "u", "ns": t.getFullName(), "o2": {_id: 6}, "o": {$set: {z: 1, a: 2}}}
- ],
- txnNumber: NumberLong(txnNumber++)
-}));
-assert.eq(t.findOne({_id: 6}), {_id: 6, a: 2, z: 1}); // Note: 'a' and 'z' have been sorted.
-
-// 'ModifierInterface' semantics are not supported, so an update with {$v: 0} should fail.
-jsTestLog("Fail update with {$v:0}");
-res = assert.commandFailed(db.adminCommand({
- doTxn: [
- {"op": "i", "ns": t.getFullName(), "o": {_id: 7}},
- {
- "op": "u",
- "ns": t.getFullName(),
- "o2": {_id: 7},
- "o": {$v: NumberLong(0), $set: {z: 1, a: 2}}
- }
- ],
- txnNumber: NumberLong(txnNumber++),
-}));
-assert.eq(res.code, 40682);
-
-// When we explicitly specify {$v: 1}, we should get 'UpdateNode' update semantics, and $set
-// operations get performed in lexicographic order.
-jsTestLog("update with {$v:1}");
-res = assert.commandWorked(db.adminCommand({
- doTxn: [
- {"op": "i", "ns": t.getFullName(), "o": {_id: 8}},
- {
- "op": "u",
- "ns": t.getFullName(),
- "o2": {_id: 8},
- "o": {$v: NumberLong(1), $set: {z: 1, a: 2}}
- }
- ],
- txnNumber: NumberLong(txnNumber++),
-}));
-assert.eq(t.findOne({_id: 8}), {_id: 8, a: 2, z: 1}); // Note: 'a' and 'z' have been sorted.
-})();
diff --git a/jstests/core/txns/statement_ids_accepted.js b/jstests/core/txns/statement_ids_accepted.js
index 05640ead1f8..d93c0e818be 100644
--- a/jstests/core/txns/statement_ids_accepted.js
+++ b/jstests/core/txns/statement_ids_accepted.js
@@ -90,8 +90,6 @@ assert.commandWorked(sessionDb.runCommand({
autocommit: false
}));
-// The doTxn command is intentionally left out.
-
jsTestLog("Check that find and getmore accept a statement ID");
// Put in some data to find so getMore has a cursor to use.
assert.writeOK(testColl.insert([{_id: 0}, {_id: 1}], {writeConcern: {w: "majority"}}));
diff --git a/jstests/core/views/views_all_commands.js b/jstests/core/views/views_all_commands.js
index 30019797aa9..48374ac51ac 100644
--- a/jstests/core/views/views_all_commands.js
+++ b/jstests/core/views/views_all_commands.js
@@ -200,20 +200,6 @@ let viewsCommandTests = {
dbStats: {skip: "TODO(SERVER-25948)"},
delete: {command: {delete: "view", deletes: [{q: {x: 1}, limit: 1}]}, expectFailure: true},
distinct: {command: {distinct: "view", key: "_id"}},
- doTxn: {
- command: {
- doTxn: [{op: "i", o: {_id: 1}, ns: "test.view"}],
- txnNumber: NumberLong("0"),
- lsid: {id: UUID()}
- },
- expectFailure: true,
- expectedErrorCode: [
- ErrorCodes.CommandNotSupportedOnView,
- ErrorCodes.CommandNotSupported,
- ErrorCodes.IllegalOperation
- ],
- skipSharded: true,
- },
driverOIDTest: {skip: isUnrelated},
drop: {command: {drop: "view"}},
dropAllRolesFromDatabase: {skip: isUnrelated},
diff --git a/jstests/libs/override_methods/read_and_write_concern_helpers.js b/jstests/libs/override_methods/read_and_write_concern_helpers.js
index 6a04d6dc260..5fa3443ae72 100644
--- a/jstests/libs/override_methods/read_and_write_concern_helpers.js
+++ b/jstests/libs/override_methods/read_and_write_concern_helpers.js
@@ -51,7 +51,6 @@ var kCommandsSupportingWriteConcern = new Set([
"createUser",
"delete",
"deleteIndexes",
- "doTxn",
"drop",
"dropAllRolesFromDatabase",
"dropAllUsersFromDatabase",
@@ -82,4 +81,4 @@ var kCommandsSupportingWriteConcern = new Set([
]);
var kCommandsSupportingWriteConcernInTransaction =
- new Set(["doTxn", "abortTransaction", "commitTransaction"]);
+ new Set(["abortTransaction", "commitTransaction"]);
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index 8efc03ab32d..17bc2af8fca 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -114,7 +114,6 @@ const StringMap<int> txnCmdWhitelist = {{"abortTransaction", 1},
{"coordinateCommitTransaction", 1},
{"delete", 1},
{"distinct", 1},
- {"doTxn", 1},
{"find", 1},
{"findandmodify", 1},
{"findAndModify", 1},
@@ -129,7 +128,6 @@ const StringMap<int> txnCmdWhitelist = {{"abortTransaction", 1},
const StringMap<int> txnAdminCommands = {{"abortTransaction", 1},
{"commitTransaction", 1},
{"coordinateCommitTransaction", 1},
- {"doTxn", 1},
{"prepareTransaction", 1}};
} // namespace
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index 61c39a599cd..d48a75d91b3 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -349,7 +349,6 @@ env.Library(
"dbcheck.cpp",
"dbcommands_d.cpp",
"dbhash.cpp",
- "do_txn_cmd.cpp",
"driverHelpers.cpp",
"haystack.cpp",
"mr.cpp",
diff --git a/src/mongo/db/commands/do_txn_cmd.cpp b/src/mongo/db/commands/do_txn_cmd.cpp
deleted file mode 100644
index fbc542f952a..00000000000
--- a/src/mongo/db/commands/do_txn_cmd.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand
-
-#include "mongo/platform/basic.h"
-
-#include <vector>
-
-#include "mongo/bson/util/bson_check.h"
-#include "mongo/bson/util/bson_extract.h"
-#include "mongo/db/auth/authorization_session.h"
-#include "mongo/db/catalog/collection_catalog.h"
-#include "mongo/db/catalog/document_validation.h"
-#include "mongo/db/client.h"
-#include "mongo/db/commands.h"
-#include "mongo/db/commands/oplog_application_checks.h"
-#include "mongo/db/commands/test_commands_enabled.h"
-#include "mongo/db/concurrency/write_conflict_exception.h"
-#include "mongo/db/db_raii.h"
-#include "mongo/db/dbdirectclient.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/db/operation_context.h"
-#include "mongo/db/repl/do_txn.h"
-#include "mongo/db/repl/oplog.h"
-#include "mongo/db/repl/oplog_entry_gen.h"
-#include "mongo/db/repl/repl_client_info.h"
-#include "mongo/db/service_context.h"
-#include "mongo/util/log.h"
-#include "mongo/util/scopeguard.h"
-#include "mongo/util/uuid.h"
-
-namespace mongo {
-namespace {
-
-/**
- * Returns kNeedsUseUUID if the operation contains a UUID. Returns kOk if no conditions
- * which must be specially handled are detected. Throws an exception if the input is malformed or
- * if a command is in the list of ops.
- */
-OplogApplicationValidity validateDoTxnCommand(const BSONObj& doTxnObj) {
- auto parseOp = [](const BSONObj& opObj) {
- try {
- return repl::ReplOperation::parse(IDLParserErrorContext("doTxn"), opObj);
- } catch (...) {
- uasserted(ErrorCodes::FailedToParse,
- str::stream() << "cannot apply a malformed operation in doTxn: "
- << redact(opObj) << ": " << exceptionToStatus().toString());
- }
- };
-
- OplogApplicationValidity ret = OplogApplicationValidity::kOk;
-
- checkBSONType(BSONType::Array, doTxnObj.firstElement());
- // Check if the doTxn command is empty. There's no good reason for an empty transaction,
- // so reject it.
- uassert(ErrorCodes::InvalidOptions,
- "An empty doTxn command is not allowed.",
- !doTxnObj.firstElement().Array().empty());
-
- // Iterate the ops.
- for (BSONElement element : doTxnObj.firstElement().Array()) {
- checkBSONType(BSONType::Object, element);
- BSONObj opObj = element.Obj();
- auto op = parseOp(opObj);
-
- // If the op is a command, it's illegal.
- uassert(ErrorCodes::InvalidOptions,
- "Commands cannot be applied via doTxn.",
- op.getOpType() != repl::OpTypeEnum::kCommand);
-
- // If the op uses any UUIDs at all then the user must possess extra privileges.
- if (op.getUuid()) {
- ret = OplogApplicationValidity::kNeedsUseUUID;
- }
- }
- return ret;
-}
-
-class DoTxnCmd : public BasicCommand {
-public:
- DoTxnCmd() : BasicCommand("doTxn") {}
-
- AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
- return AllowedOnSecondary::kNever;
- }
-
- bool supportsWriteConcern(const BSONObj& cmd) const override {
- return true;
- }
-
- bool supportsReadConcern(const std::string& dbName,
- const BSONObj& cmdObj,
- repl::ReadConcernLevel level) const {
- // Support the read concerns before and after upconversion.
- return level == repl::ReadConcernLevel::kLocalReadConcern ||
- level == repl::ReadConcernLevel::kSnapshotReadConcern;
- }
-
- std::string help() const override {
- return "internal (sharding)\n{ doTxn : [ ] , preCondition : [ { ns : ... , q : ... , "
- "res : ... } ] }";
- }
-
- Status checkAuthForOperation(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj) const override {
- OplogApplicationValidity validity = validateDoTxnCommand(cmdObj);
- return OplogApplicationChecks::checkAuthForCommand(opCtx, dbname, cmdObj, validity);
- }
-
- bool run(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
- BSONObjBuilder& result) override {
-
- validateDoTxnCommand(cmdObj);
-
- boost::optional<DisableDocumentValidation> maybeDisableValidation;
- if (shouldBypassDocumentValidationForCommand(cmdObj))
- maybeDisableValidation.emplace(opCtx);
-
- auto status = OplogApplicationChecks::checkOperationArray(cmdObj.firstElement());
- uassertStatusOK(status);
-
- // TODO (SERVER-30217): When a write concern is provided to the doTxn command, we
- // normally wait on the OpTime of whichever operation successfully completed last. This is
- // erroneous, however, if the last operation in the array happens to be a write no-op and
- // thus isn’t assigned an OpTime. Let the second to last operation in the doTxn be write
- // A, the last operation in doTxn be write B. Let B do a no-op write and let the
- // operation that caused B to be a no-op be C. If C has an OpTime after A but before B,
- // then we won’t wait for C to be replicated and it could be rolled back, even though B
- // was acknowledged. To fix this, we should wait for replication of the node’s last applied
- // OpTime if the last write operation was a no-op write.
-
- auto doTxnStatus = CommandHelpers::appendCommandStatusNoThrow(
- result, doTxn(opCtx, dbname, cmdObj, &result));
-
- return doTxnStatus;
- }
-};
-
-MONGO_REGISTER_TEST_COMMAND(DoTxnCmd);
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/db/commands/oplog_application_checks.h b/src/mongo/db/commands/oplog_application_checks.h
index 4831950b3d4..3537dc716ab 100644
--- a/src/mongo/db/commands/oplog_application_checks.h
+++ b/src/mongo/db/commands/oplog_application_checks.h
@@ -50,11 +50,9 @@ class OperationContext;
// with a specified UUID, so both the forceUUID and useUUID actions must be authorized.
//
// kOk means no special conditions apply.
-//
-// Only kOk and kNeedsUseUUID are valid for 'doTxn'. All are valid for 'applyOps'.
enum class OplogApplicationValidity { kOk, kNeedsUseUUID, kNeedsForceAndUseUUID, kNeedsSuperuser };
-// OplogApplicationChecks contains helper functions for checking the applyOps and doTxn commands.
+// OplogApplicationChecks contains helper functions for checking the applyOps command.
class OplogApplicationChecks {
public:
/**
diff --git a/src/mongo/db/initialize_operation_session_info.cpp b/src/mongo/db/initialize_operation_session_info.cpp
index 9be38b08946..0257878e145 100644
--- a/src/mongo/db/initialize_operation_session_info.cpp
+++ b/src/mongo/db/initialize_operation_session_info.cpp
@@ -143,22 +143,6 @@ OperationSessionInfoFromClient initializeOperationSessionInfo(OperationContext*
osi.getStartTransaction().value());
}
- // Populate the session info for doTxn command.
- if (requestBody.firstElementFieldName() == "doTxn"_sd) {
- uassert(ErrorCodes::InvalidOptions,
- "doTxn can only be run with a transaction number.",
- osi.getTxnNumber());
- uassert(ErrorCodes::OperationNotSupportedInTransaction,
- "doTxn can not be run in a transaction",
- !osi.getAutocommit());
- // 'autocommit' and 'startTransaction' are populated for 'doTxn' to get the oplog
- // entry generation behavior used for multi-document transactions. The 'doTxn'
- // command still logically behaves as a commit.
- osi.setAutocommit(false);
- osi.setStartTransaction(true);
- }
-
-
return osi;
}
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index eda9c1c6315..871e321363d 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -32,7 +32,6 @@ env.Library(
target='oplog',
source=[
'apply_ops.cpp',
- 'do_txn.cpp',
'oplog.cpp',
'transaction_oplog_application.cpp',
env.Idlc('apply_ops.idl')[0],
@@ -116,25 +115,6 @@ env.Library(
],
)
-env.CppUnitTest(
- target='do_txn_test',
- source=[
- 'do_txn_test.cpp',
- ],
- LIBDEPS=[
- 'oplog',
- 'oplog_entry',
- 'oplog_interface_local',
- 'replmocks',
- 'storage_interface_impl',
- '$BUILD_DIR/mongo/db/auth/authmocks',
- '$BUILD_DIR/mongo/db/s/op_observer_sharding_impl',
- '$BUILD_DIR/mongo/db/service_context_d_test_fixture',
- '$BUILD_DIR/mongo/db/transaction',
- '$BUILD_DIR/mongo/rpc/command_status',
- ],
-)
-
env.Library(
target='rollback_source_impl',
source=[
diff --git a/src/mongo/db/repl/do_txn.cpp b/src/mongo/db/repl/do_txn.cpp
deleted file mode 100644
index d9d6f7bf2ce..00000000000
--- a/src/mongo/db/repl/do_txn.cpp
+++ /dev/null
@@ -1,313 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/db/repl/do_txn.h"
-
-#include "mongo/bson/util/bson_extract.h"
-#include "mongo/db/catalog/collection.h"
-#include "mongo/db/catalog/collection_catalog.h"
-#include "mongo/db/catalog/database.h"
-#include "mongo/db/catalog/database_holder.h"
-#include "mongo/db/catalog/document_validation.h"
-#include "mongo/db/client.h"
-#include "mongo/db/concurrency/lock_state.h"
-#include "mongo/db/concurrency/write_conflict_exception.h"
-#include "mongo/db/curop.h"
-#include "mongo/db/db_raii.h"
-#include "mongo/db/dbhelpers.h"
-#include "mongo/db/index/index_descriptor.h"
-#include "mongo/db/matcher/matcher.h"
-#include "mongo/db/op_observer.h"
-#include "mongo/db/operation_context.h"
-#include "mongo/db/query/collation/collation_spec.h"
-#include "mongo/db/repl/replication_coordinator.h"
-#include "mongo/db/service_context.h"
-#include "mongo/db/transaction_participant.h"
-#include "mongo/db/views/view_catalog.h"
-#include "mongo/rpc/get_status_from_command_result.h"
-#include "mongo/util/fail_point_service.h"
-#include "mongo/util/log.h"
-
-namespace mongo {
-
-constexpr StringData DoTxn::kPreconditionFieldName;
-
-namespace {
-
-// If enabled, causes loop in _doTxn() to hang after applying current operation.
-MONGO_FAIL_POINT_DEFINE(doTxnPauseBetweenOperations);
-
-/**
- * Return true iff the doTxnCmd can be executed in a single WriteUnitOfWork.
- */
-bool _areOpsCrudOnly(const BSONObj& doTxnCmd) {
- for (const auto& elem : doTxnCmd.firstElement().Obj()) {
- const char* opType = elem.Obj().getField("op").valuestrsafe();
-
- // All atomic ops have an opType of length 1.
- if (opType[0] == '\0' || opType[1] != '\0')
- return false;
-
- // Only consider CRUD operations.
- switch (*opType) {
- case 'd':
- case 'u':
- break;
- case 'i':
- break;
- // Fallthrough.
- default:
- return false;
- }
- }
-
- return true;
-}
-
-Status _doTxn(OperationContext* opCtx,
- const std::string& dbName,
- const BSONObj& doTxnCmd,
- BSONObjBuilder* result,
- int* numApplied) {
- BSONObj ops = doTxnCmd.firstElement().Obj();
- // apply
- *numApplied = 0;
- int errors = 0;
-
- BSONObjIterator i(ops);
- BSONArrayBuilder ab;
- invariant(opCtx->lockState()->inAWriteUnitOfWork());
-
- // Apply each op in the given 'doTxn' command object.
- while (i.more()) {
- BSONElement e = i.next();
- const BSONObj& opObj = e.Obj();
-
- boost::optional<NamespaceString> nss = NamespaceString(opObj["ns"].String());
-
- // Need to check this here, or OldClientContext may fail an invariant.
- if (!nss->isValid())
- return {ErrorCodes::InvalidNamespace, "invalid ns: " + nss->ns()};
-
- Status status(ErrorCodes::InternalError, "");
-
- AutoGetDb autoDb(opCtx, nss->db(), MODE_IX);
- auto db = autoDb.getDb();
- if (!db) {
- uasserted(ErrorCodes::NamespaceNotFound,
- str::stream() << "cannot apply insert, delete, or update operation on a "
- "non-existent namespace "
- << nss->ns() << ": " << mongo::redact(opObj));
- }
-
- if (opObj.hasField("ui")) {
- auto uuidStatus = UUID::parse(opObj["ui"]);
- uassertStatusOK(uuidStatus.getStatus());
- // If "ui" is present, it overrides "nss" for the collection name.
- nss = CollectionCatalog::get(opCtx).lookupNSSByUUID(uuidStatus.getValue());
- uassert(ErrorCodes::NamespaceNotFound,
- str::stream() << "cannot find collection uuid " << uuidStatus.getValue(),
- nss);
- }
- Lock::CollectionLock collLock(opCtx, *nss, MODE_IX);
- auto collection = db->getCollection(opCtx, *nss);
-
- // When processing an update on a non-existent collection, applyOperation_inlock()
- // returns UpdateOperationFailed on updates and allows the collection to be
- // implicitly created on upserts. We detect both cases here and fail early with
- // NamespaceNotFound.
- // Additionally for inserts, we fail early on non-existent collections.
- if (!collection && ViewCatalog::get(db)->lookup(opCtx, nss->ns())) {
- uasserted(ErrorCodes::CommandNotSupportedOnView,
- str::stream() << "doTxn not supported on a view: " << redact(opObj));
- }
- if (!collection) {
- uasserted(ErrorCodes::NamespaceNotFound,
- str::stream() << "cannot apply operation on a non-existent namespace "
- << nss->ns() << " with doTxn: " << redact(opObj));
- }
-
- // Setting alwaysUpsert to true makes sense only during oplog replay, and doTxn commands
- // should not be executed during oplog replay.
- const bool alwaysUpsert = false;
- status = repl::applyOperation_inlock(
- opCtx, db, opObj, alwaysUpsert, repl::OplogApplication::Mode::kApplyOpsCmd);
- if (!status.isOK())
- return status;
-
- ab.append(status.isOK());
- if (!status.isOK()) {
- log() << "doTxn error applying: " << status;
- errors++;
- }
-
- (*numApplied)++;
-
- if (MONGO_FAIL_POINT(doTxnPauseBetweenOperations)) {
- MONGO_FAIL_POINT_PAUSE_WHILE_SET(doTxnPauseBetweenOperations);
- }
- }
-
- result->append("applied", *numApplied);
- result->append("results", ab.arr());
-
- if (errors != 0) {
- return Status(ErrorCodes::UnknownError, "doTxn had one or more errors applying ops");
- }
-
- return Status::OK();
-}
-
-bool _hasPrecondition(const BSONObj& doTxnCmd) {
- return doTxnCmd[DoTxn::kPreconditionFieldName].type() == Array;
-}
-
-Status _checkPrecondition(OperationContext* opCtx,
- const BSONObj& doTxnCmd,
- BSONObjBuilder* result) {
- // Precondition check must be done in a write unit of work to make sure it's
- // sharing the same snapshot as the writes.
- invariant(opCtx->lockState()->inAWriteUnitOfWork());
-
- invariant(_hasPrecondition(doTxnCmd));
-
- for (auto elem : doTxnCmd[DoTxn::kPreconditionFieldName].Obj()) {
- auto preCondition = elem.Obj();
- if (preCondition["ns"].type() != BSONType::String) {
- return {ErrorCodes::InvalidNamespace,
- str::stream() << "ns in preCondition must be a string, but found type: "
- << typeName(preCondition["ns"].type())};
- }
- const NamespaceString nss(preCondition["ns"].valueStringData());
- if (!nss.isValid()) {
- return {ErrorCodes::InvalidNamespace, "invalid ns: " + nss.ns()};
- }
-
- // Even if snapshot isolation is provided, database catalog still needs locking.
- // Only X and IX mode locks are stashed by the WriteUnitOfWork and IS->IX upgrade
- // is not supported, so we can not use IS mode here.
- AutoGetCollection autoColl(opCtx, nss, MODE_IX);
-
- Database* database = autoColl.getDb();
- if (!database) {
- return {ErrorCodes::NamespaceNotFound, "database in ns does not exist: " + nss.ns()};
- }
- Collection* collection = autoColl.getCollection();
- if (!collection) {
- return {ErrorCodes::NamespaceNotFound, "collection in ns does not exist: " + nss.ns()};
- }
-
- BSONObj realres;
- auto qrStatus = QueryRequest::fromLegacyQuery(nss, preCondition["q"].Obj(), {}, 0, 0, 0);
- if (!qrStatus.isOK()) {
- return qrStatus.getStatus();
- }
- auto recordId = Helpers::findOne(
- opCtx, autoColl.getCollection(), std::move(qrStatus.getValue()), false);
- if (!recordId.isNull()) {
- realres = collection->docFor(opCtx, recordId).value();
- }
-
-
- // Get collection default collation.
- const CollatorInterface* collator = collection->getDefaultCollator();
-
- boost::intrusive_ptr<ExpressionContext> expCtx(new ExpressionContext(opCtx, collator));
- Matcher matcher(preCondition["res"].Obj(), std::move(expCtx));
- if (!matcher.matches(realres)) {
- result->append("got", realres);
- result->append("whatFailed", preCondition);
- return {ErrorCodes::BadValue, "preCondition failed"};
- }
- }
-
- return Status::OK();
-}
-} // namespace
-
-Status doTxn(OperationContext* opCtx,
- const std::string& dbName,
- const BSONObj& doTxnCmd,
- BSONObjBuilder* result) {
- auto txnParticipant = TransactionParticipant::get(opCtx);
- uassert(ErrorCodes::InvalidOptions, "doTxn must be run within a transaction", txnParticipant);
- invariant(txnParticipant.inMultiDocumentTransaction());
- invariant(opCtx->getWriteUnitOfWork());
- uassert(
- ErrorCodes::InvalidOptions, "doTxn supports only CRUD opts.", _areOpsCrudOnly(doTxnCmd));
- auto hasPrecondition = _hasPrecondition(doTxnCmd);
-
-
- // Acquire global lock in IX mode so that the replication state check will remain valid.
- Lock::GlobalLock globalLock(opCtx, MODE_IX);
-
- auto replCoord = repl::ReplicationCoordinator::get(opCtx);
- bool userInitiatedWritesAndNotPrimary =
- opCtx->writesAreReplicated() && !replCoord->canAcceptWritesForDatabase(opCtx, dbName);
-
- if (userInitiatedWritesAndNotPrimary)
- return Status(ErrorCodes::NotMaster,
- str::stream() << "Not primary while applying ops to database " << dbName);
-
- int numApplied = 0;
-
- try {
- BSONObjBuilder intermediateResult;
-
- // The transaction takes place in a global unit of work, so the precondition check
- // and the writes will share the same snapshot.
- if (hasPrecondition) {
- uassertStatusOK(_checkPrecondition(opCtx, doTxnCmd, result));
- }
-
- numApplied = 0;
- uassertStatusOK(_doTxn(opCtx, dbName, doTxnCmd, &intermediateResult, &numApplied));
- txnParticipant.commitUnpreparedTransaction(opCtx);
- result->appendElements(intermediateResult.obj());
- } catch (const DBException& ex) {
- txnParticipant.abortActiveUnpreparedOrStashPreparedTransaction(opCtx);
- BSONArrayBuilder ab;
- ++numApplied;
- for (int j = 0; j < numApplied; j++)
- ab.append(false);
- result->append("applied", numApplied);
- result->append("code", ex.code());
- result->append("codeName", ErrorCodes::errorString(ex.code()));
- result->append("errmsg", ex.what());
- result->append("results", ab.arr());
- return Status(ErrorCodes::UnknownError, ex.what());
- }
-
- return Status::OK();
-}
-
-} // namespace mongo
diff --git a/src/mongo/db/repl/do_txn.h b/src/mongo/db/repl/do_txn.h
deleted file mode 100644
index 7bed2bf3700..00000000000
--- a/src/mongo/db/repl/do_txn.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-#include "mongo/base/status.h"
-#include "mongo/db/repl/oplog.h"
-
-namespace mongo {
-class BSONObj;
-class BSONObjBuilder;
-class OperationContext;
-
-class DoTxn {
-public:
- static constexpr StringData kPreconditionFieldName = "preCondition"_sd;
-};
-
-/**
- * Applies ops contained in 'doTxnCmd' and populates fields in 'result' to be returned to the
- * caller. The information contained in 'result' can be returned to the user if called as part
- * of the execution of an 'doTxn' command.
- *
- * The 'oplogApplicationMode' argument determines the semantics of the operations contained within
- * the given command object. This function may be called as part of a direct user invocation of the
- * 'doTxn' command, or as part of the application of an 'doTxn' oplog operation. In either
- * case, the mode can be set to determine how the internal ops are executed.
- */
-Status doTxn(OperationContext* opCtx,
- const std::string& dbName,
- const BSONObj& doTxnCmd,
- BSONObjBuilder* result);
-
-} // namespace mongo
diff --git a/src/mongo/db/repl/do_txn_test.cpp b/src/mongo/db/repl/do_txn_test.cpp
deleted file mode 100644
index 561579a069c..00000000000
--- a/src/mongo/db/repl/do_txn_test.cpp
+++ /dev/null
@@ -1,335 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/db/catalog/collection_options.h"
-#include "mongo/db/catalog/database_holder.h"
-#include "mongo/db/client.h"
-#include "mongo/db/op_observer_noop.h"
-#include "mongo/db/op_observer_registry.h"
-#include "mongo/db/repl/do_txn.h"
-#include "mongo/db/repl/oplog_interface_local.h"
-#include "mongo/db/repl/repl_client_info.h"
-#include "mongo/db/repl/replication_coordinator_mock.h"
-#include "mongo/db/repl/storage_interface_impl.h"
-#include "mongo/db/s/op_observer_sharding_impl.h"
-#include "mongo/db/service_context_d_test_fixture.h"
-#include "mongo/db/session_catalog_mongod.h"
-#include "mongo/db/transaction_participant.h"
-#include "mongo/logger/logger.h"
-#include "mongo/rpc/get_status_from_command_result.h"
-#include "mongo/stdx/memory.h"
-
-namespace mongo {
-namespace repl {
-namespace {
-
-boost::optional<OplogEntry> onAllTransactionCommit(OperationContext* opCtx) {
- OplogInterfaceLocal oplogInterface(opCtx);
- auto oplogIter = oplogInterface.makeIterator();
- auto opEntry = unittest::assertGet(oplogIter->next());
- return unittest::assertGet(OplogEntry::parse(opEntry.first));
-}
-
-/**
- * Mock OpObserver that tracks doTxn commit events.
- */
-class OpObserverMock : public OpObserverNoop {
-public:
- /**
- * Called by doTxn() when ops are ready to commit.
- */
- void onUnpreparedTransactionCommit(OperationContext* opCtx,
- const std::vector<repl::ReplOperation>& statements) override;
-
-
- /**
- * Called by doTxn() when ops are ready to commit.
- */
- void onPreparedTransactionCommit(
- OperationContext* opCtx,
- OplogSlot commitOplogEntryOpTime,
- Timestamp commitTimestamp,
- const std::vector<repl::ReplOperation>& statements) noexcept override;
-
- // If present, holds the applyOps oplog entry written out by the ObObserverImpl
- // onPreparedTransactionCommit or onUnpreparedTransactionCommit.
- boost::optional<OplogEntry> applyOpsOplogEntry;
-};
-
-void OpObserverMock::onUnpreparedTransactionCommit(
- OperationContext* opCtx, const std::vector<repl::ReplOperation>& statements) {
-
- applyOpsOplogEntry = onAllTransactionCommit(opCtx);
-}
-
-void OpObserverMock::onPreparedTransactionCommit(
- OperationContext* opCtx,
- OplogSlot commitOplogEntryOpTime,
- Timestamp commitTimestamp,
- const std::vector<repl::ReplOperation>& statements) noexcept {
- applyOpsOplogEntry = onAllTransactionCommit(opCtx);
-}
-
-/**
- * Test fixture for doTxn().
- */
-class DoTxnTest : public ServiceContextMongoDTest {
-private:
- void setUp() override;
- void tearDown() override;
-
-protected:
- OperationContext* opCtx() {
- return _opCtx.get();
- }
-
- void checkTxnTable() {
- auto result = _storage->findById(
- opCtx(),
- NamespaceString::kSessionTransactionsTableNamespace,
- BSON(SessionTxnRecord::kSessionIdFieldName << opCtx()->getLogicalSessionId()->toBSON())
- .firstElement());
- if (!_opObserver->applyOpsOplogEntry) {
- ASSERT_NOT_OK(result);
- return;
- }
- auto txnRecord = SessionTxnRecord::parse(IDLParserErrorContext("parse txn record for test"),
- unittest::assertGet(result));
-
- ASSERT(opCtx()->getTxnNumber());
- ASSERT_EQ(*opCtx()->getTxnNumber(), txnRecord.getTxnNum());
- ASSERT_EQ(_opObserver->applyOpsOplogEntry->getOpTime(), txnRecord.getLastWriteOpTime());
- ASSERT(_opObserver->applyOpsOplogEntry->getWallClockTime());
- ASSERT_EQ(*_opObserver->applyOpsOplogEntry->getWallClockTime(),
- txnRecord.getLastWriteDate());
- }
-
- OpObserverMock* _opObserver = nullptr;
- std::unique_ptr<StorageInterface> _storage;
- ServiceContext::UniqueOperationContext _opCtx;
- boost::optional<MongoDOperationContextSession> _ocs;
-};
-
-void DoTxnTest::setUp() {
- // Set up mongod.
- ServiceContextMongoDTest::setUp();
-
- const auto service = getServiceContext();
- _opCtx = cc().makeOperationContext();
-
- // Set up ReplicationCoordinator and create oplog.
- ReplicationCoordinator::set(service, stdx::make_unique<ReplicationCoordinatorMock>(service));
- setOplogCollectionName(service);
- createOplog(_opCtx.get());
-
- // Ensure that we are primary.
- auto replCoord = ReplicationCoordinator::get(_opCtx.get());
- ASSERT_OK(replCoord->setFollowerMode(MemberState::RS_PRIMARY));
-
- // onStepUp() relies on the storage interface to create the config.transactions table.
- repl::StorageInterface::set(service, std::make_unique<StorageInterfaceImpl>());
-
- // Set up session catalog
- MongoDSessionCatalog::onStepUp(_opCtx.get());
-
- // Need the OpObserverImpl in the registry in order for doTxn to work.
- OpObserverRegistry* opObserverRegistry =
- dynamic_cast<OpObserverRegistry*>(service->getOpObserver());
- opObserverRegistry->addObserver(stdx::make_unique<OpObserverShardingImpl>());
-
- // Use OpObserverMock to track applyOps calls generated by doTxn().
- auto opObserver = stdx::make_unique<OpObserverMock>();
- _opObserver = opObserver.get();
- opObserverRegistry->addObserver(std::move(opObserver));
-
- // This test uses StorageInterface to create collections and inspect documents inside
- // collections.
- _storage = stdx::make_unique<StorageInterfaceImpl>();
-
- // Set up the transaction and session.
- _opCtx->setLogicalSessionId(makeLogicalSessionIdForTest());
- _opCtx->setTxnNumber(0); // TxnNumber can always be 0 because we have a new session.
- _ocs.emplace(_opCtx.get());
-
- auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant.beginOrContinue(opCtx(), *opCtx()->getTxnNumber(), false, true);
- txnParticipant.unstashTransactionResources(opCtx(), "doTxn");
-}
-
-void DoTxnTest::tearDown() {
- _ocs = boost::none;
- _opCtx = nullptr;
- _storage = {};
- _opObserver = nullptr;
-
- // Reset default log level in case it was changed.
- logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogComponent::kReplication,
- logger::LogSeverity::Debug(0));
-
- ServiceContextMongoDTest::tearDown();
-}
-
-/**
- * Fixes up result document returned by doTxn and converts to Status.
- */
-Status getStatusFromDoTxnResult(const BSONObj& result) {
- if (result["ok"]) {
- return getStatusFromCommandResult(result);
- }
-
- BSONObjBuilder builder;
- builder.appendElements(result);
- auto code = result.getIntField("code");
- builder.appendIntOrLL("ok", code == 0);
- auto newResult = builder.obj();
- return getStatusFromCommandResult(newResult);
-}
-
-BSONObj makeInsertOperation(const NamespaceString& nss,
- const OptionalCollectionUUID& uuid,
- const BSONObj& documentToInsert) {
- return uuid ? BSON("op"
- << "i"
- << "ns" << nss.ns() << "o" << documentToInsert << "ui" << *uuid)
- : BSON("op"
- << "i"
- << "ns" << nss.ns() << "o" << documentToInsert);
-}
-
-/**
- * Creates an doTxn command object with a single insert operation.
- */
-BSONObj makeDoTxnWithInsertOperation(const NamespaceString& nss,
- const OptionalCollectionUUID& uuid,
- const BSONObj& documentToInsert) {
- auto insertOp = makeInsertOperation(nss, uuid, documentToInsert);
- return BSON("doTxn" << BSON_ARRAY(insertOp));
-}
-
-/**
- * Creates an applyOps command object with a single insert operation.
- */
-BSONObj makeApplyOpsWithInsertOperation(const NamespaceString& nss,
- const OptionalCollectionUUID& uuid,
- const BSONObj& documentToInsert) {
- auto insertOp = makeInsertOperation(nss, uuid, documentToInsert);
- return BSON("applyOps" << BSON_ARRAY(insertOp));
-}
-
-TEST_F(DoTxnTest, AtomicDoTxnInsertIntoNonexistentCollectionReturnsNamespaceNotFoundInResult) {
- NamespaceString nss("test.t");
- auto documentToInsert = BSON("_id" << 0);
- auto cmdObj = makeDoTxnWithInsertOperation(nss, boost::none, documentToInsert);
- BSONObjBuilder resultBuilder;
- ASSERT_EQUALS(ErrorCodes::UnknownError, doTxn(opCtx(), "test", cmdObj, &resultBuilder));
- auto result = resultBuilder.obj();
- auto status = getStatusFromDoTxnResult(result);
- ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, status);
- checkTxnTable();
-}
-
-TEST_F(DoTxnTest, AtomicDoTxnInsertWithUuidIntoCollectionWithUuid) {
- NamespaceString nss("test.t");
-
- auto uuid = UUID::gen();
-
- CollectionOptions collectionOptions;
- collectionOptions.uuid = uuid;
- ASSERT_OK(_storage->createCollection(opCtx(), nss, collectionOptions));
-
- auto documentToInsert = BSON("_id" << 0);
- auto cmdObj = makeDoTxnWithInsertOperation(nss, uuid, documentToInsert);
- auto expectedCmdObj = makeApplyOpsWithInsertOperation(nss, uuid, documentToInsert);
- BSONObjBuilder resultBuilder;
- ASSERT_OK(doTxn(opCtx(), "test", cmdObj, &resultBuilder));
- ASSERT_EQ(expectedCmdObj.woCompare(_opObserver->applyOpsOplogEntry->getObject(),
- BSONObj(),
- BSONObj::ComparisonRules::kIgnoreFieldOrder |
- BSONObj::ComparisonRules::kConsiderFieldName),
- 0)
- << "expected: " << expectedCmdObj
- << " got: " << _opObserver->applyOpsOplogEntry->getObject();
- checkTxnTable();
-}
-
-TEST_F(DoTxnTest, AtomicDoTxnInsertWithUuidIntoCollectionWithOtherUuid) {
- NamespaceString nss("test.t");
-
- auto doTxnUuid = UUID::gen();
-
- // Collection has a different UUID.
- CollectionOptions collectionOptions;
- collectionOptions.uuid = UUID::gen();
- ASSERT_NOT_EQUALS(doTxnUuid, *collectionOptions.uuid);
- ASSERT_OK(_storage->createCollection(opCtx(), nss, collectionOptions));
-
- // The doTxn returns a NamespaceNotFound error because of the failed UUID lookup
- // even though a collection exists with the same namespace as the insert operation.
- auto documentToInsert = BSON("_id" << 0);
- auto cmdObj = makeDoTxnWithInsertOperation(nss, doTxnUuid, documentToInsert);
- BSONObjBuilder resultBuilder;
- ASSERT_EQUALS(ErrorCodes::UnknownError, doTxn(opCtx(), "test", cmdObj, &resultBuilder));
- auto result = resultBuilder.obj();
- auto status = getStatusFromDoTxnResult(result);
- ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, status);
- checkTxnTable();
-}
-
-TEST_F(DoTxnTest, AtomicDoTxnInsertWithoutUuidIntoCollectionWithUuid) {
- NamespaceString nss("test.t");
-
- auto uuid = UUID::gen();
-
- CollectionOptions collectionOptions;
- collectionOptions.uuid = uuid;
- ASSERT_OK(_storage->createCollection(opCtx(), nss, collectionOptions));
-
- auto documentToInsert = BSON("_id" << 0);
- auto cmdObj = makeDoTxnWithInsertOperation(nss, boost::none, documentToInsert);
- BSONObjBuilder resultBuilder;
- ASSERT_OK(doTxn(opCtx(), "test", cmdObj, &resultBuilder));
-
- // Insert operation provided by caller did not contain collection uuid but doTxn() should add
- // the uuid to the oplog entry.
- auto expectedCmdObj = makeApplyOpsWithInsertOperation(nss, uuid, documentToInsert);
- ASSERT_EQ(expectedCmdObj.woCompare(_opObserver->applyOpsOplogEntry->getObject(),
- BSONObj(),
- BSONObj::ComparisonRules::kIgnoreFieldOrder |
- BSONObj::ComparisonRules::kConsiderFieldName),
- 0)
- << "expected: " << expectedCmdObj
- << " got: " << _opObserver->applyOpsOplogEntry->getObject();
- checkTxnTable();
-}
-
-} // namespace
-} // namespace repl
-} // namespace mongo
diff --git a/src/mongo/db/transaction_validation.cpp b/src/mongo/db/transaction_validation.cpp
index d69b935e5bb..a2af2228612 100644
--- a/src/mongo/db/transaction_validation.cpp
+++ b/src/mongo/db/transaction_validation.cpp
@@ -61,7 +61,7 @@ void validateWriteConcernForTransaction(const WriteConcernOptions& wcResult, Str
"writeConcern is not allowed within a multi-statement transaction",
wcResult.usedDefault || cmdName == "commitTransaction" ||
cmdName == "coordinateCommitTransaction" || cmdName == "abortTransaction" ||
- cmdName == "prepareTransaction" || cmdName == "doTxn");
+ cmdName == "prepareTransaction");
}
bool shouldCommandSkipSessionCheckout(StringData cmdName) {