トランザクション
Overview
このガイドでは、Node.js ドライバーを使用してトランザクションを実行する方法を学習できます。トランザクションを使用すると、トランザクション全体がコミットされるまでデータを変更しない一連の操作を実行できます。トランザクション内のいずれかの操作が失敗した場合、ドライバーによってトランザクションがキャンセルされ、変更が反映される前にすべてのデータ変更が破棄されます。この特徴はアトミック性と呼ばれます。
MongoDB の 1 つのドキュメントに対する書込み (write) 操作はすべてアトミックであるため、トランザクションを使用して複数のドキュメントを変更するアトミックな変更を行うことをお勧めします。 この状況ではマルチドキュメントトランザクションが必要になります。 MongoDB は、ドライバーで予期しないエラーが発生した場合でも、トランザクション操作に関係するデータの一貫性が保たれることを保証するため、マルチドキュメントトランザクションはACID に準拠します。
ACID compliance とトランザクションの詳細については、 ACID トランザクションに関するの記事をご覧ください。
注意
マルチドキュメントトランザクションを実行するには、MongoDB Server バージョン 4.0 以降を実行している配置に接続する必要があります。
制限事項の詳細なリストについては、サーバー マニュアルの「トランザクションと操作」セクションを参照してください。
MongoDB では、マルチドキュメントトランザクションはクライアント セッション中に実行されます。クライアント セッションは、順番に実行されるよう関連付けられた読み取り操作または書込み操作のグループです。毎回新しいクライアントをインスタンス化するのではなく、クライアントを複数のセッションやトランザクションで再利用することをお勧めします。
majority
の読み取りおよび書込み保証と組み合わせると、ドライバーは操作間の因果一貫性を保証します。 詳細については、サーバー マニュアルの 「クライアント セッションと因果一貫性の保証」 を参照してください。
ドライバーを使用してマルチドキュメントトランザクションを実行する方法の詳細については、このガイドの次のセクションを参照してください。
トランザクション 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 });
この 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 の使用」使用例を参照してください。
トランザクションのオプション
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);
トランザクションのエラー
Core API を使用してトランザクションを実行している場合は、次のエラーに対するエラー処理ロジックをアプリケーションに組み込む必要があります。
TransientTransactionError
: ドライバがトランザクションをコミットする前に書き込み操作でエラーが発生した場合に発生します。 このエラーの詳細については、サーバー マニュアルのドライバー API ページの 「 TransientTransactionError の説明」 を参照してください。UnknownTransactionCommitResult
: コミット操作でエラーが発生した場合に発生します。 このエラーの詳細については、サーバー マニュアルのドライバー API ページの 「 UnknownTransactionCommitResult の説明」 を参照してください。
Convenient Transaction API には、これらのエラー タイプに対応する再試行ロジックが組み込まれているため、ドライバーはコミットが成功するまでトランザクションを再試行します。