Overview
En esta guía, aprenderá a usar Mongoid para realizar transacciones. Las transacciones permiten realizar una serie de operaciones que modifican los datos solo si se confirma la transacción completa. Si alguna operación de la transacción no se realiza correctamente, el controlador la detiene y descarta todos los cambios de datos antes de que sean visibles. Esta función se denomina atomicidad.
En MongoDB, las transacciones se ejecutan en sesiones lógicas. Una sesión es una agrupación de operaciones de lectura o escritura relacionadas que se ejecutan secuencialmente. Las sesiones permiten la coherencia causal para un grupo de operaciones, lo que significa que todos los procesos de la aplicación coinciden en el orden de las operaciones causalmente relacionadas.
Las sesiones permiten ejecutar operaciones en una transacción compatible con ACID que cumple con las expectativas de atomicidad, consistencia, aislamiento y durabilidad. MongoDB garantiza que los datos involucrados en las operaciones de transacción se mantengan consistentes, incluso si las operaciones presentan errores inesperados.
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: Debe gestionar el ciclo de vida de la transacción. Puede usar esta API en Mongoid v6.4 y versiones posteriores.
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
Puede usar la API de Transacciones de Alto Nivel para gestionar internamente el ciclo de vida de su transacción. Esta API confirma o finaliza la transacción e incorpora lógica de gestión de errores.
Puede iniciar una transacción llamando al transaction método en una instancia de un modelo, en la clase del modelo o en un módulo Mongoid.
Cuando llama al método transaction, Mongoid realiza las siguientes tareas:
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 invoca cualquier after_commit devolución de llamada para todos los objetos modificados dentro de la transacción. Si se produce un error y la transacción se revierte, Mongoid invoca cualquier after_rollback devolución de llamada para todos los objetos modificados dentro de la transacción. Para obtener más información sobre estas devoluciones de llamada y su comportamiento, consulte la sección "Devoluciones de llamada" 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 múltiples 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
Solo las operaciones del mismo cliente entran en el ámbito de una transacción, ya que cada transacción está asociada a un cliente específico. Asegúrese de utilizar objetos del mismo cliente dentro del bloque de 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
Cuando llama 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 en los datos. Mongoid muestra todas las excepciones excepto la excepción Mongoid::Errors::Rollback. Puede generar esta excepción en su aplicación para finalizar la transacción explícitamente sin que se le devuelva la excepción. Por ejemplo, podría implementar esta excepción de transacción para finalizar una transacción cuando no se cumple una condición determinada, pero sin generar un mensaje de excepción.
Devoluciones de llamada
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 se confirma la transacción si el objeto fue modificado dentro de la transacción.
Después de que el objeto persiste si el objeto fue modificado fuera del bloque de transacción.
La devolución de llamada after_commit se activa solo después de que todas las demás se hayan ejecutado correctamente. Por lo tanto, si un objeto se modifica fuera de una transacción, es posible que persista, pero la devolución de llamada after_commit no se active. Esto podría ocurrir, por ejemplo, si Mongoid generara una excepción en la devolución de llamada after_save, ya que esta debe completarse correctamente 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ía para personalizar devoluciones de llamadas para modelos de datos.
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, puede iniciar una transacción llamando al método start_transaction en una sesión. Al usar esta API, debe confirmar o finalizar la transacción manualmente. Puede usar los métodos commit_transaction y abort_transaction en la instancia de sesión para administrar 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 de 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 finaliza e incluye una transacción abierta, la transacción finaliza automáticamente.
Reintento de transacción
Puede reintentar la confirmación de la transacción si falla inicialmente. El siguiente ejemplo muestra cómo reintentar la transacción cuando Mongoid genera 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 realizar operaciones dentro de una transacción, se debe usar el mismo cliente en el que se inició la sesión. De forma predeterminada, todas las operaciones se realizan con el cliente predeterminado.
Para utilizar explícitamente un cliente diferente, utilice 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 forma similar a como se realiza una transacción. Puedes llamar al método with_session en una clase de modelo o en una instancia de un modelo y realizar operaciones en un bloque. Todas las operaciones del bloque se realizarán en el contexto de una sola sesión.
Se aplican las siguientes limitaciones al utilizar sesiones:
No se puede compartir una sesión entre subprocesos. Las sesiones no son seguras para subprocesos.
No se pueden anidar sesiones. Por ejemplo, no se puede llamar al método
with_sessionde una clase de modelo dentro del bloque pasado al métodowith_sessionde otra clase de modelo. El siguiente código muestra una sesión anidada que genera 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 modelo utilizadas en el bloque de sesión deben usar el mismo cliente controlador. Por ejemplo, si especifica un cliente distinto para un modelo utilizado en el bloque que el del modelo o la instancia en la que se invocó
with_session, Mongoid devuelve 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 y 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, consulte los detalles del constructor de la clase Session en la documentación de la API del controlador Ruby.
Alternativamente, puede utilizar el método with_session en una instancia de un 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 una instancia de Book, 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 obtener más información sobre las transacciones, consulte Transacciones en el manual del servidor.
Para obtener más información sobre cómo realizar operaciones CRUD, consulta la guía Realiza operaciones de datos.