Transações
Nesta página
Visão geral
Neste guia, você pode aprender a usar o driver Node.js para executar transações. As transações permitem que você execute uma série de operações que não alteram nenhum dado até que toda a transação seja confirmada. Se qualquer operação na transação falhar, o driver encerra a transação e descarta todas as alterações de dados antes que elas se tornem visíveis. Esse recurso é chamado de atomicidade.
Como todas as operações de gravação em um único documento no MongoDB são atômicas, convém usar transações para fazer uma alteração atômica que modifique vários documentos. Essa situação exige uma transação multidocumento. As transações de vários documentos são compatíveis com ACID porque o MongoDB garante que os dados envolvidos em suas operações de transação permaneçam consistentes, mesmo se o driver encontrar erros inesperados.
Para saber mais sobre conformidade com ACID e ACID transactions, consulte nosso artigo sobre transações ACID.
Observação
Para executar uma transação de vários documentos, você deve estar conectado a uma implementação executando o Servidor MongoDB versão 4.0 ou posterior.
Para obter uma lista detalhada das limitações, consulte a seção Transações e operações no manual do servidor.
No MongoDB, transações de vários documentos são executadas dentro de uma sessão de cliente. Uma sessão de cliente é um agrupamento de operações de leitura ou escrita relacionadas que você deseja executar sequencialmente. Recomendamos que você reutilize seu cliente para várias sessões e transações, em vez de fazer a instância de um novo cliente a cada vez.
Quando combinado com majority
preocupações de leitura e gravação, o driver garante a consistência causal entre as operações. Para saber mais, consulte Sessões de cliente e garantias de consistência causal no manual do servidor.
Saiba mais sobre como usar o driver para realizar transações com vários documentos nas seguintes seções deste guia:
APIs de transações
O driver fornece duas APIs para realizar transações, a API Principal e a API de Transações Convenientes.
A API principal é uma estrutura que permite criar, confirmar e encerrar transações. Ao usar esta API, você deve executar explicitamente as seguintes ações:
Crie, confirme e encerre a transação.
Crie e finalize a sessão na qual você executa a transação.
Implemente lógica de tratamento de erros.
A Convenient Transaction API é uma estrutura que permite que você execute transações sem ser responsável por confirmá-las ou encerrá-las. Essa API incorpora automaticamente a lógica de tratamento de erros para tentar novamente as operações quando o servidor gera determinados tipos de erro. Para saber mais sobre esse comportamento, consulte a seção Erros de transação deste guia.
Importante
Quando você se conecta ao MongoDB Server versão 4.2 ou anterior, você pode executar operações de gravação em uma transação somente em coleções que já existem. Quando você se conecta ao MongoDB Server versão 4.4 e posterior, o servidor cria automaticamente coleções conforme necessário quando você executa operações de gravação em uma transação. Para saber mais sobre esse comportamento, consulte Criar coleções e índices em uma transação no manual do servidor.
Core API
A API central fornece os seguintes métodos para implementar transações:
startSession(): cria uma nova
ClientSession
instânciastartTransaction(): inicia uma nova transação
commitTransaction (): confirma a transação ativa na sessão em que ela foi criada
abortTransaction(): termina a transação ativa na sessão em que foi criada
endSession(): termina a sessão ativa
Você deve executar as seguintes etapas ao usar esta API:
Passe a instância de sessão para cada operação que deseja executar nessa sessão.
Implemente um bloco
catch
no qual você identifica erros de transação do servidor e implementa a lógica de tratamento de erros.
O código a seguir demonstra como realizar uma transação usando a API Central:
async function coreTest(client) { const session = client.startSession(); try { session.startTransaction(); const savingsColl = client.db("bank").collection("savings_accounts"); await savingsColl.findOneAndUpdate( {account_id: "9876"}, {$inc: {amount: -100 }}, { session }); const checkingColl = client.db("bank").collection("checking_accounts"); await checkingColl.findOneAndUpdate( {account_id: "9876"}, {$inc: {amount: 100 }}, { session }); // ... perform other operations await session.commitTransaction(); console.log("Transaction committed."); } catch (error) { console.log("An error occurred during the transaction:" + error); await session.abortTransaction(); } finally { await session.endSession(); } }
Importante
Usar uma Sessão com o Cliente Que a Iniciou
O driver lançará um erro se você fornecer uma sessão de uma instância de MongoClient
para uma instância de cliente diferente.
Por exemplo, o código a seguir gera um erro MongoInvalidArgumentError
porque cria uma instância ClientSession
do cliente client1
, mas fornece essa sessão ao cliente client2
para uma operação de gravação:
const session = client1.startSession(); client2.db('myDB').collection('myColl').insertOne({ name: 'Jane Eyre' }, { session });
Para ver um exemplo totalmente executável que usa essa API, consulte o exemplo de uso Usar a Core API .
API de transação conveniente
A API de transação conveniente oferece os seguintes métodos para implementar transações:
withSession(): Executa a chamada de resposta passada em uma sessão. A API lida com a criação e o encerramento da sessão automaticamente.
withTransaction(): Executa a chamada de resposta passada a ela dentro de uma transação e chama o método
commitTransaction()
quando a chamada de resposta retorna.
Esses métodos retornam o valor que o retorno de chamada retorna. Por exemplo, se um retorno de chamada que você passar para o método withTransaction()
retornar o documento { hello: "world" }
, então o método withTransaction()
também retornará esse documento.
Importante
Para evitar erros de loop infinito, certifique-se de que a chamada de resposta de resposta que você passa para o método withTransaction()
capture todos os erros que ela gera.
Ao usar a API de transação conveniente, você pode propagar os valores de retorno da chamada de resposta como os valores de retorno dos métodos withTransaction()
e withSession()
para trabalhar com eles em outras partes do seu código.
Você deve executar as seguintes etapas ao usar esta API:
Passe a instância de sessão para cada operação que deseja executar nessa sessão.
Implemente a sintaxe assíncrona
await
para cada operação na sessão.Evite paralelismo, como chamar o método
Promise.all()
. Usar sessões em paralelo geralmente leva a erros no servidor.
O código a seguir demonstra como realizar uma transação usando a API de Transação Conveniente:
async function convTest(client) { let txnRes = await client.withSession(async (session) => session.withTransaction(async (session) => { const savingsColl = client.db("bank").collection("savings_accounts"); await savingsColl.findOneAndUpdate( {account_id: "9876"}, {$inc: {amount: -100 }}, { session }); const checkingColl = client.db("bank").collection("checking_accounts"); await checkingColl.findOneAndUpdate( {account_id: "9876"}, {$inc: {amount: 100 }}, { session }); // ... perform other operations return "Transaction committed."; }, null) ); console.log(txnRes); }
Para ver um exemplo totalmente executável que usa essa API, consulte o exemplo de uso API de transação conveniente.
Observação
Operações paralelas não suportadas
O driver Node.js não suporta a execução de operações paralelas dentro de uma única transação.
opções de transação
Você pode passar uma instância TransactionOptions
para os métodos startTransaction()
e withTransaction()
para configurar como o driver executa uma transação. Quando você especifica uma opção, ela substitui o valor da opção que você pode ter definido em sua instância MongoClient
.
A tabela seguinte inclui opções que você pode especificar em uma instância do TransactionOptions
:
Contexto | Descrição |
---|---|
| Specifies read operation consistency of the replica set. To learn more, see Read Concern in the Server manual. |
| Specifies the write operation level of acknowledgment required
from a replica set. To learn more, see Write Concern in the Server manual. |
| Specifies how to route read operations to members of a replica set. To learn more, see Read Preference in the Server manual. |
| Especifica o tempo de execução de uma ação de confirmação em uma transação, em milissegundos. |
Para obter uma lista completa de opções, consulte a documentação da API para TransactionOptions.
Observação
A transação herda as configurações de sua instância MongoClient
, a menos que você as especifique em suas opções de transação.
O código a seguir mostra como definir e passar as opções de transação para o método startTransaction()
:
const txnOpts = { readPreference: 'primary', readConcern: { level: 'local' }, writeConcern: { w: 'majority' }, maxCommitTimeMS: 1000 }; session.startTransaction(txnOpts);
Erros de transação
Se você estiver usando a API Central para executar uma transação, deverá incorporar a lógica de tratamento de erros em seu aplicativo para os seguintes erros:
TransientTransactionError
: gerado se uma operação de gravação falhar antes do driver confirmar a transação. Para saber mais sobre esse erro, consulte a descrição TransientTransactionError na página API do Driver no manual do servidor.UnknownTransactionCommitResult
: Criado se a operação de confirmação encontrar um erro. Para saber mais sobre esse erro, consulte a descrição UnknownTransactionCommitResult na página API do driver no manual do servidor.
A API Convenient Transaction incorpora a lógica de repetição para esses tipos de erro, de modo que o driver repita a transação até que haja uma confirmação bem-sucedida.