Conflito de dados em transações

Esta página descreve a contenção de dados transacionais, a serialização e o isolamento. Para ver exemplos de código de transações, consulte o artigo sobre transações e gravações em lote.

Transações e contenção de dados

Para que uma transação seja bem-sucedida, os documentos obtidos pelas respetivas operações de leitura têm de permanecer inalterados por operações fora da transação. Se outra operação tentar alterar um desses documentos, essa operação entra num estado de contenção de dados com a transação.

Conflito 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 concorrente tenta atualizar os valores dos campos desse documento.

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

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

Ao decidir que operação falhar ou atrasar, o comportamento depende do tipo de biblioteca de cliente.

O Firestore pode ser configurado com um modo de simultaneidade: PESSIMISTIC ou OPTIMISTIC. A predefinição para a edição standard é PESSIMISTIC, enquanto que a edição enterprise é OPTIMISTIC. Recomendamos o uso do (PESSIMISTIC). Os SDKs para dispositivos móveis e Web comportam-se independentemente desta definição, uma vez que emulam sempre a concorrência otimista.

  • Os SDKs para dispositivos móveis/Web usam controlos de concorrência otimistas.

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

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

Os SDKs para dispositivos móveis e Web emulam transações de simultaneidade otimista através de pré-condições de escrita nas versões dos documentos. Esta emulação ocorre independentemente da definição do modo de simultaneidade da base de dados. Os SDKs para dispositivos móveis e Web não usam a funcionalidade de transações incorporadas, pelo que, mesmo que o modo de concorrência da base de dados esteja configurado para PESSIMISTIC, os clientes móveis continuam a comportar-se de forma otimista.

Controlos de simultaneidade otimistas
Com base no pressuposto de que a contenção de dados não é provável ou que não é eficiente manter os bloqueios da base de dados. As transações otimistas não usam bloqueios da base de dados para impedir que outras operações alterem os dados.

Os SDKs para dispositivos móveis/Web usam controlos de concorrência otimistas, porque podem funcionar em ambientes com latência elevada e uma ligação de rede não fiável. O bloqueio de documentos num ambiente de latência elevada causaria demasiadas falhas de contenção de dados.

Nos SDKs para dispositivos móveis/Web, uma transação acompanha todos os documentos que lê na transação. A transação conclui as respetivas operações de escrita apenas se nenhum desses documentos tiver sido alterado durante a execução da transação. Se algum documento tiver sido alterado, o processador de transações tenta novamente a transação. Se a transação não conseguir um resultado limpo após algumas novas tentativas, a transação falha devido a contenção de dados.

Conflito de dados nas bibliotecas de cliente do servidor

As bibliotecas cliente do servidor (C#, Go, Java, Node.js, PHP, Python e Ruby) usam a funcionalidade de transações incorporadas, que, por predefinição, implementa controlos de simultaneidade pessimistas. Estas transações respeitam a definição do modo de simultaneidade ao nível da base de dados (normalmente PESSIMISTIC) e usam bloqueios de documentos para evitar escritas em conflito.

Controlos de simultaneidade pessimistas
Com base no pressuposto de que é provável que haja contenção de dados. As transações pessimistas usam bloqueios de bases de dados para impedir que outras operações modifiquem os dados.

As bibliotecas cliente do servidor usam controlos de simultaneidade pessimistas, porque partem do princípio de que existe uma baixa latência e uma ligação fiável à base de dados.

Nas bibliotecas de cliente do servidor, as transações colocam bloqueios nos documentos que leem. O bloqueio de uma transação num documento impede que outras transações, escritas em lote e escritas não transacionais alterem esse documento. Uma transação liberta os bloqueios de documentos no momento da confirmação. Também liberta os respetivos bloqueios se expirar o tempo limite ou falhar por qualquer motivo.

Quando uma transação bloqueia um documento, outras operações de escrita têm de aguardar que a transação desbloqueie o documento. As transações adquirem os respetivos bloqueios por ordem cronológica.

Isolamento serializável

A contenção de dados entre transações está estreitamente relacionada com os níveis de isolamento da base de dados. O nível de isolamento de uma base de dados descreve a forma como o sistema processa conflitos entre operações simultâneas. O conflito resulta dos seguintes requisitos da base de dados:

  • As transações requerem dados precisos e consistentes.
  • Para usar os recursos de forma eficiente, as bases de dados executam operações em simultâneo.

Em sistemas com um nível de isolamento baixo, uma operação de leitura numa transação pode ler dados imprecisos de alterações não comprometidas numa operação concorrente.

O isolamento serializável define o nível de isolamento mais elevado. O isolamento serializável significa que:

  • Pode assumir que a base 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.

Esta garantia tem de ser válida mesmo quando a base de dados executa várias transações em paralelo. A base de dados tem de implementar controlos de concorrência para resolver conflitos que possam violar esta garantia.

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

Isolamento serializável por hora de confirmação

O Firestore atribui a cada transação uma hora de confirmação que representa um único ponto no tempo. Quando o Firestore confirma as alterações de uma transação na base de dados, pode assumir que todas as leituras e escritas na transação ocorrem 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 da hora de confirmação, e a execução de várias operações pode sobrepor-se. O Firestore mantém o isolamento serializável e garante que:

  • O Firestore confirma as transações por ordem da hora de confirmação.
  • O Firestore isola as transações de operações simultâneas com uma hora de confirmação posterior.

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

Isolamento numa transação

O isolamento de transações também se aplica a operações de escrita numa transação. As consultas e as leituras numa transação não veem os resultados das escritas anteriores nessa transação. Mesmo que modifique ou elimine um documento numa transação, todas as leituras de documentos nessa transação devolvem a versão do documento no momento da confirmação, antes das operações de escrita da transação. As operações de leitura não devolvem nada se o documento não existir nesse momento.

Problemas com a contenção de dados

Para mais informações sobre a contenção de dados e como a resolver, consulte a página de resolução de problemas.