Menu Docs
Página inicial do Docs
/ / /
Driver Ruby MongoDB
/

Transações

Nesta página

  • Usando transação
  • API de baixo nível
  • Tentando novamente os commits
  • Aninhamento de transação

Versão 4.0 do servidor MongoDB introduz transações multidocumento. (As atualizações de vários campos em um único documento são atômicas em todas as versões do MongoDB.) Ruby driver version 2.6.0 adiciona suporte para transações.

Para iniciar uma transação, o aplicativo deve ter umasessão .

A maneira recomendada de usar transação é utilizar o método auxiliar do with_transaction :

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

O auxiliar do with_transaction faz o seguinte:

  • Ele inicia uma transação antes de chamar o bloco fornecido e confirma a transação quando o bloco é concluído.

  • Se qualquer uma das operações no bloco, ou a operação de confirmação, resultar em um erro de transação transitório, o bloco e/ou o commit serão executados novamente.

O bloqueio deve ser idempotente, pois pode ser chamado várias vezes.

O bloco pode confirmar ou abortar explicitamente a transação, ligando para commit_transaction ou abort_transaction; nesse caso, with_transaction não tentará cometer ou abortar (mas ainda pode tentar novamente o bloco em caso de erros de transação transitórios propagados para fora do bloco).

O bloqueio também será repetido se o resultado do commit da transação for desconhecido. Isso pode acontecer, por exemplo, se o cluster passar por uma eleição durante o commit. Nesse caso, quando o bloqueio fosse repetido, o servidor principal da topologia provavelmente teria mudado.

Atualmente, with_transaction parará de tentar novamente o bloqueio e a confirmação quando 120 segundos se passarem desde o início de sua execução. Este tempo não é configurável e pode mudar em uma versão futura do driver. Observe que isso não garante que o tempo de execução geral de with_transactions seja de 120 segundos ou menos - apenas que, depois de decorridos 120 segundos do tempo do relógio de parede, outras tentativas de repetição não serão iniciadas.

Uma API de baixo nível também estará disponível se desejar mais controle sobre a transação.

with_transaction usa as mesmas opções que start_transaction , que são referência de leitura, referência de escrita e preferência de leitura:

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

Se um comando dentro do bloco with_transaction falhar, ele poderá fazer com que a transação no servidor seja cancelada. Essa situação normalmente é tratada de forma transparente pelo driver. No entanto, se o aplicativo detectar esse erro e não o aumentar novamente, o driver não poderá determinar se a transação foi cancelada ou não. Depois, o driver tentará novamente o bloqueio indefinidamente.

Para evitar essa situação, o aplicativo não deve lidar silenciosamente com erros dentro de with_transaction blocos. Se o aplicativo precisar lidar com erros dentro do bloco, ele deverá gerar os erros novamente.

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

Se os aplicativos precisarem lidar com erros de forma personalizada, eles devem usar a API de baixo nível.

Uma transação pode ser iniciada chamando o método start_transaction em uma sessão:

session = client.start_session
session.start_transaction

Também é possível especificar a referência de leitura, referência de escrita e preferência de leitura ao iniciar uma transação:

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

Para persistir as alterações feitas em uma transação no banco de dados, a transação deve ser explicitamente confirmada. Se uma sessão terminar com uma transação aberta, a transação será cancelada. Uma transação também pode ser abortada explicitamente.

Para confirmar ou cancelar uma transação, chame commit_transaction ou abort_transaction na instância da sessão:

session.commit_transaction
session.abort_transaction

Observação: uma transação pendente pode conter travas em vários objetos no servidor, como o banco de dados. Por exemplo, a chamada de queda no trecho a seguir ficará pendurada por transactionLifetimeLimitSeconds segundos (padrão 60) até que o servidor expire e cancele a transação:

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

Since transactions are associated with server-side sessions, closing the client does not abort a transaction that this client initiated - the application must either call abort_transaction or wait for the transaction to time out on the server side. Além de confirmar ou cancelar a transação, um aplicativo também pode encerrar a sessão, o que cancelará uma transação nessa sessão, se houver uma em andamento:

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

Se um comando dentro da transação falhar, a transação poderá ser cancelada no servidor. Os erros que abortam transações não têm TransientTransactionError em seus rótulos de erro. Uma tentativa de confirmar essa transação será rejeitada com NoSuchTransaction erro.

A confirmação da transação pode ser repetida se falhar. Aqui está o código Ruby para fazer isso:

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

O MongoDB não oferece suporte a aninhamento de transação. Tentar ligar para start_transaction ou with_transaction quando uma transação já está em andamento resultará em um erro.

Voltar

Sessões