Docs Menu
Docs Home
/ / /
Node.js
/

トランザクション

項目一覧

  • Overview
  • トランザクション API
  • Core API
  • Transaction Settings
  • サンプル データ
  • Core API実装
  • 支払いトランザクションの結果

Node.js ドライバーを使用して MongoDB でトランザクションを実行する方法については、次のガイドを参照してください。 トランザクションは 、一緒に成功させるための一連の操作で構成される作業単位であり、1 つ以上の操作が失敗した場合はまとめて失敗します。 この動作はアトミック性と 呼ばれます。 アトミック性とは、1 つ以上の操作で構成されるトランザクションが同時に発生するプロパティであり、他のクライアントはそれらを個別の操作として監視できず、いずれかの操作が失敗しても変更は生じません。

MongoDB の 1 つのドキュメントに対するすべての書込み (write) 操作はアトミックであるため、複数のドキュメントを変更するアトミックな変更を行う必要がある場合( マルチドキュメントトランザクションと呼ばれます)、トランザクションの最もメリットが得られます。 単一ドキュメントに対する書込み操作と同様に、 マルチドキュメントトランザクションはACID に準拠しています 。つまり、MongoDB は、予期しないエラーが発生した場合でも、トランザクション操作に関係するデータの一貫性が保たれることを保証します。 ACID トランザクションに関する MongoDB の記事の詳細については、こちらの MongoDB に関する記事をご覧ください。

ドライバーを使用してマルチドキュメントトランザクションを実行できます。

注意

マルチドキュメントトランザクションを実行するには、MongoDB バージョン 4.0 以降を使用する必要があります。

制限事項の詳細なリストについては、サーバー マニュアルの「トランザクションと操作」セクションを参照してください。

MongoDB では、マルチドキュメントトランザクションはクライアント セッション 中に実行されます。 クライアント セッションは、順番に実行されるよう関連付けられた読み取り操作または書込み操作のグループです。 毎回新しいクライアントをインスタンス化するのではなく、クライアントを複数のセッションやトランザクションで再利用することをお勧めします。

過半数の読み取りおよび書込み保証(write concern)と組み合わせると、ドライバーは操作間の因果一貫性を保証できます。 詳細については、 「クライアント セッションと因果整合性の保証」に関するサーバー マニュアル ガイドを参照してください。

ドライバーを使用して MongoDB でマルチドキュメントトランザクションを実行する方法の詳細については、このガイドの次のセクションを参照してください。

Core APIを使用して、ドライバーとのトランザクションを実装します。 Core API を使用するには、トランザクションの開始点とコミット点を宣言します。

Core API には、トランザクションを開始、キャンセル、コミットするためのメソッドが用意されています。 トランザクションをコミットするときは、操作からの変更を不可分的に行うための リクエストをサーバーに送信します。 この API を使用する場合は、サーバーによって返された特定のトランザクション エラーを手動で処理する必要があります。

これらのエラーの詳細については、「 TransientTransactionErrorUnknownTransactionCommitResultを参照してください。

トランザクションを開始、キャンセル、コミットするには、 Sessionオブジェクトで対応するメソッドを呼び出します。

  • startTransaction()

  • commitTransaction()

  • abortTransaction()

サンプル トランザクションの実装については、「 Core API の例」を参照してください。

トランザクションをインスタンス化するときに、次のオプションを指定して、そのトランザクションのデフォルトの動作を設定できます。

設定
説明
readConcern

読み取り操作がレプリカセットから取得するデータの整合性をチェックする方法を指定します。

詳細については、サーバー マニュアルの「読み取り保証」を参照してください。

writeConcern
書込み (write) を確認する条件を指定します。 詳細については、サーバー マニュアルの「書込み保証」を参照してください。
readPreference
詳細については、サーバー マニュアルの「読み込み設定」を参照してください。
maxCommitTimeMS
トランザクション上でのコミット アクションの実行を許可する最大時間をミリ秒単位で指定します。

値を指定しない場合、ドライバーはクライアント設定を使用します。

次のようなコードを使用して、Core API でトランザクション オプションを指定できます。

const transactionOptions = {
readPreference: 'primary',
readConcern: { level: 'local' },
writeConcern: { w: 'majority' },
maxCommitTimeMS: 1000
};
session.startTransaction(transactionOptions);

カスタマーがオンライン ストアから商品を購入するシナリオを考えてみましょう。 購入を記録するには、アプリケーションは在庫、カスタマーの注文に関連する情報を更新し、注文詳細を登録する必要があります。 データの更新を次のように整理するとします。

コレクション
操作
変更の説明
orders
insert
購入情報を記録する
customers
update
注文 ID を追加して、カスタマーに関連付けます
inventory
update
注文商品の減算

在庫アイテムの量が不足している場合、注文を完了できなかった場合、支払いシステムがオフラインの場合など、さまざまな方法で購入が失敗する可能性があります。

支払いが失敗した場合は、トランザクションを使用して、そのデータに依存する他の操作でデータ整合性の問題が発生する可能性のある部分的な更新を公開しないようにできます。

コード例では、マルチドキュメント支払いトランザクションを実行するために、 testdbデータベース内の次のサンプル データが必要です。

  • カスタマーとその注文を説明するcustomersコレクション内のドキュメント。

  • それぞれが食材の数量と説明を追跡するinventoryコレクション内のドキュメント。

この例のcustomersコレクション内のドキュメントには、次のものが含まれています。

{ _id: 98765, orders: [] }

この例のinventoryコレクション内のドキュメントには、次のものが含まれています。

[
{ name: "sunblock", sku: 5432, qty: 85 },
{ name: "beach towel", sku: 7865, qty: 41 }
]

これらのコード例ではordersコレクションに対して操作も実行されますが、事前のサンプル ドキュメントは必要ありません。

コード例では、 cartpayment変数を使用して、購入されたアイテムのサンプル リストと注文支払いの詳細を次のように表します。

const cart = [
{ name: 'sunblock', sku: 5432, qty: 1, price: 5.19 },
{ name: 'beach towel', sku: 7865, qty: 2, price: 15.99 }
];
const payment = { customer: 98765, total: 37.17 };

重要

次のセクションの例では、トランザクションの外部でコレクションを作成するか、MongoDB 4.4以降を使用している必要があります。 トランザクション内でのコレクション作成の詳細については、「 トランザクション内でのコレクションとインデックスの作成 」ガイドを参照してください。

このセクションのコード例は、Core API を使用してセッション内でマルチドキュメント支払いトランザクションを実行する方法を示しています。 この関数では、次の操作を実行する方法が表示されます。

  1. セッションを開始する

  2. トランザクション オプションを指定してトランザクションを開始

  3. 同じセッションでデータ操作を実行

  4. トランザクションをコミットするか、ドライバーでエラーが発生した場合はキャンセルします

  5. セッションを終了する

1async function placeOrder(client, cart, payment) {
2 const transactionOptions = {
3 readConcern: { level: 'snapshot' },
4 writeConcern: { w: 'majority' },
5 readPreference: 'primary'
6 };
7
8 const session = client.startSession();
9 try {
10 session.startTransaction(transactionOptions);
11
12 const ordersCollection = client.db('testdb').collection('orders');
13 const orderResult = await ordersCollection.insertOne(
14 {
15 customer: payment.customer,
16 items: cart,
17 total: payment.total,
18 },
19 { session }
20 );
21
22 const inventoryCollection = client.db('testdb').collection('inventory');
23 for (let i=0; i<cart.length; i++) {
24 const item = cart[i];
25
26 // Cancel the transaction when you have insufficient inventory
27 const checkInventory = await inventoryCollection.findOne(
28 {
29 sku: item.sku,
30 qty: { $gte: item.qty }
31 },
32 { session }
33 )
34 if (checkInventory === null) {
35 throw new Error('Insufficient quantity or SKU not found.');
36 }
37
38 await inventoryCollection.updateOne(
39 { sku: item.sku },
40 { $inc: { 'qty': -item.qty }},
41 { session }
42 );
43 }
44
45 const customerCollection = client.db('testdb').collection('customers');
46 await customerCollection.updateOne(
47 { _id: payment.customer },
48 { $push: { orders: orderResult.insertedId }},
49 { session }
50 );
51 await session.commitTransaction();
52 console.log('Transaction successfully committed.');
53
54 } catch (error) {
55 if (error instanceof MongoError && error.hasErrorLabel('UnknownTransactionCommitResult')) {
56 // add your logic to retry or handle the error
57 }
58 else if (error instanceof MongoError && error.hasErrorLabel('TransientTransactionError')) {
59 // add your logic to retry or handle the error
60 } else {
61 console.log('An error occured in the transaction, performing a data rollback:' + error);
62 }
63 await session.abortTransaction();
64 } finally {
65 await session.endSession();
66 }
67}

当該のセッションで実行したい各 CRUD 操作にセッション オブジェクトを渡す必要があることに注意してください。

catchブロックのコードとコメントは、サーバー トランザクション エラーを識別する方法と、それを処理するロジックを配置する場所を示しています。 次のサンプル インポート ステートメントに示すように、コードにドライバーのMongoError型を必ず含めてください。

const { MongoError, MongoClient } = require('mongodb');

トランザクションを実行した後にコレクションに含まれる内容を確認するには、「支払いトランザクションの結果」セクションを参照してください。

アプリケーションが支払いトランザクションを完了した場合、データベースにはすべての更新が含まれます。例外によってトランザクションが中断された場合は、データベースに変更はいずれも存在しません。

customersコレクションには、注文フィールドに注文 ID が追加されたカスタマー ドキュメントが含まれている必要があります。

{
"_id": 98765,
"orders": [
"61dc..."
]
}

inventory

[
{
"_id": ...,
"name": "sunblock",
"sku": 5432,
"qty": 84
},
{
"_id": ...,
"name": "beach towel",
"sku": 7865,
"qty": 39
}
]

ordersコレクションには、注文と支払いの情報が含まれています。

[
{
"_id": "...",
"customer": 98765,
"items": [
{
"name": "sunblock",
"sku": 5432,
"qty": 1,
"price": 5.19
},
{
"name": "beach towel",
"sku": 7865,
"qty": 2,
"price": 15.99
}
],
"total": 37.17
}
]

戻る

集計