Isolamento e capacidade de serialização de transações

Nesta página, descrevemos o isolamento, a capacidade de serialização e a disputa de dados transacionais. Para ver exemplos de códigos de transações, consulte transações e gravações em lote.

Transações e disputa de dados

Para que uma transação seja bem-sucedida, os documentos recuperados pelas operações de leitura precisam permanecer inalterados por operações fora da transação. Se outra operação tentar alterar um desses documentos, as operações entrarão em um estado de disputa de dados com a transação.

Disputa de dados
Quando duas ou mais operações competem para controlar o mesmo documento. Por exemplo, uma transação pode exigir que um documento permaneça consistente enquanto uma operação simultânea tenta atualizar os valores do campo desse documento.

O Firestore resolve a contenção de dados atrasando ou apresentando falhas em uma das operações. As bibliotecas de cliente do Firestore repetem automaticamente as transações que falham devido à contenção de dados. Após um número finito de novas tentativas, a operação de transação falha e retorna uma mensagem de erro:

ABORTED: Too much contention on these documents. Please try again.

Ao decidir qual operação terá uma falha ou sofrerá um atraso, o comportamento depende do tipo de biblioteca de cliente.

  • Os SDKs para dispositivos móveis/Web usam controles de simultaneidade otimistas.

  • As bibliotecas de cliente do servidor usam controles de simultaneidade pessimistas.

Disputa de dados nos SDKs para dispositivos móveis/Web

Os SDKs para dispositivos móveis/Web (plataformas da Apple, Android, Web, C++) usam controles de simultaneidade otimistas para resolver a disputa de dados.

Controles de simultaneidade otimistas
Supõem que a disputa de dados não seja provável ou que não seja eficiente manter bloqueios do banco de dados. As transações otimistas não usam bloqueios de banco de dados para impedir que outras operações alterem dados.

Os SDKs para dispositivos móveis/Web usam controles de simultaneidade otimistas porque podem operar em ambientes com alta latência e uma conexão de rede não confiável. O bloqueio de documentos em um ambiente de alta latência causaria muitas falhas de disputa de dados.

Nos SDKs para dispositivos móveis/Web, uma transação monitora todos os documentos lidos dentro dela. A transação concluirá as operações de gravação somente se nenhum desses documentos for alterado durante a execução da transação. Se algum documento tiver sido alterado, o gerenciador de transações repetirá a transação. Se não for possível conseguir o resultado desejado após algumas tentativas, a transação falhará devido à disputa de dados.

Disputa de dados nas bibliotecas de cliente do servidor

As bibliotecas de cliente do servidor (C#, Go, Java, Node.js, PHP, Python, Ruby) usam controles de simultaneidade pessimistas para resolver a disputa de dados.

Controles de simultaneidade pessimistas
Supõem que a disputa de dados seja provável. Essas transações usam bloqueios do banco de dados para evitar que outras operações modifiquem dados.

As bibliotecas de cliente do servidor usam controles de simultaneidade pessimistas porque pressupõem baixa latência e uma conexão confiável com o banco de dados.

Nas bibliotecas de clientes do servidor, as transações aplicam bloqueios nos documentos lidos. O bloqueio de uma transação em um documento impede que outras transações, gravações em lote e gravações não transacionais alterem o documento. Uma transação libera os bloqueios de documentos no momento da confirmação. Ela também libera os bloqueios quando eles expiram ou falham por algum motivo.

Quando uma transação bloqueia um documento, outras operações de gravação precisam esperar que a transação remova o bloqueio. As transações adquirem os bloqueios em ordem cronológica.

Isolamento serializável

A disputa de dados entre transações está intimamente relacionada aos níveis de isolamento do banco de dados. O nível de isolamento de um banco de dados descreve como o sistema gerencia conflitos entre operações simultâneas. Os conflitos podem vir dos seguintes requisitos de banco de dados:

  • As transações exigem dados precisos e consistentes.
  • Para usar recursos com eficiência, os bancos de dados executam operações ao mesmo tempo.

Em sistemas com baixo nível de isolamento, uma operação de leitura em uma transação pode ler dados imprecisos de alterações não confirmadas em uma operação simultânea.

O isolamento serializável define o maior nível de isolamento. Isolamento serializado significa que:

  • você pode presumir que o banco de dados executa transações em série;
  • as transações não são afetadas por alterações não confirmadas em operações simultâneas.

Essa garantia precisa ser mantida mesmo quando o banco de dados executa várias transações em paralelo. O banco de dados precisa implementar controles de simultaneidade para resolver conflitos que quebram essa garantia.

O Firestore garante um isolamento serializável de transações. As transações no Firestore são serializadas e isoladas por tempo de confirmação.

Isolamento serializável por tempo de confirmação

O Firestore atribui a cada transação um tempo de confirmação que representa um único ponto no tempo. Quando o Firestore confirma as alterações de uma transação no banco de dados, você pode presumir que todas as leituras e gravações na transação tenham efeito exatamente no momento da confirmação.

A execução real de uma transação requer algum tempo. A execução de uma transação começa antes do tempo de confirmação e a execução de várias operações pode se sobrepor. O Firestore mantém o isolamento serializável e garante que:

  • O Firestore confirma transações por ordem de tempo de confirmação.
  • O Firestore isola transações de operações simultâneas com um tempo de confirmação posterior.

No caso de contenção de dados entre operações simultâneas, o Firestore usa controles de simultaneidade otimista e pessimista para resolver a contenção.

Isolamento dentro de uma transação

O isolamento da transação também se aplica a operações de gravação dentro dela. As consultas e leituras dentro de uma transação não veem os resultados das gravações anteriores dentro dessa transação. Mesmo que você modifique ou exclua um documento dentro de uma transação, todas as leituras de documentos dessa transação retornarão a versão do documento no momento da confirmação, antes das operações de gravação. Se naquele momento o documento não existia, as operações de leitura não retornam nada.