O Google Code é oferecido em: English - Español - 日本語 - 한국어 - Português - Pусский - 中文(简体) - 中文(繁體)
O armazenamento de dados do Google App Engine suporta transações. Uma transação é uma operação ou conjunto de operações que tem total sucesso ou falha completamente. Um aplicativo pode executar diversas operações e cálculos em uma única transação.
Uma transação é uma operação ou conjunto de operações do armazenamento de dados que tem sucesso total ou falha completamente. Se a transação tiver sucesso, todos os efeitos pretendidos são aplicados ao armazenamento de dados. Se a transação falhar, nenhum dos efeitos é aplicado.
Cada operação de gravação no armazenamento de dados é atômica. Uma tentativa de criar, atualizar ou excluir uma entidade pode ser realizada ou não. Uma operação pode falhar devido a uma alta taxa de contenção, com usuários demais tentando modificar uma entidade ao mesmo tempo. Ou pode falhar devido ao aplicativo ter atingido a quota limite. Ou pode haver um erro interno do armazenamento de dados. Em todos os casos, os efeitos da operação não são aplicados e a API de armazenamento de dados emite uma exceção.
Um aplicativo pode executar um conjunto de instruções e operações no armazenamento de dados em uma única transação, de tal maneira que se qualquer instrução ou operação emitir uma exceção, nenhuma das operações do armazenamento de dados presentes no conjunto será aplicada. O aplicativo define as ações a serem realizadas na transação utilizando uma função de Python e, em seguida, chama db.run_in_transaction() com a função como argumento:
from google.appengine.ext import db
class Accumulator(db.Model):
counter = db.IntegerProperty()
def increment_counter(key, amount):
obj = db.get(key)
obj.counter += amount
obj.put()
q = db.GqlQuery("SELECT * FROM Accumulator")
acc = q.get()
db.run_in_transaction(increment_counter, acc.key(), 5)
db.run_in_transaction() assume o objeto de função e argumentos de posição e palavra-chave para passar à função. Se a função retorna um valor, db.run_in_transaction() retornará o valor.
Se a função retorna, a transação é executada e todos os efeitos das operações no armazenamento de dados são aplicados. Se a função emite uma exceção, a transação é "revertida" e os efeitos não são aplicados.
Se a função emite a exceção Rollback, db.run_in_transaction() retorna None. Para qualquer outra exceção, db.run_in_transaction() reemite a exceção.
O armazenamento de dados impõe diversas restrições sobre o que pode ser feito dentro de uma única transação.
Todas as operações de armazenamento de dados de uma transação devem ser realizadas em entidades do mesmo grupo. Isso inclui a recuperação das entidades por chave, a atualização das entidades e a exclusão das entidades. Cada entidade raiz pertence a um grupo de entidades separado. Assim, uma única transação não pode criar ou operar com mais de uma entidade raiz. Para obter uma explicação sobre grupos de entidades, consulte Chaves e grupos de entidade.
Um aplicativo não pode executar uma consulta durante uma transação. No entanto, um aplicativo pode recuperar entidades do armazenamento de dados usando chaves durante uma transação e ter a garantia de que a entidade obtida é consistente com o resto da transação. Você pode preparar as chaves antes da transação ou pode construir chaves dentro da transação com os nomes ou IDs da chave.
Um aplicativo não pode criar ou atualizar uma entidade mais de uma vez em uma única transação.
Qualquer outro código Python é permitido dentro de uma função de transação. A função de transação não deve ter efeitos colaterais além das operações com o armazenamento de dados. A função de transação pode ser chamada diversas vezes em caso de falha de uma operação do armazenamento de dados, devido à presença de outro usuário atualizando entidades no grupo de entidades ao mesmo tempo. Quando isto ocorrer, a API de armazenamento de dados tenta realizar a transação por um número fixo de vezes. Se todas falharem, db.run_in_transaction() emite um TransactionFailedError. Para ajustar o número de tentativas da transação, use db.run_in_transaction_custom_retries() em vez de db.run_in_transaction().
De um modo semelhante, a função de transação não deve ter efeitos adversos que dependam do sucesso da transação, a menos que o código que chama a função da transação saiba como desfazer tais efeitos. Por exemplo, se uma transação para armazenar uma nova entidade do armazenamento de dados e salvar o ID da entidade criada para uso posterior falhar, o ID salvo não se refere à entidade pretendida porque a criação da entidade foi revertida. O código de chamada deve ter o cuidado de não usar o ID salvo neste caso.
Este exemplo demonstra um uso das transações: a atualização de uma entidade com um novo valor de propriedade relativo ao seu valor atual.
def increment_counter(key, amount): obj = db.get(key) obj.counter += amount obj.put()
Isso exige uma transação, pois o valor pode ser atualizado por outro usuário após o código obter o objeto, mas antes de salvar o objeto modificado. Sem uma transação, a solicitação do usuário usará o valor de counter anterior à atualização do outro usuário e a gravação substituirá o novo valor. Com uma transação, o aplicativo recebe as informações sobre a atualização do outro usuário. Se a entidade for atualizada durante a transação, há uma nova tentativa de transação até todas as etapas serem concluídas sem interrupções.
Outro uso comum para as transações é atualizar uma entidade com uma chave nomeada ou criá-la, caso ainda não exista:
class SalesAccount(db.Model):
address = db.PostalAddressProperty()
phone_number = db.PhoneNumberProperty()
def create_or_update(parent_obj, account_id, address, phone_number):
obj = db.get(Key.from_path("SalesAccount", account_id, parent=parent_obj))
if not obj:
obj = SalesAccount(key_name=account_id,
parent=parent_obj,
address=address,
phone_number=phone_number)
else:
obj.address = address
obj.phone_number = phone_number
obj.put()
Assim como antes, é necessária uma transação para manipular o caso no qual outro usuário tenta criar ou atualizar uma entidade com o mesmo ID de string. Sem uma transação, se a entidade não existir e dois usuários tentarem criá-la, o segundo substituirá o primeiro sem saber que isso aconteceu. Com uma transação, a segunda tentativa será feita novamente, perceberá que a entidade já existe e, em vez disso, irá atualizá-la.
A criação ou atualização é tão útil que há um método integrado para ela: Model.get_or_insert() assume um nome de chave, um pai opcional e argumentos a serem passados ao construtor de modelo caso não exista uma entidade com tal nome e caminho. A tentativa de obtenção e a criação ocorrem em uma transação. Portanto (se a transação tiver sucesso), o método sempre retorna uma instância de modelo representando uma entidade real.
Dica: Uma transação deve ocorrer o mais rápido possível para reduzir a possibilidade de que as entidades usadas pela transação sejam alteradas, exigindo novas tentativas da transação. Procure preparar os dados fora da transação o máximo possível e, em seguida, execute a transação para realizar as operações com o armazenamento de dados que dependam de um estado consistente. O aplicativo deve preparar as chaves para os objetos usados dentro da transação e obter as entidades dentro da transação.