Overview
このガイドでは、Node.js ドライバーを使用してトランザクションを実行する方法を学習できます。トランザクションを使用すると、トランザクション全体がコミットされるまでデータを変更しない一連の操作を実行できます。トランザクション内のいずれかの操作が失敗した場合、ドライバーによってトランザクションがキャンセルされ、変更が反映される前にすべてのデータ変更が破棄されます。この特徴はアトミック性と呼ばれます。
MongoDB の 1 つのドキュメントに対する書込み (write) 操作はすべてアトミックであるため、トランザクションを使用して複数のドキュメントを変更するアトミックな変更を行うことをお勧めします。 この状況ではマルチドキュメントトランザクションが必要になります。 MongoDB は、ドライバーで予期しないエラーが発生した場合でも、トランザクション操作に関係するデータの一貫性が保たれることを保証するため、マルチドキュメントトランザクションはACID に準拠します。
ACID compliance とトランザクションの詳細については、 ACID トランザクションに関するの記事をご覧ください。
注意
マルチドキュメントトランザクションを実行するには、MongoDB Server バージョン 4.0 以降を実行している配置に接続する必要があります。
制限事項の詳細なリストについては、サーバー マニュアルの「トランザクションと操作」セクションを参照してください。
ドライバーを使用してマルチドキュメントトランザクションを実行する方法の詳細については、このガイドの次のセクションを参照してください。
因果整合性
MongoDB は特定のクライアントセッションで因果整合性を有効にします。因果整合性モデルは、分散システム内でセッション内の操作が因果順序で実行されることを保証します。クライアントは、因果関係、または操作間の依存関係と整合性のある結果を観察します。例、ある操作が別の操作の結果に論理的に依存する一連の操作を実行すると、後続のすべての読み取りは 依存関係を反映します。
因果整合性を保証するには、クライアントセッションが次の要件を満たす必要があります。
セッションを開始するとき、ドライバーは 因果整合性 オプションを有効にする必要があります。このオプションはデフォルトで有効になっています。
操作は 1 つのスレッド上の 1 つのセッションで実行する必要があります。それ以外の場合、セッションまたはスレッドは optime とクラスター時間の値を相互に通信する必要があります。 これらの値を通信する 2 つのセッションの例については、 MongoDB Serverマニュアルの 因果整合性の例 を参照してください。
majority
の読み取り保証 (read concern)を使用する必要があります。majority
の書込み保証 (write concern)を使用する必要があります。これは、デフォルトの書込み保証 (write concern)の値です。
次の表では、因果整合性のあるセッションが提供する保証について説明しています。
保証 | 説明 |
---|---|
書込み操作の結果を読み取る | 読み取り操作は、前の書込み操作の結果を反映します。 |
単調な読み取り | 読み取り操作では、前の 読み取り操作よりも前のデータ状態を反映した結果は返されません。 |
単調書込み | 書込み操作が他の書込み操作に優先する必要がある場合、サーバーはこの書込み操作を最初に実行します。 例、 |
書込み操作の前に読み取り操作をする | 書込み操作が他の読み取り操作の後に続く必要がある場合、サーバーは最初に読み取り操作を実行します。 例、 |
Tip
このセクションで述べられた概念の詳細については、次のMongoDB Serverマニュアル エントリを参照してください。
トランザクション API
このドライバーは、トランザクションを実行するための 2 つの API (Core API および Convenient Transaction API)を提供します。
Core API は、トランザクションを作成、コミット、終了できるフレームワークです。この API を使用する場合は、次のアクションを明示的に実行する必要があります。
トランザクションを作成、コミット、および終了します。
トランザクションを実行するセッションを作成および終了します。
エラー処理ロジックを実装します。
Convenient Transaction API は、トランザクションのコミットや終了の責任を負うことなくトランザクションを実行できるフレームワークです。この API には、サーバーが特定のエラーの種類を発生させたときに操作を再試行するためのエラー処理論理が自動的に組み込まれています。この動作の詳細については、このガイドの「トランザクション エラー」セクションを参照してください。
重要
MongoDB Server バージョン 4.2 以前に接続する場合、トランザクション内で書き込み操作を実行できるのは、既に存在するコレクションに対してのみです。MongoDB Server バージョン 4 . 4 以降に接続すると、トランザクションで書き込み操作を実行するときに、サーバーは必要に応じてコレクションを自動的に作成します。この動作の詳細については、サーバー マニュアルの「トランザクション内でのコレクションとインデックスの作成 」を参照してください。
Core API
Core API では、トランザクションを実装するための次のメソッドが提供されます。
startSession() : 新しい インスタンスを作成します
ClientSession
startTransaction(): 新しいトランザクションを開始します
commitTransaction(): トランザクションが作成されたセッション内で、アクティブなトランザクションをコミットします
abortTransaction() : トランザクションが作成されたセッション内で、アクティブなトランザクションを終了します
endSession(): アクティブなセッションを終了します
この API を使用する場合は、次の手順を実行する必要があります。
当該のセッションで実行したい各操作に、セッション インスタンスを渡します。
catch
ブロックを実装して、サーバー トランザクション エラーを識別し、エラー処理ロジックを実装します。
次のコードは、Core API を使用してトランザクションを実行する方法を示しています。
async function coreTest(client) { const session = client.startSession(); try { session.startTransaction(); const savingsColl = client.db("bank").collection("savings_accounts"); await savingsColl.findOneAndUpdate( {account_id: "9876"}, {$inc: {amount: -100 }}, { session }); const checkingColl = client.db("bank").collection("checking_accounts"); await checkingColl.findOneAndUpdate( {account_id: "9876"}, {$inc: {amount: 100 }}, { session }); // ... perform other operations await session.commitTransaction(); console.log("Transaction committed."); } catch (error) { console.log("An error occurred during the transaction:" + error); await session.abortTransaction(); } finally { await session.endSession(); } }
重要
セッションを開始したクライアントによるセッションの使用
1 つのMongoClient
インスタンスから別のクライアントインスタンスにセッションを提供すると、ドライバーによりエラーが返されます。
たとえば、次のコードでは、client1
クライアントから ClientSession
インスタンスを作成するため、MongoInvalidArgumentError
エラーが生成されますが、書込み操作のためにこのセッションをclient2
クライアントに提供します。
const session = client1.startSession(); client2.db('myDB').collection('myColl').insertOne({ name: 'Jane Eyre' }, { session });
Tip
明示的なリソース管理
Node.jsドライバーは、MongoClient
、ClientSession
、ChangeStreams
、およびカーソルの明示的なリソース管理をネイティブでサポートしています。この機能は実験的なものであり、変更される可能性があります。明示的なリソース管理を使用する方法については、 v6.9 リリースノートを参照してください。
この API を使用する完全に実行可能な使用例については、「Core API の使用」を参照してください。
Convenient Transaction API
Convenient Transaction API には、トランザクションを実装するための次のメソッドが用意されています。
withSession(): セッション内で渡されたコールバックを実行します。API はセッションの作成と終了を自動的に処理します。
withTransaction() : トランザクション内で渡されたコールバックを実行し、コールバックが返されたときに
commitTransaction()
メソッドを呼び出します。
これらのメソッドは、コールバックで返された値を返します。たとえば、withTransaction()
メソッドに渡したコールバックがドキュメント { hello: "world" }
を返す場合、withTransaction()
メソッドもそのドキュメントを返します。
重要
無限ループ エラーを回避するには、withTransaction()
メソッドに渡すコールバックが、発生するエラーをすべてキャッチするようにします。
Convenient Transaction API を使用すると、コールバックからの戻り値を withTransaction()
メソッドと withSession()
メソッドの戻り値として伝達し、コードの他の場所で処理できます。
この API を使用する場合は、次の手順を実行する必要があります。
当該のセッションで実行したい各操作に、セッション インスタンスを渡します。
セッションの各操作に対して非同期
await
構文を実装します。Promise.all()
メソッドを呼び出すなどの並列処理は避けます。セッションを並行して使用すると、通常はサーバー エラーが発生します。
次のコードは、 Convenient Transaction API を使用してトランザクションを実行する方法を示しています。
async function convTest(client) { let txnRes = await client.withSession(async (session) => session.withTransaction(async (session) => { const savingsColl = client.db("bank").collection("savings_accounts"); await savingsColl.findOneAndUpdate( {account_id: "9876"}, {$inc: {amount: -100 }}, { session }); const checkingColl = client.db("bank").collection("checking_accounts"); await checkingColl.findOneAndUpdate( {account_id: "9876"}, {$inc: {amount: 100 }}, { session }); // ... perform other operations return "Transaction committed."; }, null) ); console.log(txnRes); }
この API を使用する完全に実行可能な使用例を確認するには、「Convenient Transaction API の使用」使用例を参照してください。
注意
並列操作はサポートされていません
Node.js ドライバーは、単一のトランザクション内での並列処理をサポートしていません。
トランザクションのオプション
TransactionOptions
インスタンスを startTransaction()
メソッドと withTransaction()
メソッドに渡して、ドライバーがトランザクションを実行する方法を設定できます。オプションを指定すると、 MongoClient
インスタンスに設定したオプションの値が上書きされます。
次の表には、 TransactionOptions
インスタンスで指定できるオプションが含まれています。
設定 | 説明 |
---|---|
| Specifies read operation consistency of the replica set. To learn more, see Read Concern in the Server manual. |
| Specifies the write operation level of acknowledgment required
from a replica set. To learn more, see Write Concern in the Server manual. |
| Specifies how to route read operations to members of a replica set. To learn more, see Read Preference in the Server manual. |
| トランザクション上でのコミット アクションを実行できる時間の長さをミリ秒単位で指定します。 |
オプションの完全なリストについては TransactionOptions の API ドキュメントを参照してください。
注意
トランザクションオプションでの指定がない限り、MongoClient
インスタンスからトランザクションに設定が継承されます。
次のコードは、トランザクション オプションを定義してstartTransaction()
メソッドに渡す方法を示しています。
const txnOpts = { readPreference: 'primary', readConcern: { level: 'local' }, writeConcern: { w: 'majority' }, maxCommitTimeMS: 1000 }; session.startTransaction(txnOpts);
トランザクションのエラー
MongoDB のトランザクションはACIDに準拠している ため、データの一貫性を確保するために操作中にドライバーがエラーを発生させる可能性があります。次のエラーが発生した場合、アプリケーションはトランザクションを再試行する必要があります。
TransientTransactionError
: ドライバがトランザクションをコミットする前に書き込み操作でエラーが発生した場合に発生します。このエラーの種類の詳細については、サーバー マニュアルのドライバーAPIページの 「TransientTransactionError の説明」 を参照してください。UnknownTransactionCommitResult
: コミット操作でエラーが発生した場合に発生します。このエラーの種類の詳細については、サーバー マニュアルのドライバーAPIページの 「UnknownTransactionCommitResult の説明」 を参照してください。
次のセクションでは、別の API を使用する際にこれらのエラーを処理する方法について説明します。
Convenient Transaction APIエラー処理
Convenient Transaction API には、これらのエラー タイプに対応する再試行ロジックが組み込まれています。ドライバーは、コミットが成功するまでトランザクションを自動的に再試行します。
Core APIエラー処理
Core APIを使用してトランザクションを実行している場合は、次のエラー処理関数をアプリケーションに追加する 必要があります。
ドライバが
TransientTransactionError
ドライバーが
UnknownTransactionCommitResult
これらの関数は、コミットが成功するか、別のエラーが発生するまで実行する必要があります。この再試行ロジックの例については、サーバー マニュアルのドライバーAPIページの 「Core API」セクション を参照してください。