Overview
En esta guía, puedes aprender a usar Mongoid para realizar transacciones. Las transacciones te permiten realizar una serie de operaciones que cambian los datos solo si toda la transacción se completa. Si alguna operación en la transacción no tuvo éxito, el controlador detiene la transacción y descarta todos los cambios de datos antes de que lleguen a ser visibles. Esta funcionalidad se llama atomicidad.
En MongoDB, las transacciones se ejecutan dentro de sesiones lógicas. Una sesión es una agrupación de operaciones de lectura o escritura relacionadas que se desean ejecutar secuencialmente. Las sesiones permiten la coherencia causal para un grupo de operaciones, lo que significa que todos los procesos en tu aplicación están de acuerdo sobre el orden de las operaciones relacionadas causalmente.
Las sesiones le permiten ejecutar operaciones en una transacción compatibles con ACID que cumplen con las expectativas de atomicidad, coherencia, aislamiento y durabilidad. MongoDB garantiza que los datos involucrados en las operaciones de tus transacciones permanezcan coherentes, incluso si las operaciones encuentran errores imprevistos.
En Mongoid, puedes realizar transacciones utilizando cualquiera de las siguientes API:
API de transacciones de alto nivel: Mongoid gestiona el ciclo de vida de las transacciones. Puede usar esta API en Mongoid v9.0 y versiones posteriores.
API de Transacciones de Bajo Nivel: Debes gestionar el ciclo de vida de la transacción. Puedes utilizar esta API en Mongoid v6.4 o posterior.
La sección API de sesión describe cómo realizar cambios en tus datos desde dentro de una sesión sin realizar una transacción.
API de transacciones de alto nivel
Puedes usar la API de Transacciones de Alto Nivel para gestionar internamente el ciclo de vida de tu transacción. Esta API o bien confirma tu transacción o la finaliza e incorpora lógica de manejo de errores.
Puedes comenzar una transacción llamando al transaction método en una instancia de un modelo, en la clase modelo o en un módulo Mongoid.
Cuando llamas al método transaction, Mongoid realiza las tareas siguientes:
Crea una sesión en el cliente.
Inicia una transacción en la sesión.
Realiza los cambios de datos especificados.
Confirma la transacción en la base de datos si no se producen errores o finaliza la transacción si hay un error.
Cierra la sesión.
Si su transacción se confirma, Mongoid llama a cualquier after_commit función de retorno para todos los objetos modificados dentro de la transacción. Si ocurre un error y la transacción se revierte, Mongoid llama a cualquier after_rollback función de retorno para todos los objetos modificados dentro de la transacción. Para aprender más sobre estas funciones de retorno y su comportamiento, consulta la sección funciones de retorno de esta guía.
Ejemplo
Este ejemplo utiliza los siguientes modelos para representar documentos que describen libros y películas:
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
El siguiente código demuestra cómo realizar una transacción en diferentes objetos para cambiar datos en varias colecciones:
# 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
Comportamiento del cliente
Sólo las operaciones sobre el mismo cliente están en el ámbito de una transacción, porque cada transacción está vinculada a un cliente específico. Asegúrese de utilizar objetos del mismo cliente dentro del bloque del método de transacción.
El siguiente ejemplo define clases modelo que utilizan diferentes clientes y demuestra cómo se ejecutan las operaciones en función del cliente de origen:
# 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
Nota
Al llamar al método transaction en el módulo Mongoid, Mongoid crea la transacción utilizando el cliente :default.
Finalización de transacciones
Cualquier excepción generada dentro del bloque del método de transacción finaliza la transacción y revierte los cambios de datos. Mongoid muestra todas las excepciones excepto la excepción Mongoid::Errors::Rollback. Puedes activar esta excepción en tu aplicación para finalizar explícitamente la transacción sin que la excepción se te devuelva. Por ejemplo, puedes implementar esta excepción de transacción para finalizar una transacción cuando no se cumple una determinada condición, pero sin generar un mensaje de excepción.
función de retorno
Esta API de transacción presenta las devoluciones de llamadas after_commit y after_rollback.
Mongoid activa la devolución de llamada after_commit para un objeto que se creó, guardó o eliminó en los siguientes casos:
Después de que la transacción se haya confirmado, si el objeto fue modificado dentro de la transacción.
Después de que el objeto se conserve si el objeto fue modificado fuera del bloque de transacción.
La after_commit función de retorno se activa solo después de que todas las demás funciones de retorno se completan con éxito. Por ende, si un objeto se modifica fuera de una transacción, es posible que luego se persista el objeto, pero no se dispare la función de retorno after_commit. Esto puede ocurrir, por ejemplo, si Mongoid lanza una excepción en el after_save función de retorno porque este función de retorno debe completarse con éxito para activar after_commit.
La devolución de llamada after_rollback se activa para un objeto creado, guardado o eliminado dentro de una transacción si esta no se realizó correctamente y se revirtieron los cambios. Mongoid nunca activa after_rollback fuera de una transacción.
Para obtener más información sobre las devoluciones de llamadas, consulte la Guíapara personalizar las funciones de devolución de llamada para modelos.
API de Transacción de bajo nivel
Al usar la API de bajo nivel, debe crear una sesión antes de iniciar una transacción. Puede crear una sesión llamando al método with_session en una clase de modelo o en una instancia de un modelo.
Luego, puedes iniciar una transacción llamando al método start_transaction en una sesión. Al utilizar esta API, debe comprometerse o finalizar manualmente la transacción. Puede usar los métodos commit_transaction y abort_transaction en la instancia de sesión para gestionar el ciclo de vida de la transacción.
Ejemplo
Este ejemplo utiliza la API de transacciones de bajo nivel para realizar las siguientes acciones:
Crea una sesión
inicia una transacción
Realiza operaciones sobre datos
Confirma la transacción o la finaliza si hay errores
# 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
Nota
Si una sesión termina e incluye una transacción abierta, la transacción finaliza automáticamente.
Reintento de transacción
Puedes reintentar la confirmación de la transacción si inicialmente falla. El siguiente ejemplo demuestra cómo reintentar la transacción cuando Mongoid lanza la excepción 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
opciones
Puede especificar una preocupación de lectura, preocupación de escritura o preferencia de lectura al iniciar una transacción pasando opciones al método start_transaction:
session.start_transaction( read_concern: {level: :majority}, write_concern: {w: 3}, read: {mode: :primary} )
Para obtener más información sobre las opciones de transacción disponibles, consulte start_transaction en la documentación de la API del controlador Ruby.
Comportamiento del cliente
Para efectuar operaciones dentro de una transacción, las operaciones deben usar el mismo cliente en el que se inició la sesión. De forma predeterminada, todas las operaciones se realizan utilizando el cliente por defecto.
Para utilizar explícitamente un cliente diferente, utiliza el método 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
Session API
Puedes usar sesiones en Mongoid de manera similar a como puedes realizar una transacción. Puedes llamar al método with_session en una clase de modelo o en una instancia de un modelo y realizar algunas operaciones en un bloque. Todas las operaciones del bloque se realizarán en el contexto de una única sesión.
Se aplican las siguientes limitaciones al usar sesiones:
No se puede compartir una sesión entre hilos. Las sesiones no son seguras para subprocesos.
No puedes anidar sesiones. Por ejemplo, no se puede llamar al método
with_sessionen una clase modelo dentro del bloque pasado al métodowith_sessionen otra clase modelo. El siguiente código demuestra una sesión anidada que resulta en un error:Book.with_session(causal_consistency: true) do # Nesting sessions results in errors Film.with_session(causal_consistency: true) do ... end end Todas las clases e instancias de modelos utilizadas dentro del bloque de sesión deben utilizar el mismo cliente del controlador. Por ejemplo, si especificas un cliente diferente para un modelo utilizado en el bloque que los del modelo o instancia en la que llamaste
with_session, Mongoid devolverá un error.
Ejemplos
Puede utilizar el método with_session en una clase modelo y pasarle opciones de sesión para realizar un bloque de operaciones en el contexto de una sesión.
El siguiente código habilita la opción causal_consistency para garantizar el orden de las operaciones al crear una sesión en el modelo Book, luego realiza operaciones de datos:
Book.with_session(causal_consistency: true) do Book.create! book = Person.first book.title = "Swann's Way" book.save end
Para obtener más información sobre las opciones de sesión disponibles, consulta los Detalles del constructor de la clase Session en la documentación de la API del driver Ruby.
Como alternativa, puedes usar el método with_session en una instancia de un modelo y pasarle opciones de sesión para ejecutar un bloque de operaciones en el contexto de una sesión.
El siguiente código habilita la opción causal_consistency para garantizar el orden de las operaciones al crear una sesión en una instancia de Book, y luego realiza operaciones de datos:
book = Book.new book.with_session(causal_consistency: true) do book.title = 'Catch-22' book.save book.sellers << Shop.create! end
Información Adicional
Para aprender más sobre transacciones, consulta Transacciones en el manual del Servidor.
Para obtener más información sobre cómo realizar operaciones CRUD, consulte la guía de operaciones CRUD.