From 0df006c3b042c2faf00a190387e4f8116f392c7a Mon Sep 17 00:00:00 2001 From: Patrick Freed Date: Wed, 20 May 2020 14:26:19 -0400 Subject: [PATCH 1/3] add async transactions examples --- .../Docs/Sources/AsyncExamples/main.swift | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/Examples/Docs/Sources/AsyncExamples/main.swift b/Examples/Docs/Sources/AsyncExamples/main.swift index 8efe3e981..199d6db9d 100644 --- a/Examples/Docs/Sources/AsyncExamples/main.swift +++ b/Examples/Docs/Sources/AsyncExamples/main.swift @@ -147,3 +147,81 @@ private func changeStreams() throws { // End Changestream Example 4 } } + +/// Examples used for the MongoDB documentation on transactions. +/// - SeeAlso: https://docs.mongodb.com/manual/core/transactions-in-applications/ +private func transactions() throws { + let elg = MultiThreadedEventLoopGroup(numberOfThreads: 1) + let client = try MongoClient(using: elg) + + // Start Transactions Into Example 1 + func updateEmployeeInfo(session: ClientSession) -> EventLoopFuture { + let employees = client.db("hr").collection("employees") + let events = client.db("reporting").collection("events") + + let options = TransactionOptions(readConcern: .snapshot, writeConcern: .majority) + return session.startTransaction(options: options).flatMap { + employees.updateOne( + filter: ["employee": 3], + update: ["$set": ["status": "Inactive"]], + session: session + ).flatMap { _ in + events.insertOne(["employee": 3, "status": ["new": "Inactive", "old": "Active"]]) + }.flatMapError { error in + print("Caught error during transaction, aborting") + return session.abortTransaction().flatMapThrowing { _ in + throw error + } + } + }.flatMap { _ in + commitWithRetry(session: session) + } + } + // End Transactions Intro Example 1 + + // Start Transactions Retry Example 1 + func runTransactionWithRetry( + session: ClientSession, + txnFunc: @escaping (ClientSession) -> EventLoopFuture + ) -> EventLoopFuture { + let txnFuture = txnFunc(session) + let eventLoop = txnFuture.eventLoop + return txnFuture.flatMapError { error in + guard + let labeledError = error as? LabeledError, + labeledError.errorLabels?.contains("TransientTransactionError") == true + else { + return eventLoop.makeFailedFuture(error) + } + print("TransientTransactionError, retrying transaction...") + return runTransactionWithRetry(session: session, txnFunc: txnFunc) + } + } + // End Transactions Retry Example 1 + + // Start Transactions Retry Example 2 + func commitWithRetry(session: ClientSession) -> EventLoopFuture { + let commitFuture = session.commitTransaction() + let eventLoop = commitFuture.eventLoop + return commitFuture.flatMapError { error in + guard + let labeledError = error as? LabeledError, + labeledError.errorLabels?.contains("UnknownTransactioncommitResult") == true + else { + print("Error during commit...") + return eventLoop.makeFailedFuture(error) + } + print("UnknownTransactioncommitResult, retrying commit operation...") + return commitWithRetry(session: session) + } + } + // End Transactions Retry Example 2 + + // Start Transactions Retry Example 3 + try client.withSession { session in + runTransactionWithRetry(session: session, txnFunc: updateEmployeeInfo).flatMapErrorThrowing { _ in + // do something with error + } + }.wait() + // End Transactions Retry Example 3 +} From 909038d733fd04e95c613d9d49422f68d26b0b4d Mon Sep 17 00:00:00 2001 From: Patrick Freed Date: Wed, 20 May 2020 15:07:00 -0400 Subject: [PATCH 2/3] add sync transactions examples --- Examples/Docs/Sources/SyncExamples/main.swift | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/Examples/Docs/Sources/SyncExamples/main.swift b/Examples/Docs/Sources/SyncExamples/main.swift index f423b16d4..74b223eac 100644 --- a/Examples/Docs/Sources/SyncExamples/main.swift +++ b/Examples/Docs/Sources/SyncExamples/main.swift @@ -90,3 +90,78 @@ private func changeStreams() throws { // End Changestream Example 4 } } + +/// Examples used for the MongoDB documentation on transactions. +/// - SeeAlso: https://docs.mongodb.com/manual/core/transactions-in-applications/ +private func transactions() throws { + // Start Transactions Intro Example 1 + func updateEmployeeInfo(session: ClientSession) throws { + let employees = session.client.db("hr").collection("employees") + let events = session.client.db("reporting").collection("events") + + do { + try employees.updateOne(filter: ["employee": 3], update: ["$set": ["status": "Inactive"]], session: session) + try events.insertOne(["employee": 3, "status": ["new": "Inactive", "old": "Active"]], session: session) + } catch { + print("Caught error during transaction, aborting") + try session.abortTransaction() + throw error + } + try commitWithRetry(session: session) + } + // End Transactions Intro Example 1 + + // Start Transactions Retry Example 1 + func runTransactionWithRetry(session: ClientSession, txnFunc: @escaping (ClientSession) throws -> Void) throws { + while true { + do { + return try txnFunc(session) // performs transaction + } catch { + print("Transaction aborted. Caught exception during transaction.") + guard + let labeledError = error as? LabeledError, + labeledError.errorLabels?.contains("TransientTransactionError") == true + else { + throw error + } + // If transient error, retry the whole transaction + print("TransientTransactionError, retrying transaction ...") + continue + } + } + } + // End Transactions Retry Example 1 + + // Start Transactions Retry Example 2 + func commitWithRetry(session: ClientSession) throws { + while true { + do { + try session.commitTransaction() // Uses write concern set at transaction start + print("Transaction committed.") + break + } catch { + guard + let labeledError = error as? LabeledError, + labeledError.errorLabels?.contains("UnknownTransactionCommitResult") == true + else { + print("Error during commit ...") + throw error + } + print("UnknownTransactionCommitResult, retrying commit operation ...") + continue + } + } + } + // End Transactions Retry Example 2 + + let client = try MongoClient() + // Start Transactions Retry Example 3 + client.withSession { session in + do { + try runTransactionWithRetry(session: session, txnFunc: updateEmployeeInfo) + } catch { + // do something with error + } + } + // End Transactions Retry Example 3 +} From 45963ce0209982532a68e9bb97ccfacc2d8ccee0 Mon Sep 17 00:00:00 2001 From: Patrick Freed Date: Thu, 21 May 2020 12:13:10 -0400 Subject: [PATCH 3/3] fix capitalization --- Examples/Docs/Sources/AsyncExamples/main.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Examples/Docs/Sources/AsyncExamples/main.swift b/Examples/Docs/Sources/AsyncExamples/main.swift index 199d6db9d..6c114efe2 100644 --- a/Examples/Docs/Sources/AsyncExamples/main.swift +++ b/Examples/Docs/Sources/AsyncExamples/main.swift @@ -206,12 +206,12 @@ private func transactions() throws { return commitFuture.flatMapError { error in guard let labeledError = error as? LabeledError, - labeledError.errorLabels?.contains("UnknownTransactioncommitResult") == true + labeledError.errorLabels?.contains("UnknownTransactionCommitResult") == true else { print("Error during commit...") return eventLoop.makeFailedFuture(error) } - print("UnknownTransactioncommitResult, retrying commit operation...") + print("UnknownTransactionCommitResult, retrying commit operation...") return commitWithRetry(session: session) } }