ANNOUNCEMENT: Voyage AI joins MongoDB to power more accurate and trustworthy AI applications on Atlas.
Learn more
Docs Menu

トランザクション

MongoDB サーバーのバージョン4.0では、マルチドキュメントトランザクションが導入されています。 (単一のドキュメント内の複数のフィールドの更新は、MongoDB のすべてのバージョンでアトミックです)。 Ruby ドライバー バージョン2.6.0 トランザクションのサポートを追加します。

注意

並列操作はサポートされていません

Rubyドライバーは、単一のトランザクション内での並列操作の実行中をサポートしていません。

トランザクションを開始するには、アプリケーションにセッションが必要です。

トランザクションを使用する推奨方法は、 with_transactionヘルパー メソッドを利用することです。

session = client.start_session
session.with_transaction do
collection.insert_one({hello: 'world'}, session: session)
end

with_transactionヘルパーは次の処理を実行します。

  • 指定されたブロックを呼び出す前にトランザクションを開始し、ブロックが終了するとトランザクションをコミットします。

  • ブロック内のいずれかの操作またはコミット操作によって一時的なトランザクション エラーが発生した場合、ブロックやコミットが再度実行されます。

ブロックは複数回呼び出される可能性があるため、冪等である必要があります。

ブロックは、 commit_transactionまたはabort_transactionを呼び出して、トランザクションを明示的にコミットまたは中止することができます。この場合、 with_transactionはコミットや中止を試行しません(ただし、ブロックから発生した一時的なトランザクション エラーによってブロックが再試行される場合がある)。

トランザクションのコミット結果が不明な場合は、ブロックも再試行されます。 This may happen, for example, if the cluster undergoes an election during the commit. この場合、ブロックが再試行されると、トポロジーのプライマリ サーバーが変更されている可能性があります。

現在、 with_transactionは、実行の開始から 120 秒が経過すると、ブロックとコミットの再試行を停止します。 この時間は構成できず、将来のドライバー バージョンで変更される可能性があります。 これは、 with_transactionsの全体的な実行時間が 120 秒以下になることを保証するものではありません。ただし、ウォール クロックの時間が 120 秒経過すると、それ以上の再試行は開始されないだけです。

トランザクションをより制御する必要がある場合は、 低レベル API も使用できます。

with_transactionは、読み取り保証、書込み保証、読み込み設定(read preference)でstart_transactionと同じオプションを選択します。

session = client.start_session
session.with_transaction(
read_concern: {level: :majority},
write_concern: {w: 3},
read: {mode: :primary}
) do
collection.insert_one({hello: 'world'}, session: session)
end

with_transactionブロック内のコマンドが失敗すると、サーバー上のトランザクションが中止される可能性があります。 この状況は通常、ドライバーによって透過的に処理されます。 しかし、アプリケーションがこのようなエラーをキャッチして再度発生しない場合、ドライバーはトランザクションが中止されたかどうかを判断できなくなります。 その後、ドライバーはブロックを無期限に再試行します。

この状況を回避するには、アプリケーションはwith_transactionブロック内で暗黙的にエラーを処理しないようにする必要があります。 アプリケーションがブロック内のエラーを処理する必要がある場合は、エラーを再度発生させる必要があります。

session.with_transaction do
collection.insert_one({hello: 'world'}, session: session)
rescue Mongo::Error::OperationFailure => e
# Do something in response to the error
raise e
end

アプリケーションでエラーをカスタム方法で処理する必要がある場合は、代わりに低レベル API を使用する必要があります。

トランザクションは、セッションでstart_transactionメソッドを呼び出すことで開始できます。

session = client.start_session
session.start_transaction

トランザクションを開始するときに、読み取り保証、書込み保証、および読み込み設定(read preference)を指定することもできます。

session = client.start_session
session.start_transaction(
read_concern: {level: :majority},
write_concern: {w: 3},
read: {mode: :primary})

トランザクションで行われた変更を データベースに保持するには、トランザクションを明示的にコミットする必要があります。 オープン トランザクションでセッションが終了すると、そのトランザクションは 中止されます 。 トランザクションは明示的に中止されることもできます。

トランザクションをコミットまたは中止するには、セッション インスタンスでcommit_transactionまたはabort_transactionを呼び出します。

session.commit_transaction
session.abort_transaction

注:未処理のトランザクションは、 データベースなど、サーバー内のさまざまなオブジェクトをロックすることができます。 たとえば、次のスニペットの drop 呼び出しは、サーバーが期限切れでトランザクションを中止するまで、 transactionLifetimeLimitSeconds秒(デフォルトは60 )ハングします。

c1 = Mongo::Client.new(['127.0.0.1:27017']).use(:test_db)
session = c1.start_session
c1['foo'].insert_one(test: 1)
session.start_transaction
c1['foo'].insert_one({test: 2}, session: session)
c2 = Mongo::Client.new(['127.0.0.1:27017']).use(:test_db)
# hangs
c2.database.drop

トランザクションはサーバー側のセッションに関連付けられているため、クライアントを閉じても、このクライアントが開始したトランザクションは中止されません。アプリケーションはabort_transactionを呼び出すか、サーバー側でトランザクションがタイムアウトするまで待機する必要があります。 トランザクションをコミットまたは中止するだけでなく、アプリケーションはセッションを終了することもできます。これにより、このセッションではトランザクションが進行中の場合に中止されます。

session.end_session
c2 = Mongo::Client.new(['127.0.0.1:27017']).use(:test_db)
# ok
c2.database.drop

トランザクション内の コマンドが失敗した場合、サーバー上でトランザクションが中止される可能性があります。 トランザクションを中止するエラーのエラー ラベルにTransientTransactionErrorが含まれていない。 このようなトランザクションをコミットしようとすると、 NoSuchTransactionエラーで拒否されます。

トランザクションのコミットは失敗した場合に再試行できます 。 以下は、そのための Ruby コードです。

begin
session.commit_transaction
rescue Mongo::Error => e
if e.label?('UnknownTransactionCommitResult')
retry
else
raise
end
end

MongoDB はネストトランザクションをサポートしていません。 トランザクションがすでに進行中のときにstart_transactionまたはwith_transactionを呼び出しようとすると、エラーが発生します。