2025-03-17 20:58:26 +01:00

560 lines
15 KiB
YAML

# Test that all the operations go to the same mongos.
#
# In tests that don't include command-started events the assertion is implicit:
# that all the read operations succeed. If the driver does not properly pin to
# a single mongos then one of the operations in a transaction will eventually
# be sent to a different mongos, which is unaware of the transaction, and the
# mongos will return a command error. An example of such an error is:
# {
# 'ok': 0.0,
# 'errmsg': 'cannot continue txnId -1 for session 28938f50-9d29-4ca5-8de5-ddaf261267c4 - 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU= with txnId 1',
# 'code': 251,
# 'codeName': 'NoSuchTransaction',
# 'errorLabels': ['TransientTransactionError']
# }
runOn:
-
minServerVersion: "4.1.8"
topology: ["sharded"]
# serverless proxy doesn't append error labels to errors in transactions
# caused by failpoints (CLOUDP-88216)
serverless: "forbid"
database_name: &database_name "transaction-tests"
collection_name: &collection_name "test"
data: &data
- {_id: 1}
- {_id: 2}
tests:
- description: countDocuments
useMultipleMongoses: true
operations:
- &startTransaction
name: startTransaction
object: session0
- &countDocuments
name: countDocuments
object: collection
arguments:
filter:
_id: 2
session: session0
result: 1
- *countDocuments
- *countDocuments
- *countDocuments
- *countDocuments
- *countDocuments
- *countDocuments
- *countDocuments
- &commitTransaction
name: commitTransaction
object: session0
outcome:
collection:
data: *data
- description: distinct
useMultipleMongoses: true
operations:
- *startTransaction
- &distinct
name: distinct
object: collection
arguments:
fieldName: _id
session: session0
result: [1, 2]
- *distinct
- *distinct
- *distinct
- *distinct
- *distinct
- *distinct
- *distinct
- *commitTransaction
outcome:
collection:
data: *data
- description: find
useMultipleMongoses: true
operations:
- name: startTransaction
object: session0
- &find
name: find
object: collection
arguments:
filter:
_id: 2
session: session0
result:
- {_id: 2}
- *find
- *find
- *find
- *find
- *find
- *find
- *find
- *commitTransaction
outcome:
collection:
data: *data
- description: insertOne
useMultipleMongoses: true
operations:
- name: startTransaction
object: session0
- name: insertOne
object: collection
arguments:
document:
_id: 3
session: session0
result:
insertedId: 3
- name: insertOne
object: collection
arguments:
document:
_id: 4
session: session0
result:
insertedId: 4
- name: insertOne
object: collection
arguments:
document:
_id: 5
session: session0
result:
insertedId: 5
- name: insertOne
object: collection
arguments:
document:
_id: 6
session: session0
result:
insertedId: 6
- name: insertOne
object: collection
arguments:
document:
_id: 7
session: session0
result:
insertedId: 7
- name: insertOne
object: collection
arguments:
document:
_id: 8
session: session0
result:
insertedId: 8
- name: insertOne
object: collection
arguments:
document:
_id: 9
session: session0
result:
insertedId: 9
- name: insertOne
object: collection
arguments:
document:
_id: 10
session: session0
result:
insertedId: 10
- *commitTransaction
outcome:
collection:
data:
- {_id: 1}
- {_id: 2}
- {_id: 3}
- {_id: 4}
- {_id: 5}
- {_id: 6}
- {_id: 7}
- {_id: 8}
- {_id: 9}
- {_id: 10}
- description: mixed read write operations
useMultipleMongoses: true
operations:
- name: startTransaction
object: session0
- name: insertOne
object: collection
arguments:
document:
_id: 3
session: session0
result:
insertedId: 3
- &countDocuments
name: countDocuments
object: collection
arguments:
filter:
_id: 3
session: session0
result: 1
- *countDocuments
- *countDocuments
- *countDocuments
- *countDocuments
- name: insertOne
object: collection
arguments:
document:
_id: 4
session: session0
result:
insertedId: 4
- name: insertOne
object: collection
arguments:
document:
_id: 5
session: session0
result:
insertedId: 5
- name: insertOne
object: collection
arguments:
document:
_id: 6
session: session0
result:
insertedId: 6
- name: insertOne
object: collection
arguments:
document:
_id: 7
session: session0
result:
insertedId: 7
- *commitTransaction
outcome:
collection:
data:
- {_id: 1}
- {_id: 2}
- {_id: 3}
- {_id: 4}
- {_id: 5}
- {_id: 6}
- {_id: 7}
- description: multiple commits
useMultipleMongoses: true
operations:
- name: startTransaction
object: session0
- name: insertMany
object: collection
arguments:
documents:
- _id: 3
- _id: 4
session: session0
result:
insertedIds: {0: 3, 1: 4}
# Session is pinned and remains pinned after successful commits.
- &assertSessionPinned
name: assertSessionPinned
object: testRunner
arguments:
session: session0
- *commitTransaction
- *assertSessionPinned
- *commitTransaction
- *commitTransaction
- *commitTransaction
- *commitTransaction
- *commitTransaction
- *commitTransaction
- *commitTransaction
- *commitTransaction
- *commitTransaction
- *assertSessionPinned
outcome:
collection:
data:
- {_id: 1}
- {_id: 2}
- {_id: 3}
- {_id: 4}
- description: remain pinned after non-transient error on commit
useMultipleMongoses: true
operations:
- name: startTransaction
object: session0
- name: insertMany
object: collection
arguments:
documents:
- _id: 3
- _id: 4
session: session0
result:
insertedIds: {0: 3, 1: 4}
# Session is pinned.
- *assertSessionPinned
# Fail the commit with a non-transient error.
- name: targetedFailPoint
object: testRunner
arguments:
session: session0
failPoint:
configureFailPoint: failCommand
mode: { times: 1 }
data:
failCommands: ["commitTransaction"]
errorCode: 51 # ManualInterventionRequired
- name: commitTransaction
object: session0
result:
errorLabelsOmit: ["TransientTransactionError"]
errorCode: 51
- *assertSessionPinned
# The next commit should succeed.
- name: commitTransaction
object: session0
- *assertSessionPinned
outcome:
collection:
data:
- {_id: 1}
- {_id: 2}
- {_id: 3}
- {_id: 4}
- description: unpin after transient error within a transaction
useMultipleMongoses: true
operations:
- name: startTransaction
object: session0
- name: insertOne
object: collection
arguments:
session: session0
document:
_id: 3
result:
insertedId: 3
# Enable the fail point only on the Mongos that session0 is pinned to.
- name: targetedFailPoint
object: testRunner
arguments:
session: session0
failPoint:
configureFailPoint: failCommand
mode: { times: 1 }
data:
failCommands: ["insert"]
closeConnection: true
- name: insertOne
object: collection
arguments:
session: session0
document:
_id: 4
result:
errorLabelsContain: ["TransientTransactionError"]
errorLabelsOmit: ["UnknownTransactionCommitResult"]
# Session unpins from the first mongos after the insert error and
# abortTransaction succeeds immediately on any mongos.
- name: abortTransaction
object: session0
expectations:
- command_started_event:
command:
insert: *collection_name
documents:
- _id: 3
ordered: true
readConcern:
lsid: session0
txnNumber:
$numberLong: "1"
startTransaction: true
autocommit: false
writeConcern:
command_name: insert
database_name: *database_name
- command_started_event:
command:
insert: *collection_name
documents:
- _id: 4
ordered: true
readConcern:
lsid: session0
txnNumber:
$numberLong: "1"
startTransaction:
autocommit: false
writeConcern:
command_name: insert
database_name: *database_name
- command_started_event:
command:
abortTransaction: 1
lsid: session0
txnNumber:
$numberLong: "1"
startTransaction:
autocommit: false
writeConcern:
recoveryToken: 42
command_name: abortTransaction
database_name: admin
outcome:
collection:
data:
- _id: 1
- _id: 2
# Applications should not run commitTransaction after transient errors but
# the transactions API allows it and this test confirms unpinning behavior.
# In a sharded cluster, a transient error within a transaction unpins the
# session. This way a subsequent abort can "succeed" immediately instead of
# blocking for serverSelectionTimeoutMS in the case the mongos went down.
# However since the abortTransaction helper ignores errors, this test uses
# commitTransaction to prove the session was unpinned.
- description: unpin after transient error within a transaction and commit
useMultipleMongoses: true
clientOptions:
# Increase heartbeatFrequencyMS to avoid the race condition where an in
# flight heartbeat refreshes the first mongoes' SDAM state in between
# the insert connection error and the single commit attempt.
heartbeatFrequencyMS: 30000
operations:
- name: startTransaction
object: session0
- name: insertOne
object: collection
arguments:
session: session0
document:
_id: 3
result:
insertedId: 3
# Enable the fail point only on the Mongos that session0 is pinned to.
# Fail hello/legacy hello to prevent the heartbeat requested directly after the
# insert error from racing with server selection for the commit.
# Note: times: 7 is slightly artbitrary but it accounts for one failed
# insert and some SDAM heartbeats. A test runner will have multiple
# clients connected to this server so this fail point configuration
# is also racy.
- name: targetedFailPoint
object: testRunner
arguments:
session: session0
failPoint:
configureFailPoint: failCommand
mode: { times: 7 }
data:
failCommands: ["insert", "isMaster", "hello"]
closeConnection: true
- name: insertOne
object: collection
arguments:
session: session0
document:
_id: 4
result:
errorLabelsContain: ["TransientTransactionError"]
errorLabelsOmit: ["UnknownTransactionCommitResult"]
# Session unpins from the first mongos after the insert error and
# commitTransaction selects the second mongos which is unaware of the
# transaction and therefore fails with NoSuchTransaction error. If this
# commit succeeds it indicates a bug, either:
# - the driver mistakenly remained pinned even after the insert error, or
# - the test client was initialized with a single mongos seed
#
# Note that the commit attempt should not select the original mongos
# because that server's SDAM state is reset by the connection error,
# heartbeatFrequencyMS is high, and subsequent heartbeats
# should fail.
- name: commitTransaction
object: session0
result:
errorLabelsContain: ["TransientTransactionError"]
errorLabelsOmit: ["UnknownTransactionCommitResult"]
errorCodeName: NoSuchTransaction
expectations:
- command_started_event:
command:
insert: *collection_name
documents:
- _id: 3
ordered: true
readConcern:
lsid: session0
txnNumber:
$numberLong: "1"
startTransaction: true
autocommit: false
writeConcern:
command_name: insert
database_name: *database_name
- command_started_event:
command:
insert: *collection_name
documents:
- _id: 4
ordered: true
readConcern:
lsid: session0
txnNumber:
$numberLong: "1"
startTransaction:
autocommit: false
writeConcern:
command_name: insert
database_name: *database_name
- command_started_event:
command:
commitTransaction: 1
lsid: session0
txnNumber:
$numberLong: "1"
startTransaction:
autocommit: false
writeConcern:
recoveryToken: 42
command_name: commitTransaction
database_name: admin
outcome:
collection:
data:
- _id: 1
- _id: 2