diff --git a/Guides/Transactions.md b/Guides/Transactions.md index dd12469d7..4f2aa5287 100644 --- a/Guides/Transactions.md +++ b/Guides/Transactions.md @@ -8,6 +8,8 @@ Transactions in the driver must be started on a `ClientSession` using `startTran ## Examples +Below are some basic examples of using transactions in `MongoSwift`. In realistic use cases, transactions would ideally be retried when facing transient errors. For more detailed examples featuring retry logic, see the [official MongoDB documentation's examples](https://docs.mongodb.com/manual/core/transactions-in-applications/#txn-core-api). + ### Transaction that Atomically Moves a `Document` from One `MongoCollection` to Another The transaction below atomically deletes the document `{ "hello": "world" }` from the collection `test.src` and inserts the document in the collection `test.dest`. This ensures that the document exists in either `test.src` or `test.dest`, but not both or neither. Executing the delete and insert non-atomically raises the following issues: @@ -15,8 +17,13 @@ The transaction below atomically deletes the document `{ "hello": "world" }` fro - If `deleteOne()` fails and `insertOne()` succeeds, the document exists in both collections. - If `deleteOne()` succeeds and `insertOne()` fails, the document does not exist in either collection. +In order to achieve the highest safety guarantees that MongoDB transactions offer, a "snapshot" read concern and a "majority" write concern must be used. To see the varying levels safety provided by different read concern / write concern configurations, see the [official MongoDB documentation](https://docs.mongodb.com/manual/core/transactions/#read-concern-write-concern-read-preference). + +Transactions will inherit the read concern / write concern / read preference specified on the client that started the transaction's session unless they were also specified in either the default transaction options or in the transaction options passed to `startTransaction`. See the below sections on how to do either. + +**Note:** All operations executed as part of a transaction will use the transaction's read concern / write concern / read preference. Any of those options specified on the database or collection that executes the operation or on a per-operation basis will be _ignored_. ```swift -let client = try MongoClient(using: elg) +let client = try MongoClient(using: elg, options: ClientOptions(readConcern: .snapshot, writeConcern: .majority)) let session = client.startSession() let db = client.db("test") @@ -43,9 +50,9 @@ The default transaction options specified below apply to any transaction started ```swift let txnOpts = TransactionOptions( maxCommitTimeMS: 30, - readConcern: ReadConcern(.local), - readPreference: .primaryPreferred, - writeConcern: try WriteConcern(w: .majority) + readConcern: .snapshot, + readPreference: .primary, + writeConcern: .majority ) let client = try MongoClient(using: elg) @@ -63,7 +70,7 @@ session.startTransaction().flatMap { _ in ### Transaction with Custom Transaction Options -**Note**:: Any transaction options provided directly to `startTransaction()` override the default transaction options for the session. More so, the default transaction options for the session override any options inherited from the client. +**Note**: Any transaction options provided directly to `startTransaction()` override the default transaction options for the session. More so, the default transaction options for the session override any options inherited from the client. ```swift let client = try MongoClient(using: elg) @@ -71,9 +78,9 @@ let session = client.startSession() let txnOpts = TransactionOptions( maxCommitTimeMS: 30, - readConcern: ReadConcern(.local), - readPreference: .primaryPreferred, - writeConcern: try WriteConcern(w: .majority) + readConcern: .snapshot, + readPreference: .primary, + writeConcern: .majority ) session.startTransaction(options: txnOpts).flatMap { _ in @@ -87,4 +94,6 @@ session.startTransaction(options: txnOpts).flatMap { _ in ``` ## See Also -- [MongoDB Transactions documentation](https://docs.mongodb.com/manual/core/transactions/) \ No newline at end of file +- [MongoDB Transactions documentation](https://docs.mongodb.com/manual/core/transactions/) +- [MongoDB Driver Transactions Core API](https://docs.mongodb.com/manual/core/transactions-in-applications/#txn-core-api) +- [MongoDB Transactions and Read Concern / Write Concern / Read Preference](https://docs.mongodb.com/manual/core/transactions/#read-concern-write-concern-read-preference) diff --git a/Sources/MongoSwift/ClientSession.swift b/Sources/MongoSwift/ClientSession.swift index 36ba47891..2f6640294 100644 --- a/Sources/MongoSwift/ClientSession.swift +++ b/Sources/MongoSwift/ClientSession.swift @@ -340,10 +340,18 @@ public final class ClientSession { } /** - * Starts a multi-document transaction for all subsequent operations in this session. Any options provided in - * `options` override the default transaction options for this session and any options inherited from - * `MongoClient`. The transaction must be completed with `commitTransaction` or `abortTransaction`. An in-progress - * transaction is automatically aborted when `ClientSession.end()` is called. + * Starts a multi-document transaction for all subsequent operations in this session. + * + * Any options provided in `options` will override the default transaction options for this session and any options + * inherited from `MongoClient`. + * + * Operations executed as part of the transaction will use the options specified on the transaction, and those + * options cannot be overridden at a per-operation level. Any options that overlap with the transaction options + * which can be specified at a per operation level (e.g. write concern) _will be ignored_ if specified. This + * includes options specified at the database or collection level on the object used to execute an operation. + * + * The transaction must be completed with `commitTransaction` or `abortTransaction`. An in-progress transaction is + * automatically aborted when `ClientSession.end()` is called. * * - Parameters: * - options: The options to use when starting this transaction diff --git a/Sources/MongoSwift/Operations/StartSessionOperation.swift b/Sources/MongoSwift/Operations/StartSessionOperation.swift index 1ab368b85..0e27eb4f9 100644 --- a/Sources/MongoSwift/Operations/StartSessionOperation.swift +++ b/Sources/MongoSwift/Operations/StartSessionOperation.swift @@ -9,6 +9,11 @@ public struct ClientSessionOptions { public var causalConsistency: Bool? /// The default `TransactionOptions` to use for transactions started on this session. + /// + /// These may be overridden by options provided directly to `ClientSession.startTransaction`. + /// + /// If this option is not specified, the options will be inherited from the client that started this session where + /// applicable (e.g. write concern). public var defaultTransactionOptions: TransactionOptions? /// Convenience initializer allowing any/all parameters to be omitted. diff --git a/Sources/MongoSwiftSync/ClientSession.swift b/Sources/MongoSwiftSync/ClientSession.swift index cbedba157..01a436e50 100644 --- a/Sources/MongoSwiftSync/ClientSession.swift +++ b/Sources/MongoSwiftSync/ClientSession.swift @@ -98,10 +98,18 @@ public final class ClientSession { } /** - * Starts a multi-document transaction for all subsequent operations in this session. Any options provided in - * `options` override the default transaction options for this session and any options inherited from - * `MongoClient`. The transaction must be completed with `commitTransaction` or `abortTransaction`. An in-progress - * transaction is automatically aborted when `ClientSession` goes out of scope. + * Starts a multi-document transaction for all subsequent operations in this session. + * + * Any options provided in `options` will override the default transaction options for this session and any options + * inherited from `MongoClient`. + * + * Operations executed as part of the transaction will use the options specified on the transaction, and those + * options cannot be overridden at a per-operation level. Any options that overlap with the transaction options + * which can be specified at a per operation level (e.g. write concern) _will be ignored_ if specified. This + * includes options specified at the database or collection level on the object used to execute an operation. + * + * The transaction must be completed with `commitTransaction` or `abortTransaction`. An in-progress transaction is + * automatically aborted when `ClientSession.end()` is called. * * - Parameters: * - options: The options to use when starting this transaction