Callback API と Core API
トランザクションを開始し、指定された操作を実行し、コミット(エラーの場合は中止)します。
TransientTransactionErrorおよびUnknownTransactionCommitResultのエラー処理ロジックが自動的に組み込まれます。
トランザクションを開始し、トランザクションをコミットするには、明示的な呼び出しが必要です。
TransientTransactionErrorおよびUnknownTransactionCommitResultのエラー処理ロジックを組み込まず、代わりにこれらのエラーに対するカスタム エラー処理を組み込む柔軟性を提供します。
Callback API
Callback API には次のロジックが組み込まれています。
トランザクションで
TransientTransactionErrorエラーが発生した場合にトランザクション全体を再試行する方法。コミットで
UnknownTransactionCommitResultエラーが発生した場合にコミット操作を再試行する方法
MongoDB 6.0.5以降、サーバーはTransactionTooLargeForCacheエラーを受信してもトランザクションを再試行しません。
例
➤ 右上の言語の選択のドロップダウンメニューを使用して、このページの例の言語を設定します。
重要
ご使用の MongoDB バージョン向けの MongoDB ドライバーを使用してください。
ドライバーを使用する場合、トランザクション内の各操作によってセッションがそれぞれの操作に渡される必要があります。
トランザクション内の操作では、 トランザクションレベルの読み取り保証(read concern ) 、トランザクションレベルの書込み保証(write concern ) 、トランザクションレベルの読み込み設定(read preference) が使用されます。
トランザクション内で暗黙的または明示的にコレクションを作成できます。 「トランザクション内でのコレクションとインデックスの作成 」を参照してください。
この例では、トランザクションを操作するために新しいCallback APIを使用します。これにより、トランザクションが開始され、指定された操作が実行され、コミット(エラーの場合は中止)されます。 新しいCallback APIには、 TransientTransactionErrorまたはUnknownTransactionCommitResultのコミット エラーの再試行ロジックが組み込まれています。
重要
ご使用の MongoDB バージョン向けの MongoDB ドライバーを使用してください。
ドライバーを使用する場合、トランザクション内の各操作によってセッションがそれぞれの操作に渡される必要があります。
トランザクション内の操作では、 トランザクションレベルの読み取り保証(read concern ) 、トランザクションレベルの書込み保証(write concern ) 、トランザクションレベルの読み込み設定(read preference) が使用されます。
トランザクション内で暗黙的または明示的にコレクションを作成できます。 「トランザクション内でのコレクションとインデックスの作成 」を参照してください。
この例では、トランザクションを操作するために新しいCallback APIを使用します。これにより、トランザクションが開始され、指定された操作が実行され、コミット(エラーの場合は中止)されます。 新しいCallback APIには、 TransientTransactionErrorまたはUnknownTransactionCommitResultのコミット エラーの再試行ロジックが組み込まれています。
重要
ご使用の MongoDB バージョン向けの MongoDB ドライバーを使用してください。
ドライバーを使用する場合、トランザクション内の各操作によってセッションがそれぞれの操作に渡される必要があります。
トランザクション内の操作では、 トランザクションレベルの読み取り保証(read concern ) 、トランザクションレベルの書込み保証(write concern ) 、トランザクションレベルの読み込み設定(read preference) が使用されます。
トランザクション内で暗黙的または明示的にコレクションを作成できます。 「トランザクション内でのコレクションとインデックスの作成 」を参照してください。
この例では、トランザクションを操作するために新しいCallback APIを使用します。これにより、トランザクションが開始され、指定された操作が実行され、コミット(エラーの場合は中止)されます。 新しいCallback APIには、 TransientTransactionErrorまたはUnknownTransactionCommitResultのコミット エラーの再試行ロジックが組み込まれています。
この例では、トランザクションを操作するために新しいCallback APIを使用します。これにより、トランザクションが開始され、指定された操作が実行され、コミット(エラーの場合は中止)されます。 新しいCallback APIには、 TransientTransactionErrorまたはUnknownTransactionCommitResultのコミット エラーの再試行ロジックが組み込まれています。
重要
ご使用の MongoDB バージョン向けの MongoDB ドライバーを使用してください。
ドライバーを使用する場合、トランザクション内の各操作によってセッションがそれぞれの操作に渡される必要があります。
トランザクション内の操作では、 トランザクションレベルの読み取り保証(read concern ) 、トランザクションレベルの書込み保証(write concern ) 、トランザクションレベルの読み込み設定(read preference) が使用されます。
トランザクション内で暗黙的または明示的にコレクションを作成できます。 「トランザクション内でのコレクションとインデックスの作成 」を参照してください。
重要
ご使用の MongoDB バージョン向けの MongoDB ドライバーを使用してください。
ドライバーを使用する場合、トランザクション内の各操作によってセッションがそれぞれの操作に渡される必要があります。
トランザクション内の操作では、 トランザクションレベルの読み取り保証(read concern ) 、トランザクションレベルの書込み保証(write concern ) 、トランザクションレベルの読み込み設定(read preference) が使用されます。
トランザクション内で暗黙的または明示的にコレクションを作成できます。 「トランザクション内でのコレクションとインデックスの作成 」を参照してください。
この例では、トランザクションを操作するために新しいCallback APIを使用します。これにより、トランザクションが開始され、指定された操作が実行され、コミット(エラーの場合は中止)されます。 新しいCallback APIには、 TransientTransactionErrorまたはUnknownTransactionCommitResultのコミット エラーの再試行ロジックが組み込まれています。
/* For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g. String uri = "mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/admin?replicaSet=myRepl"; For a sharded cluster, connect to the mongos instances; e.g. String uri = "mongodb://mongos0.example.com:27017,mongos1.example.com:27017:27017/admin"; */ final MongoClient client = MongoClients.create(uri); /* Create collections. */ client.getDatabase("mydb1").getCollection("foo") .withWriteConcern(WriteConcern.MAJORITY).insertOne( new Document("abc", 0)); client.getDatabase("mydb2").getCollection("bar") .withWriteConcern(WriteConcern.MAJORITY).insertOne( new Document("xyz", 0)); /* Step 1: Start a client session. */ final ClientSession clientSession = client.startSession(); /* Step 2: Optional. Define options to use for the transaction. */ TransactionOptions txnOptions = TransactionOptions.builder() .readPreference(ReadPreference.primary()) .readConcern(ReadConcern.LOCAL) .writeConcern(WriteConcern.MAJORITY) .build(); /* Step 3: Define the sequence of operations to perform inside the transactions. */ TransactionBody txnBody = new TransactionBody<String>() { public String execute() { MongoCollection<Document> coll1 = client.getDatabase("mydb1").getCollection("foo"); MongoCollection<Document> coll2 = client.getDatabase("mydb2").getCollection("bar"); /* Important:: You must pass the session to the operations.. */ coll1.insertOne(clientSession, new Document("abc", 1)); coll2.insertOne(clientSession, new Document("xyz", 999)); return "Inserted into collections in different databases"; } }; try { /* Step 4: Use .withTransaction() to start a transaction, execute the callback, and commit (or abort on error). */ clientSession.withTransaction(txnBody, txnOptions); } catch (RuntimeException e) { // some error handling } finally { clientSession.close(); }
重要
ご使用の MongoDB バージョン向けの MongoDB ドライバーを使用してください。
ドライバーを使用する場合、トランザクション内の各操作によってセッションがそれぞれの操作に渡される必要があります。
トランザクション内の操作では、 トランザクションレベルの読み取り保証(read concern ) 、トランザクションレベルの書込み保証(write concern ) 、トランザクションレベルの読み込み設定(read preference) が使用されます。
トランザクション内で暗黙的または明示的にコレクションを作成できます。 「トランザクション内でのコレクションとインデックスの作成 」を参照してください。
この例では、トランザクションを操作するために新しいCallback APIを使用します。これにより、トランザクションが開始され、指定された操作が実行され、コミット(エラーの場合は中止)されます。 新しいCallback APIには、 TransientTransactionErrorまたはUnknownTransactionCommitResultのコミット エラーの再試行ロジックが組み込まれています。
# For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g. # uriString = 'mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl' # For a sharded cluster, connect to the mongos instances; e.g. # uriString = 'mongodb://mongos0.example.com:27017,mongos1.example.com:27017/' client = AsyncIOMotorClient(uriString) wc_majority = WriteConcern("majority", wtimeout=1000) # Prereq: Create collections. await client.get_database("mydb1", write_concern=wc_majority).foo.insert_one({"abc": 0}) await client.get_database("mydb2", write_concern=wc_majority).bar.insert_one({"xyz": 0}) # Step 1: Define the callback that specifies the sequence of operations to perform inside the transactions. async def callback(my_session): collection_one = my_session.client.mydb1.foo collection_two = my_session.client.mydb2.bar # Important:: You must pass the session to the operations. await collection_one.insert_one({"abc": 1}, session=my_session) await collection_two.insert_one({"xyz": 999}, session=my_session) # Step 2: Start a client session. async with await client.start_session() as session: # Step 3: Use with_transaction to start a transaction, execute the callback, and commit (or abort on error). await session.with_transaction( callback, read_concern=ReadConcern("local"), write_concern=wc_majority, read_preference=ReadPreference.PRIMARY, )
重要
ご使用の MongoDB バージョン向けの MongoDB ドライバーを使用してください。
ドライバーを使用する場合、トランザクション内の各操作によってセッションがそれぞれの操作に渡される必要があります。
トランザクション内の操作では、 トランザクションレベルの読み取り保証(read concern ) 、トランザクションレベルの書込み保証(write concern ) 、トランザクションレベルの読み込み設定(read preference) が使用されます。
トランザクション内で暗黙的または明示的にコレクションを作成できます。 「トランザクション内でのコレクションとインデックスの作成 」を参照してください。
この例では、トランザクションを操作するために新しいCallback APIを使用します。これにより、トランザクションが開始され、指定された操作が実行され、コミット(エラーの場合は中止)されます。 新しいCallback APIには、 TransientTransactionErrorまたはUnknownTransactionCommitResultのコミット エラーの再試行ロジックが組み込まれています。
// For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g. // const uri = 'mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl' // For a sharded cluster, connect to the mongos instances; e.g. // const uri = 'mongodb://mongos0.example.com:27017,mongos1.example.com:27017/' const client = new MongoClient(uri); await client.connect(); // Prereq: Create collections. await client .db('mydb1') .collection('foo') .insertOne({ abc: 0 }, { writeConcern: { w: 'majority' } }); await client .db('mydb2') .collection('bar') .insertOne({ xyz: 0 }, { writeConcern: { w: 'majority' } }); // Step 1: Start a Client Session const session = client.startSession(); // Step 2: Optional. Define options to use for the transaction const transactionOptions = { readPreference: 'primary', readConcern: { level: 'local' }, writeConcern: { w: 'majority' } }; // Step 3: Use withTransaction to start a transaction, execute the callback, and commit (or abort on error) // Note: The callback for withTransaction MUST be async and/or return a Promise. try { await session.withTransaction(async () => { const coll1 = client.db('mydb1').collection('foo'); const coll2 = client.db('mydb2').collection('bar'); // Important:: You must pass the session to the operations await coll1.insertOne({ abc: 1 }, { session }); await coll2.insertOne({ xyz: 999 }, { session }); }, transactionOptions); } finally { await session.endSession(); await client.close(); }
注意
Perl ドライバーについては、代わりにCore APIの使用例を参照してください。
重要
ご使用の MongoDB バージョン向けの MongoDB ドライバーを使用してください。
ドライバーを使用する場合、トランザクション内の各操作によってセッションがそれぞれの操作に渡される必要があります。
トランザクション内の操作では、 トランザクションレベルの読み取り保証(read concern ) 、トランザクションレベルの書込み保証(write concern ) 、トランザクションレベルの読み込み設定(read preference) が使用されます。
トランザクション内で暗黙的または明示的にコレクションを作成できます。 「トランザクション内でのコレクションとインデックスの作成 」を参照してください。
この例では、トランザクションを操作するために新しいCallback APIを使用します。これにより、トランザクションが開始され、指定された操作が実行され、コミット(エラーの場合は中止)されます。 新しいCallback APIには、 TransientTransactionErrorまたはUnknownTransactionCommitResultのコミット エラーの再試行ロジックが組み込まれています。
/* * For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g. * uriString = 'mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl' * For a sharded cluster, connect to the mongos instances; e.g. * uriString = 'mongodb://mongos0.example.com:27017,mongos1.example.com:27017/' */ $client = new \MongoDB\Client($uriString); // Prerequisite: Create collections. $client->selectCollection( 'mydb1', 'foo', [ 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY, 1000), ], )->insertOne(['abc' => 0]); $client->selectCollection( 'mydb2', 'bar', [ 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY, 1000), ], )->insertOne(['xyz' => 0]); // Step 1: Define the callback that specifies the sequence of operations to perform inside the transactions. $callback = function (\MongoDB\Driver\Session $session) use ($client): void { $client ->selectCollection('mydb1', 'foo') ->insertOne(['abc' => 1], ['session' => $session]); $client ->selectCollection('mydb2', 'bar') ->insertOne(['xyz' => 999], ['session' => $session]); }; // Step 2: Start a client session. $session = $client->startSession(); // Step 3: Use with_transaction to start a transaction, execute the callback, and commit (or abort on error). \MongoDB\with_transaction($session, $callback);
重要
ご使用の MongoDB バージョン向けの MongoDB ドライバーを使用してください。
ドライバーを使用する場合、トランザクション内の各操作によってセッションがそれぞれの操作に渡される必要があります。
トランザクション内の操作では、 トランザクションレベルの読み取り保証(read concern ) 、トランザクションレベルの書込み保証(write concern ) 、トランザクションレベルの読み込み設定(read preference) が使用されます。
トランザクション内で暗黙的または明示的にコレクションを作成できます。 「トランザクション内でのコレクションとインデックスの作成 」を参照してください。
この例では、トランザクションを操作するために新しいCallback APIを使用します。これにより、トランザクションが開始され、指定された操作が実行され、コミット(エラーの場合は中止)されます。 新しいCallback APIには、 TransientTransactionErrorまたはUnknownTransactionCommitResultのコミット エラーの再試行ロジックが組み込まれています。
# For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g. # uriString = 'mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl' # For a sharded cluster, connect to the mongos instances; e.g. # uriString = 'mongodb://mongos0.example.com:27017,mongos1.example.com:27017/' client = MongoClient(uriString) wc_majority = WriteConcern("majority", wtimeout=1000) # Prereq: Create collections. client.get_database("mydb1", write_concern=wc_majority).foo.insert_one({"abc": 0}) client.get_database("mydb2", write_concern=wc_majority).bar.insert_one({"xyz": 0}) # Step 1: Define the callback that specifies the sequence of operations to perform inside the transactions. def callback(session): collection_one = session.client.mydb1.foo collection_two = session.client.mydb2.bar # Important:: You must pass the session to the operations. collection_one.insert_one({"abc": 1}, session=session) collection_two.insert_one({"xyz": 999}, session=session) # Step 2: Start a client session. with client.start_session() as session: # Step 3: Use with_transaction to start a transaction, execute the callback, and commit (or abort on error). session.with_transaction(callback)
重要
ご使用の MongoDB バージョン向けの MongoDB ドライバーを使用してください。
ドライバーを使用する場合、トランザクション内の各操作によってセッションがそれぞれの操作に渡される必要があります。
トランザクション内の操作では、 トランザクションレベルの読み取り保証(read concern ) 、トランザクションレベルの書込み保証(write concern ) 、トランザクションレベルの読み込み設定(read preference) が使用されます。
トランザクション内で暗黙的または明示的にコレクションを作成できます。 「トランザクション内でのコレクションとインデックスの作成 」を参照してください。
この例では、トランザクションを操作するために新しいCallback APIを使用します。これにより、トランザクションが開始され、指定された操作が実行され、コミット(エラーの場合は中止)されます。 新しいCallback APIには、 TransientTransactionErrorまたはUnknownTransactionCommitResultのコミット エラーの再試行ロジックが組み込まれています。
# For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g. # uriString = 'mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl' # For a sharded cluster, connect to the mongos instances; e.g. # uri_string = 'mongodb://mongos0.example.com:27017,mongos1.example.com:27017/' client = Mongo::Client.new(uri_string, write_concern: { w: :majority, wtimeout: 1000 }) # Prereq: Create collections. client.use('mydb1')['foo'].insert_one(abc: 0) client.use('mydb2')['bar'].insert_one(xyz: 0) # Step 1: Define the callback that specifies the sequence of operations to perform inside the transactions. callback = proc do |my_session| collection_one = client.use('mydb1')['foo'] collection_two = client.use('mydb2')['bar'] # Important: You must pass the session to the operations. collection_one.insert_one({ abc: 1 }, session: my_session) collection_two.insert_one({ xyz: 999 }, session: my_session) end # . Step 2: Start a client session. session = client.start_session # Step 3: Use with_transaction to start a transaction, execute the callback, and commit (or abort on error). session.with_transaction( read_concern: { level: :local }, write_concern: { w: :majority, wtimeout: 1000 }, read: { mode: :primary }, &callback )
注意
Scala ドライバーについては、代わりに「 Core APIの使用例 」を参照してください。
Core API
Core transaction API には、次のラベルの付いたエラーの再試行ロジックは組み込まれていません。
TransientTransactionError。 トランザクション内の操作によってTransientTransactionErrorというラベルの付いたエラーが返された場合は、トランザクション全体を再試行できます。TransientTransactionErrorを処理するには、アプリケーションはエラーの再試行ロジックを明示的に組み込む必要があります。UnknownTransactionCommitResult。 コミットがUnknownTransactionCommitResultというラベルの付いたエラーを返す場合は、コミットを再試行できます。UnknownTransactionCommitResultを処理するには、アプリケーションはエラーの再試行ロジックを明示的に組み込む必要があります。
例
➤ 右上の言語の選択のドロップダウンメニューを使用して、このページの例の言語を設定します。
次の例には、一時的なエラーの場合はトランザクションを再試行し、不明なコミット エラーの場合はコミットを再試行するロジックが組み込まれています。
重要
読み取り操作と書込み (write) 操作をトランザクションに関連付けるには、トランザクション内の各操作にセッションを渡す必要があります。
void runTransactionWithRetry(Runnable transactional) { while (true) { try { transactional.run(); break; } catch (MongoException e) { System.out.println("Transaction aborted. Caught exception during transaction."); if (e.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL)) { System.out.println("TransientTransactionError, aborting transaction and retrying ..."); continue; } else { throw e; } } } } void commitWithRetry(ClientSession clientSession) { while (true) { try { clientSession.commitTransaction(); System.out.println("Transaction committed"); break; } catch (MongoException e) { // can retry commit if (e.hasErrorLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)) { System.out.println("UnknownTransactionCommitResult, retrying commit operation ..."); continue; } else { System.out.println("Exception during commit ..."); throw e; } } } } void updateEmployeeInfo() { MongoCollection<Document> employeesCollection = client.getDatabase("hr").getCollection("employees"); MongoCollection<Document> eventsCollection = client.getDatabase("reporting").getCollection("events"); TransactionOptions txnOptions = TransactionOptions.builder() .readPreference(ReadPreference.primary()) .readConcern(ReadConcern.MAJORITY) .writeConcern(WriteConcern.MAJORITY) .build(); try (ClientSession clientSession = client.startSession()) { clientSession.startTransaction(txnOptions); employeesCollection.updateOne(clientSession, Filters.eq("employee", 3), Updates.set("status", "Inactive")); eventsCollection.insertOne(clientSession, new Document("employee", 3).append("status", new Document("new", "Inactive").append("old", "Active"))); commitWithRetry(clientSession); } } void updateEmployeeInfoWithRetry() { runTransactionWithRetry(this::updateEmployeeInfo); }
注意
Motorについては、代わりにCallback APIを参照してください。
重要
読み取り操作と書込み (write) 操作をトランザクションに関連付けるには、トランザクション内の各操作にセッションを渡す必要があります。
async function commitWithRetry(session) { try { await session.commitTransaction(); console.log('Transaction committed.'); } catch (error) { if (error.hasErrorLabel('UnknownTransactionCommitResult')) { console.log('UnknownTransactionCommitResult, retrying commit operation ...'); await commitWithRetry(session); } else { console.log('Error during commit ...'); throw error; } } } async function runTransactionWithRetry(txnFunc, client, session) { try { await txnFunc(client, session); } catch (error) { console.log('Transaction aborted. Caught exception during transaction.'); // If transient error, retry the whole transaction if (error.hasErrorLabel('TransientTransactionError')) { console.log('TransientTransactionError, retrying transaction ...'); await runTransactionWithRetry(txnFunc, client, session); } else { throw error; } } } async function updateEmployeeInfo(client, session) { session.startTransaction({ readConcern: { level: 'snapshot' }, writeConcern: { w: 'majority' }, readPreference: 'primary' }); const employeesCollection = client.db('hr').collection('employees'); const eventsCollection = client.db('reporting').collection('events'); await employeesCollection.updateOne( { employee: 3 }, { $set: { status: 'Inactive' } }, { session } ); await eventsCollection.insertOne( { employee: 3, status: { new: 'Inactive', old: 'Active' } }, { session } ); try { await commitWithRetry(session); } catch (error) { await session.abortTransaction(); throw error; } } return client.withSession(session => runTransactionWithRetry(updateEmployeeInfo, client, session) );
重要
読み取り操作と書込み (write) 操作をトランザクションに関連付けるには、トランザクション内の各操作にセッションを渡す必要があります。
sub runTransactionWithRetry { my ( $txnFunc, $session ) = @_; LOOP: { eval { $txnFunc->($session); # performs transaction }; if ( my $error = $@ ) { print("Transaction aborted-> Caught exception during transaction.\n"); # If transient error, retry the whole transaction if ( $error->has_error_label("TransientTransactionError") ) { print("TransientTransactionError, retrying transaction ->..\n"); redo LOOP; } else { die $error; } } } return; } sub commitWithRetry { my ($session) = @_; LOOP: { eval { $session->commit_transaction(); # Uses write concern set at transaction start. print("Transaction committed->\n"); }; if ( my $error = $@ ) { # Can retry commit if ( $error->has_error_label("UnknownTransactionCommitResult") ) { print("UnknownTransactionCommitResult, retrying commit operation ->..\n"); redo LOOP; } else { print("Error during commit ->..\n"); die $error; } } } return; } # Updates two collections in a transactions sub updateEmployeeInfo { my ($session) = @_; my $employeesCollection = $session->client->ns("hr.employees"); my $eventsCollection = $session->client->ns("reporting.events"); $session->start_transaction( { readConcern => { level => "snapshot" }, writeConcern => { w => "majority" }, readPreference => 'primary', } ); eval { $employeesCollection->update_one( { employee => 3 }, { '$set' => { status => "Inactive" } }, { session => $session}, ); $eventsCollection->insert_one( { employee => 3, status => { new => "Inactive", old => "Active" } }, { session => $session}, ); }; if ( my $error = $@ ) { print("Caught exception during transaction, aborting->\n"); $session->abort_transaction(); die $error; } commitWithRetry($session); } # Start a session my $session = $client->start_session(); eval { runTransactionWithRetry(\&updateEmployeeInfo, $session); }; if ( my $error = $@ ) { # Do something with error } $session->end_session();
重要
読み取り操作と書込み (write) 操作をトランザクションに関連付けるには、トランザクション内の各操作にセッションを渡す必要があります。
private function runTransactionWithRetry3(callable $txnFunc, \MongoDB\Client $client, \MongoDB\Driver\Session $session): void { while (true) { try { $txnFunc($client, $session); // performs transaction break; } catch (\MongoDB\Driver\Exception\CommandException $error) { $resultDoc = $error->getResultDocument(); // If transient error, retry the whole transaction if (isset($resultDoc->errorLabels) && in_array('TransientTransactionError', $resultDoc->errorLabels)) { continue; } else { throw $error; } } catch (\MongoDB\Driver\Exception\Exception $error) { throw $error; } } } private function commitWithRetry3(\MongoDB\Driver\Session $session): void { while (true) { try { $session->commitTransaction(); echo "Transaction committed.\n"; break; } catch (\MongoDB\Driver\Exception\CommandException $error) { $resultDoc = $error->getResultDocument(); if (isset($resultDoc->errorLabels) && in_array('UnknownTransactionCommitResult', $resultDoc->errorLabels)) { echo "UnknownTransactionCommitResult, retrying commit operation ...\n"; continue; } else { echo "Error during commit ...\n"; throw $error; } } catch (\MongoDB\Driver\Exception\Exception $error) { echo "Error during commit ...\n"; throw $error; } } } private function updateEmployeeInfo3(\MongoDB\Client $client, \MongoDB\Driver\Session $session): void { $session->startTransaction([ 'readConcern' => new \MongoDB\Driver\ReadConcern('snapshot'), 'readPrefernece' => new \MongoDB\Driver\ReadPreference(\MongoDB\Driver\ReadPreference::PRIMARY), 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY), ]); try { $client->hr->employees->updateOne( ['employee' => 3], ['$set' => ['status' => 'Inactive']], ['session' => $session], ); $client->reporting->events->insertOne( ['employee' => 3, 'status' => ['new' => 'Inactive', 'old' => 'Active']], ['session' => $session], ); } catch (\MongoDB\Driver\Exception\Exception $error) { echo "Caught exception during transaction, aborting.\n"; $session->abortTransaction(); throw $error; } $this->commitWithRetry3($session); } private function doUpdateEmployeeInfo(\MongoDB\Client $client): void { // Start a session. $session = $client->startSession(); try { $this->runTransactionWithRetry3([$this, 'updateEmployeeInfo3'], $client, $session); } catch (\MongoDB\Driver\Exception\Exception) { // Do something with error } }
重要
読み取り操作と書込み (write) 操作をトランザクションに関連付けるには、トランザクション内の各操作にセッションを渡す必要があります。
def run_transaction_with_retry(txn_func, session): while True: try: txn_func(session) # performs transaction break except (ConnectionFailure, OperationFailure) as exc: # If transient error, retry the whole transaction if exc.has_error_label("TransientTransactionError"): print("TransientTransactionError, retrying transaction ...") continue else: raise def commit_with_retry(session): while True: try: # Commit uses write concern set at transaction start. session.commit_transaction() print("Transaction committed.") break except (ConnectionFailure, OperationFailure) as exc: # Can retry commit if exc.has_error_label("UnknownTransactionCommitResult"): print("UnknownTransactionCommitResult, retrying commit operation ...") continue else: print("Error during commit ...") raise # Updates two collections in a transactions def update_employee_info(session): employees_coll = session.client.hr.employees events_coll = session.client.reporting.events with session.start_transaction( read_concern=ReadConcern("snapshot"), write_concern=WriteConcern(w="majority"), read_preference=ReadPreference.PRIMARY, ): employees_coll.update_one( {"employee": 3}, {"$set": {"status": "Inactive"}}, session=session ) events_coll.insert_one( {"employee": 3, "status": {"new": "Inactive", "old": "Active"}}, session=session ) commit_with_retry(session) # Start a session. with client.start_session() as session: try: run_transaction_with_retry(update_employee_info, session) except Exception: # Do something with error. raise
重要
読み取り操作と書込み (write) 操作をトランザクションに関連付けるには、トランザクション内の各操作にセッションを渡す必要があります。
def run_transaction_with_retry(session) yield session # performs transaction rescue Mongo::Error => e puts 'Transaction aborted. Caught exception during transaction.' raise unless e.label?('TransientTransactionError') puts 'TransientTransactionError, retrying transaction ...' retry end def commit_with_retry(session) session.commit_transaction puts 'Transaction committed.' rescue Mongo::Error => e if e.label?('UnknownTransactionCommitResult') puts 'UnknownTransactionCommitResult, retrying commit operation ...' retry else puts 'Error during commit ...' raise end end # updates two collections in a transaction def update_employee_info(session) employees_coll = session.client.use(:hr)[:employees] events_coll = session.client.use(:reporting)[:events] session.start_transaction(read_concern: { level: :snapshot }, write_concern: { w: :majority }, read: { mode: :primary }) employees_coll.update_one({ employee: 3 }, { '$set' => { status: 'Inactive' } }, session: session) events_coll.insert_one({ employee: 3, status: { new: 'Inactive', old: 'Active' } }, session: session) commit_with_retry(session) end session = client.start_session begin run_transaction_with_retry(session) do update_employee_info(session) end rescue StandardError # rubocop:disable Lint/UselessRescue # Do something with error raise end
重要
読み取り操作と書込み (write) 操作をトランザクションに関連付けるには、トランザクション内の各操作にセッションを渡す必要があります。
/* * Copyright 2008-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.mongodb.scala import org.mongodb.scala.model.{Filters, Updates} import org.mongodb.scala.result.UpdateResult import scala.concurrent.Await import scala.concurrent.duration.Duration //scalastyle:off magic.number class DocumentationTransactionsExampleSpec extends RequiresMongoDBISpec { // Implicit functions that execute the Observable and return the results val waitDuration = Duration(5, "seconds") implicit class ObservableExecutor[T](observable: Observable[T]) { def execute(): Seq[T] = Await.result(observable.toFuture(), waitDuration) } implicit class SingleObservableExecutor[T](observable: SingleObservable[T]) { def execute(): T = Await.result(observable.toFuture(), waitDuration) } // end implicit functions "The Scala driver" should "be able to commit a transaction" in withClient { client => assume(serverVersionAtLeast(List(4, 0, 0)) && !hasSingleHost()) client.getDatabase("hr").drop().execute() client.getDatabase("hr").createCollection("employees").execute() client.getDatabase("hr").createCollection("events").execute() updateEmployeeInfoWithRetry(client).execute() should equal(Completed()) client.getDatabase("hr").drop().execute() should equal(Completed()) } def updateEmployeeInfo(database: MongoDatabase, observable: SingleObservable[ClientSession]): SingleObservable[ClientSession] = { observable.map(clientSession => { val employeesCollection = database.getCollection("employees") val eventsCollection = database.getCollection("events") val transactionOptions = TransactionOptions.builder() .readPreference(ReadPreference.primary()) .readConcern(ReadConcern.SNAPSHOT) .writeConcern(WriteConcern.MAJORITY) .build() clientSession.startTransaction(transactionOptions) employeesCollection.updateOne(clientSession, Filters.eq("employee", 3), Updates.set("status", "Inactive")) .subscribe((res: UpdateResult) => println(res)) eventsCollection.insertOne(clientSession, Document("employee" -> 3, "status" -> Document("new" -> "Inactive", "old" -> "Active"))) .subscribe((res: Completed) => println(res)) clientSession }) } def commitAndRetry(observable: SingleObservable[Completed]): SingleObservable[Completed] = { observable.recoverWith({ case e: MongoException if e.hasErrorLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL) => { println("UnknownTransactionCommitResult, retrying commit operation ...") commitAndRetry(observable) } case e: Exception => { println(s"Exception during commit ...: $e") throw e } }) } def runTransactionAndRetry(observable: SingleObservable[Completed]): SingleObservable[Completed] = { observable.recoverWith({ case e: MongoException if e.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL) => { println("TransientTransactionError, aborting transaction and retrying ...") runTransactionAndRetry(observable) } }) } def updateEmployeeInfoWithRetry(client: MongoClient): SingleObservable[Completed] = { val database = client.getDatabase("hr") val updateEmployeeInfoObservable: Observable[ClientSession] = updateEmployeeInfo(database, client.startSession()) val commitTransactionObservable: SingleObservable[Completed] = updateEmployeeInfoObservable.flatMap(clientSession => clientSession.commitTransaction()) val commitAndRetryObservable: SingleObservable[Completed] = commitAndRetry(commitTransactionObservable) runTransactionAndRetry(commitAndRetryObservable) } }
ドライバーのバージョン
トランザクションエラーの処理
MongoDB かリレーショナル データベースかにかかわらず、データベース システムに関係なく、トランザクションのコミット中にエラーを処理し、トランザクションの再試行ロジックを組み込む必要があります。
TransientTransactionError
トランザクション内の個々の書き込み操作は、retryWritesの値に関係なく、再試行できません。"TransientTransactionError"プライマリが降格する場合など、ラベル に関連付けられたエラーが操作で発生した場合は、トランザクション全体が再試行されます。
Callback API には
"TransientTransactionError"の再試行ロジックが組み込まれています。Core transaction API には
"TransientTransactionError"の再試行ロジックは組み込まれていません。"TransientTransactionError"を処理するには、アプリケーションはエラーの再試行ロジックを明示的に組み込む必要があります。 一時的なエラーの再試行ロジックを組み込んだ例については、「 Core API の例 」を参照してください。
UnknownTransactionCommitResult
コミット操作は、再試行可能な書込み操作です。 コミット操作でエラーが発生した場合、MongoDB ドライバーはretryWritesの値に関係なくコミットを再試行します。
コミット操作で"UnknownTransactionCommitResult"というラベルの付いたエラーが発生した場合は、コミットを再試行できます。
Callback API には
"UnknownTransactionCommitResult"の再試行ロジックが組み込まれています。Core transaction API には
"UnknownTransactionCommitResult"の再試行ロジックは組み込まれていません。"UnknownTransactionCommitResult"を処理するには、アプリケーションはエラーの再試行ロジックを明示的に組み込む必要があります。 不明なコミット エラーの再試行ロジックを組み込んだ例については、「 Core API の例 」を参照してください。
TransactionTooLargeForCache
バージョン 6.0.5 の新機能。
MongoDB 6.0.5 以降、サーバーはTransactionTooLargeForCacheエラーを受信してもトランザクションを再試行しません。 このエラーは、キャッシュが小さすぎて再試行が失敗する可能性があることを意味します。
transactionTooLargeForCacheThresholdしきい値のデフォルト値は0.75です。 トランザクションがキャッシュの 75% 以上を使用している場合、サーバーはトランザクションを再試行する代わりにTransactionTooLargeForCacheを返します。
MongoDB の以前のバージョンでは、サーバーは ではなくTemporarilyUnavailable WriteConflictまたはTransactionTooLargeForCache を返します。
エラーしきい値を変更するには、 setParameterコマンドを使用します。
詳細情報
mongosh 例
次のmongoshメソッドはトランザクションに利用できます。
注意
mongoshの例では、わかりやすくするために再試行ロジックと堅牢なエラー処理を省略しています。 アプリケーションにトランザクションを含めるより具体的な例については、代わりに「トランザクション エラーの処理」を参照してください。
// Create collections: db.getSiblingDB("mydb1").foo.insertOne( {abc: 0}, { writeConcern: { w: "majority", wtimeout: 2000 } } ) db.getSiblingDB("mydb2").bar.insertOne( {xyz: 0}, { writeConcern: { w: "majority", wtimeout: 2000 } } ) // Start a session. session = db.getMongo().startSession( { readPreference: { mode: "primary" } } ); coll1 = session.getDatabase("mydb1").foo; coll2 = session.getDatabase("mydb2").bar; // Start a transaction session.startTransaction( { readConcern: { level: "local" }, writeConcern: { w: "majority" } } ); // Operations inside the transaction try { coll1.insertOne( { abc: 1 } ); coll2.insertOne( { xyz: 999 } ); } catch (error) { // Abort transaction on error session.abortTransaction(); throw error; } // Commit the transaction using write concern set at transaction start session.commitTransaction(); session.endSession();