Docs Menu
Docs Home
/ / /
Mongoid
/

トランザクションとセッション

このガイドでは、Mongoid を使用して トランザクション を実行する方法を学習できます。トランザクションを使用すると、トランザクション全体がコミットされた場合にのみ、データを変更する一連の操作を実行できます。 トランザクション内のいずれかの操作が成功しない場合、ドライバーはトランザクションを停止し、変更が反映される前にすべてのデータ変更を破棄します。 この特徴は アトミック性と呼ばれます。

MongoDBでは、トランザクションは論理 セッション 内で実行されます。セッションは 、順番に実行されるよう関連付けられた読み取り操作または書き込み操作のグループです。 セッションにより、一連の操作に対する因果整合性が有効になります。つまり、アプリケーション内のすべてのプロセスが因果関連の操作の順序に一致することを意味します。

セッションを使用すると、 ACID準拠のトランザクションで操作を実行でき、アトミック性、整合性、分離、耐久性を確保できます。MongoDBは、トランザクション操作で予期せぬエラーが発生した場合でも、その操作に関わるデータの一貫性が保たれることを保証します。

Mongoid では、次のいずれかの API を使用してトランザクションを実行できます。

  • 高レベル トランザクションAPI : Mongoid がトランザクションのライフサイクルを管理します。このAPI はMongoid v9.0 以降で使用できます。

  • 低レベルトランザクションAPI : トランザクションのライフサイクルを管理する必要があります。このAPI はMongoid v6.4 以降で使用できます。

セッションAPIセクションでは、トランザクションを実行せずにセッション内からデータに変更を加える方法について説明します。

ハイレベルトランザクションAPI を使用して、トランザクションのライフサイクルを内部的に管理できます。 このAPI は、トランザクションをコミットまたは終了し、エラー処理ロジックを組み込んでいます。

トランザクションを開始するには、モデルのインスタンス、 モデルクラス、または Mongoid モジュールで transaction メソッドを呼び出します。

transaction メソッドを呼び出すと、Mongoid は次のタスクを実行します。

  1. クライアントにセッションを作成します。

  2. セッションでトランザクションを開始します。

  3. 指定されたデータ変更を実行します。

  4. エラーが発生しない場合はトランザクションをデータベースにコミットし、エラーが発生した場合はトランザクションを終了します。

  5. セッションを閉じます。

トランザクションがコミットされる場合、Mongoid はトランザクション内で変更されたすべてのオブジェクトに対して任意の after_commit コールバックを呼び出します。 エラーが発生し、トランザクションがロールバックされた場合、Mongoid はトランザクション内で変更されたすべてのオブジェクトに対して任意の after_rollback コールバックを呼び出します。 これらのコールバックとその動作の詳細については、このガイドの「 コールバック 」セクションを参照してください。

この例では、次のモデルを使用して、書籍と映画について説明するドキュメントを表します。

class Book
include Mongoid::Document
field :title, type: String
field :author, type: String
field :length, type: Integer
end
class Film
include Mongoid::Document
field :title, type: String
field :year, type: Integer
end

次のコードは、さまざまなオブジェクトに対してトランザクションを実行し、複数のコレクションのデータを変更する方法を示しています。

# Starts a transaction from the model class
Book.transaction do
# Saves new Book and Film instances to MongoDB
Book.create(title: 'Covert Joy', author: 'Clarice Lispector')
Film.create(title: 'Nostalgia', year: 1983)
end
# Starts a transaction from an instance of Book
book = Book.create(title: 'Sula', author: 'Toni Morrison')
book.transaction do
# Saves a new field value to the Book instance
book.length = 192
book.save!
end
# Starts a transaction from the Mongoid instance
Mongoid.transaction do
# Deletes the Book instance in MongoDB
book.destroy
end

各トランザクションは特定のクライアントに関連付けられているため、同じクライアントでの操作のみがトランザクションのスコープに含まれます。 トランザクション メソッド ブロック内で同じクライアントのオブジェクトを使用するようにします。

次の例では、異なるクライアントを使用するモデル クラスを定義し、元のクライアントに基づいて操作が実行される方法を示しています。

# Defines a class by using the :default client
class Post
include Mongoid::Document
end
# Defines a class by using the :encrypted_client
class User
include Mongoid::Document
store_in client: :encrypted_client
end
# Starts a transaction on the :encrypted_client
User.transaction do
# Uses the same client, so the operation is in the transaction
User.create!
# Uses a different client, so it is *not* in the transaction
Post.create!
end

注意

Mongoid モジュールで transaction メソッドを呼び出すと、Mongoid は :defaultクライアント を使用してトランザクションを作成します。

トランザクション メソッド ブロック内で発生した例外によってトランザクションが終了し、データ変更がロールバックされます。 Mongoid は Mongoid::Errors::Rollback 例外を除くすべての例外を表示します。 アプリケーション内でこの例外を発生させると、例外を返さずにトランザクションを明示的に終了できます。 例、このトランザクション例外を実装すると、特定の条件が満たされない場合に、例外メッセージを生成せずにトランザクションを終了できます。

このトランザクションAPI、after_commitafter_rollback のコールバックが導入されています。

Mongoid は、次の場合に作成、保存、または削除されたオブジェクトの after_commitコールバックをトリガーします。

  • トランザクション内でオブジェクトが変更された場合は、トランザクションがコミットされた後。

  • オブジェクトがトランザクション ブロック外で変更された場合、オブジェクトが保持された後。

after_commitコールバックは、他のすべてのコールバックが正常に実行された後にのみトリガーされます。 したがって、トランザクション外でオブジェクトが変更された場合、オブジェクトは永続化されますが、after_commitコールバックはトリガーされない可能性があります。 これは、例Mongoid が after_saveコールバックで例外を発生させた場合、after_commit をトリガーするにはこのコールバックが正常に完了する必要があるためです。

after_rollbackコールバックは、トランザクションが成功し、変更がロールバックされた場合に、トランザクション内で作成、保存、または削除されたオブジェクトに対してトリガーされます。 Mongoid では、トランザクション外で after_rollback をトリガーしません。

コールバックの詳細については、「 データモデルのコールバックのカスタマイズ 」ガイドをご覧ください。

低レベルAPIを使用する場合は、トランザクションを開始する前にセッションを作成する必要があります。 モデルクラスまたはモデルのインスタンスで with_session メソッドを呼び出すことで、セッションを作成できます。

その後、セッションで start_transaction メソッドを呼び出すことでトランザクションを開始できます。 このAPI を使用する場合は、トランザクションを手動でコミットまたは終了する必要があります。 セッションインスタンスで commit_transaction メソッドと abort_transaction メソッドを使用して、トランザクション ライフサイクルを管理できます。

この例では、低レベルのトランザクションAPI を使用して次のアクションを実行します。

  1. セッションを作成する

  2. トランザクションを開始します

  3. データ操作を実行します

  4. トランザクションをコミットします、エラーが発生した場合は終了します

# Starts a session from the model class
Book.with_session do |session|
session.start_transaction
# Creates a Book
Book.create(title: 'Siddhartha', author: 'Hermann Hesse')
# Commits the transaction
session.commit_transaction
rescue StandardError
# Ends the transaction if there is an error
session.abort_transaction
end

注意

セッションが終了し、オープンなトランザクションが含まれている場合、トランザクションは自動的に終了します。

最初に失敗した場合は、トランザクションのコミットを再試行できます。 次の例は、 Mongoid が UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL 例外を発生させた場合にトランザクションを再試行する方法を示しています。

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

トランザクションを開始するときに、読み取り保証 (read concern)、書込み保証 (write concern)、または読み込み設定 (read preference)を指定するには、start_transaction メソッドにオプションを渡します。

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

使用可能なトランザクション オプションの詳細については、 RubyドライバーAPIドキュメントの start_transaction を参照してください。

トランザクション内で操作を実行するには、操作はセッションが開始されたのと同じクライアントを使用する必要があります。 デフォルトでは 、すべての操作はデフォルトのクライアントを使用して実行されます。

別のクライアントを明示的に使用するには、 withメソッドを使用します。

# Specifies that the operation should use the "other" client instead of
# the default client
User.with(client: :other) do
Post.with(client: :other) do
Post.with_session do |session|
session.start_transaction
Post.create!
Post.create!
User.create!
session.commit_transaction
end
end
end

Mongoid では、トランザクションを実行するのと同様にセッションを使用できます。 モデルクラスまたはモデルのインスタンスで with_session メソッドを呼び出し、ブロック内で一部の操作を実行できます。 ブロック内のすべての操作は単一のセッションのコンテキストで実行されます。

セッションを使用する場合、次の制限が適用されます。

  • スレッド間でセッションを共有することはできません。 セッションはスレッドセーフではありません。

  • セッションをネストすることはできません。 例、別のモデルクラスの with_session メソッドに渡されるブロック内のモデルクラスで with_session メソッドを呼び出すことはできません。 次のコードは、エラーが発生するネストされたセッションを示しています。

    Book.with_session(causal_consistency: true) do
    # Nesting sessions results in errors
    Film.with_session(causal_consistency: true) do
    ...
    end
    end
  • セッション ブロック内で使用されるすべてのモデル クラスと インスタンスは、同じ ドライバークライアントを使用する必要があります。 例、ブロックで使用されるモデルに、with_session を呼び出したモデルまたはインスタンスとは異なるクライアントを指定すると、Mongoid はエラーを返します。

モデルクラスで with_session メソッドを使用し、そのセッション オプションを渡して、セッションのコンテキストで操作のブロックを実行できます。

次のコードでは、Book モデルでセッションを作成するときに操作順序を保証するために causal_consistency オプションを有効にしてから、データ操作を実行します。

Book.with_session(causal_consistency: true) do
Book.create!
book = Person.first
book.title = "Swann's Way"
book.save
end

使用可能なセッション オプションの詳細については、 RubyドライバーAPIドキュメントのセッションクラスコンストラクターの詳細を参照してください。

あるいは、モデルのインスタンスで with_session メソッドを使用し、そのセッションにセッション オプションを渡して、セッションのコンテキストで操作のブロックを実行することもできます。

次のコードでは、Book のインスタンスでセッションを作成するときに操作順序を保証するために causal_consistency オプションを有効にしてから、データ操作を実行します。

book = Book.new
book.with_session(causal_consistency: true) do
book.title = 'Catch-22'
book.save
book.sellers << Shop.create!
end

トランザクションの詳細については、サーバー マニュアルの「 トランザクション 」を参照してください。

CRUD操作の実行の詳細については、 「データ操作の実行」ガイドを参照してください。

戻る

検索テキスト

項目一覧