Session.startTransaction()

Definition

  • Session.startTransaction()

New in version 4.0.

Starts a multi-document transactionassociated with the session. At any given time, you can have at mostone open transaction for a session.

Availability

  • In version 4.0, MongoDB supports multi-documenttransactions on replica sets.
  • In version 4.2, MongoDB introduces distributedtransactions, which adds support for multi-documenttransactions on sharded clusters and incorporates the existingsupport for multi-document transactions on replica sets.

Important

Within a transaction, you can only specify read and write (CRUD)operations on existing collections. For example, amulti-document transaction cannot include an insert operationthat would result in the creation of a new collection.

The Session.startTransaction() method can take a documentfollowing options:

  1. { readConcern: { level: <level>}, writeConcern: { w: <value>, j: <boolean>, wtimeout: <number> } }

OptionDescriptionreadConcernOptional. A document that specifies the read concern for all operations in the transaction,overriding operation-specific read concern.

You can specify one of the following read concern levels:

The operations within the transaction use "w:1", overriding operation-specific write concern.

If you commit using "w: 1" writeconcern, your transaction can be rolled back during thefailover process.

For MongoDB Drivers, transactions use the client-level writeconcern as the default.

Behavior

Operations Supported within a Transaction

Note

If running with access control, youmust have privileges for the operations in the transaction.

For multi-document transactions:

  • You can specify read/write (CRUD) operations on existingcollections. The collections can be in different databases. For alist of CRUD operations, see CRUD Operations.
  • You cannot write to cappedcollections. (Starting in MongoDB 4.2)
  • You cannot read/write to collections in the config, admin,or local databases.
  • You cannot write to system.* collections.
  • You cannot return the supported operation’s query plan (i.e. explain).
  • For cursors created outside of a transaction, you cannot callgetMore inside the transaction.
  • For cursors created in a transaction, you cannot callgetMore outside the transaction.
  • Starting in MongoDB 4.2, you cannot specify killCursors asthe first operation in a transaction.
MethodCommandNote
db.collection.aggregate()aggregateExcluding the following stages:- $collStats- $currentOp- $indexStats- $listLocalSessions- $listSessions- $merge- $out- $planCacheStats
db.collection.countDocuments() Excluding the following query operator expressions:- $where- $near- $nearSphereThe method uses the $match aggregation stage for thequery and $group aggregation stage with a$sum expression to perform the count.
db.collection.distinct()distinctAvailable on unsharded collections.For sharded collections, use the aggregation pipeline with the$group stage. See Distinct Operation.
db.collection.find()find
geoSearch
db.collection.deleteMany()db.collection.deleteOne()db.collection.remove()delete
db.collection.findOneAndDelete()db.collection.findOneAndReplace()db.collection.findOneAndUpdate()findAndModifyFor upsert, only when run against an existing collection.
db.collection.insertMany()db.collection.insertOne()db.collection.insert()insertOnly when run against an existing collection.
db.collection.save() If an insert, only when run against an existing collection.
db.collection.updateOne()db.collection.updateMany()db.collection.replaceOne()db.collection.update()updateFor upsert, only when run against an existing collection.
db.collection.bulkWrite()Various Bulk Operation Methods For insert operations, only when run against an existing collection.For upsert, only when run against an existing collection.

Operations that affect the database catalog, such as creating ordropping a collection or an index, are not allowed in multi-documenttransactions. For example, a multi-document transaction cannot includean insert operation that would result in the creation of a newcollection. See Restricted Operations.

Informational commands, such as isMaster,buildInfo, connectionStatus (and theirhelper methods) are allowed in transactions; however, they cannot bethe first operation in the transaction.

Read Preference

Transactions support read preferenceprimary.

Atomicity

While the transaction is open, no data changes made by operations inthe transaction is visible outside the transaction:

  • When a transaction commits, all data changes made in the transactionare saved and visible outside the transaction. That is, a transactionwill not commit some of its changes while rolling back others.

Until a transaction commits, the data changes made in thetransaction are not visible outside the transaction.

However, when a transaction writes to multiple shards, not alloutside read operations need to wait for the result of the committedtransaction to be visible across the shards. For example, if atransaction is committed and write 1 is visible on shard A but write2 is not yet visible on shard B, an outside read at read concern"local" can read the results of write 1 withoutseeing write 2.

  • When a transaction aborts, all data changes made by the writes in thetransaction are discarded without ever becoming visible and thetransaction ends.

Example

Consider a scenario where as changes are made to an employee’s recordin the hr database, you want to ensure that the eventscollection in the reporting database are in sync with the hrchanges. That is, you want to ensure that these writes are done as asingle transaction, such that either both operations succeed or fail.

The employees collection in the hr database has the followingdocuments:

  1. { "_id" : ObjectId("5af0776263426f87dd69319a"), "employee" : 3, "name" : { "title" : "Mr.", "name" : "Iba Ochs" }, "status" : "Active", "department" : "ABC" }
  2. { "_id" : ObjectId("5af0776263426f87dd693198"), "employee" : 1, "name" : { "title" : "Miss", "name" : "Ann Thrope" }, "status" : "Active", "department" : "ABC" }
  3. { "_id" : ObjectId("5af0776263426f87dd693199"), "employee" : 2, "name" : { "title" : "Mrs.", "name" : "Eppie Delta" }, "status" : "Active", "department" : "XYZ" }

The events collection in the reporting database has thefollowing documents:

  1. { "_id" : ObjectId("5af07daa051d92f02462644a"), "employee" : 1, "status" : { "new" : "Active", "old" : null }, "department" : { "new" : "ABC", "old" : null } }
  2. { "_id" : ObjectId("5af07daa051d92f02462644b"), "employee" : 2, "status" : { "new" : "Active", "old" : null }, "department" : { "new" : "XYZ", "old" : null } }
  3. { "_id" : ObjectId("5af07daa051d92f02462644c"), "employee" : 3, "status" : { "new" : "Active", "old" : null }, "department" : { "new" : "ABC", "old" : null } }

The following example opens a transaction, updates an employee’s statusto Inactive in the employees status and inserts a correspondingdocument to the events collection, and commits the two operationsas a single transaction.

  1. // Runs the txnFunc and retries if TransientTransactionError encountered
  2.  
  3. function runTransactionWithRetry(txnFunc, session) {
  4. while (true) {
  5. try {
  6. txnFunc(session); // performs transaction
  7. break;
  8. } catch (error) {
  9. // If transient error, retry the whole transaction
  10. if ( error.hasOwnProperty("errorLabels") && error.errorLabels.includes("TransientTransactionError") ) {
  11. print("TransientTransactionError, retrying transaction ...");
  12. continue;
  13. } else {
  14. throw error;
  15. }
  16. }
  17. }
  18. }
  19.  
  20. // Retries commit if UnknownTransactionCommitResult encountered
  21.  
  22. function commitWithRetry(session) {
  23. while (true) {
  24. try {
  25. session.commitTransaction(); // Uses write concern set at transaction start.
  26. print("Transaction committed.");
  27. break;
  28. } catch (error) {
  29. // Can retry commit
  30. if (error.hasOwnProperty("errorLabels") && error.errorLabels.includes("UnknownTransactionCommitResult") ) {
  31. print("UnknownTransactionCommitResult, retrying commit operation ...");
  32. continue;
  33. } else {
  34. print("Error during commit ...");
  35. throw error;
  36. }
  37. }
  38. }
  39. }
  40.  
  41. // Updates two collections in a transactions
  42.  
  43. function updateEmployeeInfo(session) {
  44. employeesCollection = session.getDatabase("hr").employees;
  45. eventsCollection = session.getDatabase("reporting").events;
  46.  
  47. session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } );
  48.  
  49. try{
  50. employeesCollection.updateOne( { employee: 3 }, { $set: { status: "Inactive" } } );
  51. eventsCollection.insertOne( { employee: 3, status: { new: "Inactive", old: "Active" } } );
  52. } catch (error) {
  53. print("Caught exception during transaction, aborting.");
  54. session.abortTransaction();
  55. throw error;
  56. }
  57.  
  58. commitWithRetry(session);
  59. }
  60.  
  61. // Start a session.
  62. session = db.getMongo().startSession( { readPreference: { mode: "primary" } } );
  63.  
  64. try{
  65. runTransactionWithRetry(updateEmployeeInfo, session);
  66. } catch (error) {
  67. // Do something with error
  68. } finally {
  69. session.endSession();
  70. }

See also