Transação é uma operação ou um conjunto de operações com garantia de serem atômicas, o que significa que as transações jamais são aplicadas parcialmente. Ou todas as operações na transação são aplicadas, ou nenhuma delas é aplicada. As transações têm uma duração máxima de 60 segundos, com um tempo de expiração por inatividade de dez segundos após 30 segundos.
Usando a API assíncrona NDB, um aplicativo pode gerenciar várias transações simultaneamente caso sejam independentes.
A API síncrona oferece uma API simplificada usando
o decorador @ndb.transactional()
.
A função decorada é executada no contexto da transação.
Se "colidir" com outra, a transação falhará. O NDB repete automaticamente essas transações com falha algumas vezes.
A função pode ser chamada várias vezes caso a transação seja repetida. Há um limite para o número de novas tentativas. O padrão é
de três. Caso a transação continue a falhar, o NDB
gerará TransactionFailedError
. Para alterar a contagem de novas
tentativas, transmita retries=N
para o
decorador transactional()
.
Uma contagem de 0 novas tentativas significa que a transação
é tentada uma vez, mas não será repetida se falhar. Uma contagem de
repetições N significa que a transação pode ser tentada em um total de
N+1 vezes. Exemplo:
Em transações, somente consultas de ancestral são permitidas. Por padrão, uma transação só pode funcionar com entidades no mesmo grupo de entidades (entidades com chaves que tenham o mesmo "ancestral").
É possível especificar transações entre grupos (XG, na sigla em inglês), o que permite até 25 grupos de entidades. Para isso, transmita xg=True
:
As transações entre grupos funcionam em vários grupos de entidades e se comportam como transações de grupo único, mas não falham quando o código tenta atualizar entidades em mais de um grupo de entidades.
Caso a função gere uma exceção, a transação é anulada imediatamente e o NDB regenera a exceção. Dessa maneira, o código de chamada a vê.
É possível forçar que uma transação falhe discretamente gerando
a exceção ndb.Rollback
.
Nesse caso, a chamada de função
retorna None
. Não há mecanismo para forçar uma nova tentativa.
Talvez haja uma função que nem sempre precisa ser executada em uma transação. Em vez de decorar essa função com @ndb.transactional
, transmita-a como uma função de callback para ndb.transaction()
Para testar se algum código está sendo executado em
uma transação, use
a função
in_transaction()
.
Especifique como uma função "transacional" precisa se comportar caso invocada pelo código já em uma transação. O
decorador @ndb.non_transactional
especifica que uma função
não deve ser executada em uma transação. Caso chamado em uma transação, ele é
executado fora da transação. O decorador @ndb.transactional
e a função ndb.transaction
utilizam um
argumento de palavra-chave propagation
. Por exemplo, caso uma função precise iniciar uma transação nova e independente, decore-a da seguinte maneira:
Os tipos de propagação estão listados com as outras Opções de contexto e transação
O comportamento da transação e o comportamento do armazenamento em cache do NDB podem se confundir caso você não saiba o que está acontecendo. Se você modificar uma entidade dentro de uma transação, mas ainda não tiver confirmado a transação, o cache de contexto do NDB terá o valor modificado, mas o armazenamento de dados subjacente continuará tendo o valor não modificado.
Enfileiramento de tarefa transacional
É possível enfileirar uma tarefa como parte de uma transação do Datastore, assim ela será enfileirada apenas caso a transação seja confirmada com êxito. Não sendo confirmada, a transação não será enfileirada. Em caso de confirmação, a tarefa será enfileirada. Ela não é executada imediatamente após ser enfileirada, por isso a tarefa não é atômica com a transação. Ainda assim, uma vez enfileirada, a tarefa tentará novamente até conseguir. Isso se aplica a qualquer tarefa enfileirada durante uma função decorada.
As tarefas transacionais são úteis porque permitem combinar ações que não são do Datastore com uma transação que depende do êxito da transação, como o envio de um e-mail para confirmar uma compra. É possível também vincular ações do Datastore à transação, como confirmar alterações em grupos de entidades fora dela, apenas em caso de êxito dessa transação.
Um aplicativo não consegue inserir mais de cinco tarefas transacionais nas filas de tarefas durante uma única transação. Tarefas transacionais não podem ter nomes especificados pelo usuário.