Skip to content

Commit b6b0626

Browse files
authored
(DOCSP-45012) Transactions (#71) (#92)
* (DOCSP-45012) Added file. * (DOCSP-45012) Added methods to page. * (DOCSP-45012) Added code. * (DOCSP-45012) Edits. * (DOCSP-45012) Fix list formatting. * (DOCSP-45012) Fix list formatting. * (DOCSP-45012) Added examples for manual transactions and moved transactions page under Write. * (DOCSP-45012) Edits. * (DOCSP-45012) Edits. * (DOCSP-45012) Edits. * (DOCSP-45012) Edits. * (DOCSP-45012) Edits. * (DOCSP-45012) Editing instructions for transactions. * (DOCSP-45012) Edits. * (DOCSP-45012) Formatting. * (DOCSP-45012) Wording. * (DOCSP-45012) Edit introduction to code examples. * (DOCSP-45012) Edit. * (DOCSP-45012) Edits. * (DOCSP-45012) Edits> * (DOCSP-45012) Edits. * (DOCSP-45012) Fix links.g * (DOCSP-45012) Edits + vale fix. * (DOCSP-45012) Vale error fix. * (DOCSP-45012) Edit. * (DOCSP-45012) Add refs. * (DOCSP-45012) Fix links. * (DOCSP-45012) @mayaraman19 Review feedback. * (DOCSP-45012) Clean up. * (DOCSP-45012) Clean up. * (DOCSP-45012) @mayaraman19 Final round of review edits. * (DOCSP-45012) Edits.
1 parent 54d862d commit b6b0626

File tree

3 files changed

+356
-0
lines changed

3 files changed

+356
-0
lines changed
+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#include <iostream>
2+
3+
#include <bsoncxx/builder/basic/document.hpp>
4+
#include <bsoncxx/json.hpp>
5+
#include <mongocxx/client.hpp>
6+
#include <mongocxx/exception/exception.hpp>
7+
#include <mongocxx/instance.hpp>
8+
#include <mongocxx/uri.hpp>
9+
#include <mongocxx/exception/operation_exception.hpp>
10+
11+
using bsoncxx::builder::basic::kvp;
12+
using bsoncxx::builder::basic::make_document;
13+
14+
int main() {
15+
16+
{
17+
// start-callback-api
18+
// Establish a connection to the MongoDB deployment
19+
mongocxx::instance instance{};
20+
mongocxx::client client(mongocxx::uri{"<connectionString>"});
21+
22+
// Define database and collection variables
23+
auto db = client["sample_mflix"];
24+
auto movies_collection = db["movies"];
25+
auto comments_collection = db["comments"];
26+
27+
// Define a callback specifying the sequence of operations to perform during the transaction
28+
mongocxx::client_session::with_transaction_cb callback = [&](mongocxx::client_session* session) {
29+
// Important:: You must pass the session to the operations.
30+
movies_collection.insert_one(*session, make_document(kvp("title", "Parasite")));
31+
comments_collection.insert_one(*session, make_document(kvp("name", "Anjali Patel"),
32+
kvp("text", "This is my new favorite movie!")));
33+
};
34+
35+
// Define an options instance to explicitly set the write concern for the transaction operations
36+
mongocxx::options::transaction opts;
37+
mongocxx::write_concern wc;
38+
wc.acknowledge_level(mongocxx::write_concern::level::k_majority);
39+
opts.write_concern(wc);
40+
41+
// Start a client session
42+
auto session = client.start_session();
43+
44+
try {
45+
// Start a transaction, execute the operations in the callback function, and commit the results
46+
session.with_transaction(callback, opts);
47+
} catch (const mongocxx::exception& e) {
48+
std::cout << "An exception occurred: " << e.what() << std::endl;
49+
return EXIT_FAILURE;
50+
}
51+
52+
return EXIT_SUCCESS;
53+
// end-callback-api
54+
}
55+
{
56+
// start-core-api
57+
// Establish a connection to the MongoDB deployment
58+
mongocxx::instance instance{};
59+
mongocxx::client client(mongocxx::uri{"<connectionString>"});
60+
61+
// Runs the txn_func and retries if TransientTransactionError occurs
62+
using transaction_func = std::function<void(mongocxx::client_session& session)>;
63+
auto run_with_retry = [](transaction_func txn_func,
64+
mongocxx::client_session& session) {
65+
while (true) {
66+
try {
67+
txn_func(session); // performs transaction.
68+
break;
69+
} catch (const mongocxx::operation_exception& oe) {
70+
std::cout << "Transaction aborted. Caught exception during transaction."
71+
<< std::endl;
72+
// If transient error, retry the whole transaction.
73+
if (oe.has_error_label("TransientTransactionError")) {
74+
std::cout << "TransientTransactionError, retrying transaction..."
75+
<< std::endl;
76+
continue;
77+
} else {
78+
throw oe;
79+
}
80+
}
81+
}
82+
};
83+
84+
// Commits the active transaction and retries commit if UnknownTransactionCommitResult occurs
85+
auto commit_with_retry = [](mongocxx::client_session& session) {
86+
while (true) {
87+
try {
88+
session.commit_transaction(); // Uses write concern set at transaction start.
89+
std::cout << "Transaction committed."
90+
<< std::endl;
91+
break;
92+
} catch (const mongocxx::operation_exception& oe) {
93+
// Can retry commit
94+
if (oe.has_error_label("UnknownTransactionCommitResult")) {
95+
std::cout << "UnknownTransactionCommitResult, retrying commit..."
96+
<< std::endl;
97+
continue;
98+
} else {
99+
std::cout << "Error during commit..."
100+
<< std::endl;
101+
throw oe;
102+
}
103+
}
104+
}
105+
106+
};
107+
108+
auto txn_func = [&](mongocxx::client_session& session) {
109+
auto& client = session.client();
110+
111+
// Define database and collection variables
112+
auto db = client["sample_mflix"];
113+
auto movies_collection = db["movies"];
114+
auto comments_collection = db["comments"];
115+
116+
// Define an options instance to explicitly set the write concern for the transaction operations
117+
mongocxx::options::transaction opts;
118+
mongocxx::write_concern wc;
119+
wc.acknowledge_level(mongocxx::write_concern::level::k_majority);
120+
opts.write_concern(wc);
121+
122+
session.start_transaction(opts);
123+
124+
// Attempt to insert documents into database collections
125+
try {
126+
movies_collection.insert_one(session, make_document(kvp("title", "Parasite")));
127+
comments_collection.insert_one(session, make_document(kvp("name", "Anjali Patel"),
128+
kvp("text", "This is my new favorite movie!")));
129+
} catch (const mongocxx::operation_exception& oe) {
130+
std::cout << "Caught exception during transaction, aborting."
131+
<< std::endl;
132+
session.abort_transaction();
133+
throw oe;
134+
}
135+
136+
commit_with_retry(session);
137+
};
138+
139+
// Start a client session
140+
auto session = client.start_session();
141+
142+
try {
143+
run_with_retry(txn_func, session);
144+
} catch (const mongocxx::operation_exception& oe) {
145+
// Do something with error
146+
throw oe;
147+
}
148+
// end-core-api
149+
}
150+
}
151+

source/write.txt

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Write Data to MongoDB
2828
Delete </write/delete>
2929
Bulk Write </write/bulk-write>
3030
GridFS </write/gridfs>
31+
Transactions </write/transactions>
3132

3233
Overview
3334
--------

source/write/transactions.txt

+204
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
.. _cpp-transactions:
2+
3+
============
4+
Transactions
5+
============
6+
7+
.. facet::
8+
:name: genre
9+
:values: reference
10+
11+
.. meta::
12+
:keywords: code example, rollback, undo operation
13+
14+
.. contents:: On this page
15+
:local:
16+
:backlinks: none
17+
:depth: 2
18+
:class: singlecol
19+
20+
Overview
21+
--------
22+
23+
In this guide, you can learn how to use the {+driver-long+} to perform
24+
**transactions**. Transactions allow
25+
you to run a series of operations that do not change any data until the
26+
transaction is committed. If any operation in the transaction returns an
27+
error, the driver cancels the transaction and discards all data changes
28+
before they ever become visible.
29+
30+
In MongoDB, transactions run within logical **sessions**. A
31+
session is a grouping of related read or write operations that you intend to run sequentially.
32+
Sessions enable causal consistency for a group of operations in an **ACID-compliant** transaction, which is a
33+
transaction that meets an expectation of atomicity, consistency, isolation, and durability. MongoDB
34+
guarantees that the data involved in your transaction operations remains
35+
consistent, even if the operations encounter unexpected errors.
36+
37+
When using the {+driver-short+}, you can create a new session from a ``mongocxx::client`` instance.
38+
Then, you can use the resulting ``mongocxx::client_session`` instance to perform transactions.
39+
We recommend that you reuse your client for multiple sessions and transactions instead of
40+
instantiating a new client each time.
41+
42+
.. warning::
43+
44+
Use a ``mongocxx::client_session`` only with the ``mongocxx::client`` that created it.
45+
Using a ``client_session`` with a different ``client`` results in operation errors.
46+
47+
.. important::
48+
49+
Instances of ``mongocxx::client`` are not thread-safe.
50+
Each ``mongoxcc::client`` instance and its child instances, including ``mongocxx::client_session``, should be used by a single thread at a time.
51+
To learn more, see the :ref:`Thread and Fork Safety <cpp-thread-safety>` guide.
52+
53+
.. _cpp-transaction-apis:
54+
55+
Transaction APIs
56+
----------------
57+
58+
The {+driver-long+} provides a callback API and a core API to manage the transaction lifestyle.
59+
Before you begin a transaction, you must call the ``start_session()`` method to instantiate a ``mongocxx::client_session``.
60+
Then, you can use either of the following APIs to perform a transaction:
61+
62+
- :ref:`Callback API <cpp-callback-api>`: High-level API that manages the life cycle of the transaction and automatically incorporates error handling logic.
63+
- :ref:`Core API <cpp-core-api>`: Low-level API that allows you to manage the life cycle of the transaction and implement custom error handling logic.
64+
65+
.. tip::
66+
67+
To learn more about error handling, see the :manual:`Transaction Error Handling </core/transactions-in-applications/#transaction-error-handling>` section in the {+mdb-server+} manual.
68+
69+
.. _cpp-callback-api:
70+
71+
Callback API
72+
~~~~~~~~~~~~
73+
74+
Use the callback API to allow the {+driver-long+} to manage the life cycle of your transaction.
75+
To implement this API, call the ``with_transaction()`` method on your ``mongocxx::client_session`` and pass in a
76+
callback function specifying the sequence of operations you want to run. The ``with_transaction()`` method starts a transaction, executes the callback function, and
77+
either commits your transaction or ends the transaction if it encounters an error. If your transaction encounters a ``TransientTransactionError`` or ``UnknownTransactionCommitResult`` error, the
78+
``with_transaction()`` method reruns the transaction.
79+
80+
The following code uses the callback API to perform a transaction that inserts documents into the ``movies`` and ``comments`` collections in the ``sample_mflix`` database.
81+
This code performs the following actions:
82+
83+
1. Starts a session from the client using the ``start_session()`` method.
84+
#. Defines a callback function that specifies the operations to perform during the transaction.
85+
#. Creates an option object to prepare to set the write concern for the transaction operations.
86+
To learn more about read and write semantics, see the :manual:`Read Concern/Write Concern/Read Preference </core/transactions/#read-concern-write-concern-read-preference>`
87+
section in the {+mdb-server+} manual.
88+
#. Calls the ``with_transaction()`` method to manage the transaction, passing the callback function and option object as arguments.
89+
90+
.. literalinclude:: /includes/write/transactions.cpp
91+
:language: cpp
92+
:dedent:
93+
:start-after: start-callback-api
94+
:end-before: end-callback-api
95+
96+
.. _cpp-core-api:
97+
98+
Core API
99+
~~~~~~~~
100+
101+
Use the core API to manage the life cycle of your transaction. To implement this API, you must make explicit calls to methods in the ``mongocxx::client_session`` interface
102+
to start a transaction, commit an active transaction, and end a transaction if an error occurs. The core API doesn't automatically incorporate error handling logic,
103+
and instead allows you to implement custom handling logic for errors including ``TransientTransactionError`` and ``UnknownTransactionCommitResult``.
104+
105+
The following table describes the core API methods provided by the ``mongocxx::client_session`` interface:
106+
107+
.. list-table::
108+
:widths: 25 75
109+
:header-rows: 1
110+
111+
* - Method
112+
- Description
113+
114+
* - ``start_transaction()``
115+
- | Starts a new transaction on the current client session. Accepts an optional ``mongocxx::options::transaction``
116+
instance as an argument to set options. For a full list of options, see `mongocxx::options::transaction <{+api+}/classmongocxx_1_1v__noabi_1_1options_1_1transaction.html>`__
117+
in the API documentation.
118+
|
119+
| Raises an exception if the options are misconfigured, if there are network or other transient failures, or if there
120+
are other errors such as a session with a transaction already in progress. If an error is returned with the ``TransientTransactionError`` label,
121+
you can end the transaction and then retry it with the expectation that it will succeed.
122+
|
123+
| To learn more about this method, see the :manual:`startTransaction()
124+
</reference/method/Session.startTransaction/>` guide in the {+mdb-server+} manual.
125+
126+
* - ``commit_transaction()``
127+
- | Commits the active transaction on the current client session.
128+
|
129+
| Raises an exception if options are misconfigured, if there are network or other transient failures,
130+
or if there are other errors such as a session with no transaction in progress. If an error is returned with the ``UnknownTransactionCommitResult`` label,
131+
you can end the transaction and then retry it with the expectation that it will succeed when the committed transaction satisfies the set write concern.
132+
|
133+
| To learn more about this method, see the :manual:`commitTransaction()
134+
</reference/method/Session.commitTransaction/>` guide in the {+mdb-server+} manual.
135+
136+
* - ``abort_transaction()``
137+
- | Ends the active transaction on the current client session.
138+
|
139+
| Raises an exception if the options are misconfigured or if there are other errors such as
140+
a session with no transaction in progress.
141+
|
142+
| To learn more about this method, see the :manual:`abortTransaction()
143+
</reference/method/Session.abortTransaction/>` guide in the {+mdb-server+} manual.
144+
145+
.. tip::
146+
147+
The ``mongocxx::client_session`` class also provides methods to retrieve and modify session properties.
148+
To learn more, see `mongocxx::client_session <{+api+}/classmongocxx_1_1v__noabi_1_1client__session.html>`__ in the API documentation.
149+
150+
The following code uses the core API to perform a transaction that inserts documents into the ``movies`` and ``comments`` collections in the ``sample_mflix`` database.
151+
This code performs the following actions:
152+
153+
1. Starts a session from the client using the ``start_session()`` method.
154+
#. Creates an option object to prepare to set the write concern for the transaction operations.
155+
To learn more about read and write semantics, see the :manual:`Read Concern/Write Concern/Read Preference </core/transactions/#read-concern-write-concern-read-preference>`
156+
section in the {+mdb-server+} manual.
157+
#. Calls the ``start_transaction()`` method to start a transaction, passing in the option object as an argument.
158+
#. Runs operations to insert documents into collections in the ``sample_mflix`` database, passing the active
159+
session to each operation.
160+
If an operation encounters an error, the whole transaction is aborted. If the error has the label ``TransientTransactionError``,
161+
the transaction is retried.
162+
#. Commits the active transaction using the ``commit_transaction()`` method. If the commit encounters an error with the label ``UnknownTransactionCommitResult``, the commit is retried.
163+
164+
.. literalinclude:: /includes/write/transactions.cpp
165+
:language: cpp
166+
:dedent:
167+
:start-after: start-core-api
168+
:end-before: end-core-api
169+
170+
.. _cpp-transactions-addtl-info:
171+
172+
Additional Information
173+
----------------------
174+
175+
To learn more about the concepts discussed in this guide, see the following pages in the
176+
{+mdb-server+} manual:
177+
178+
- :manual:`Transactions </core/transactions/>`
179+
- :manual:`Drivers API </core/transactions-in-applications/>`
180+
- :manual:`Server Sessions </reference/server-sessions/>`
181+
- :manual:`Causal Consistency </core/read-isolation-consistency-recency/#causal-consistency>`
182+
183+
To learn more about ACID complicance, see the :website:`ACID Properties in Database Management Systems </basics/acid-transactions>`
184+
guide on the MongoDB website.
185+
186+
To learn more about insert operations, see the :ref:`Insert Documents <cpp-write-insert>` guide.
187+
188+
.. _api-docs-transaction:
189+
190+
API Documentation
191+
~~~~~~~~~~~~~~~~~
192+
193+
To learn more about any of the types or methods discussed in this
194+
guide, see the following API Documentation:
195+
196+
- `mongocxx::client <{+api+}/classmongocxx_1_1v__noabi_1_1client.html>`__
197+
- `mongocxx::client_session <{+api+}/classmongocxx_1_1v__noabi_1_1options_1_1transaction.html>`__
198+
- `mongocxx::options::transaction <{+api+}/classmongocxx_1_1options_1_1transaction.html>`__
199+
- `start_session() <{+api+}/classmongocxx_1_1v__noabi_1_1client.html#a03535128dbe5be973a08764b7741f24e>`__
200+
- `with_transaction() <{+api+}/classmongocxx_1_1v__noabi_1_1client__session.html#a3dcdf91bf72e69bf7e63a7c8ad859b93>`__
201+
- `start_transaction() <{+api+}/classmongocxx_1_1v__noabi_1_1client__session.html#a9cc1c32d80a6cb1b0b21001d7990111b>`__
202+
- `commit_transaction() <{+api+}/classmongocxx_1_1v__noabi_1_1client__session.html#ad2d1a8f7c77542db6ec7629d162761ff>`__
203+
- `abort_transaction() <{+api+}/classmongocxx_1_1v__noabi_1_1client__session.html#aabb8247a655741af6fe0f78bef8116e1>`__
204+
- `insert_one() <{+api+}/classmongocxx_1_1v__noabi_1_1collection.html#a839bedb5505e5ce75cbf384e2e2457bd>`__

0 commit comments

Comments
 (0)