Transakcje w datastore
Datastore obsługuje transakcje - operację lub zbiór operacji, które zostaną wykonane w całości lub w ogóle. Aplikacja może wykonać wiele operacji i obliczeń w jednej transakcji.
Jeżeli transakcja powiedzie się - wszystkie wprowadzone zmiany zostaną zapisane do datastore. Jeżeli transakcja nie powiedzie się to żadne zmiany nie zostaną zapisane do datastore. Zapewnia to integralność danych.
Każda operacja zapisu do datastore jest atomowa. Próba stworzenia, zaktualizowania, czy skasowania encji wykonuje się lub nie. Operacja może się nie powieść z różnych powodów, lecz dane nie zostaną w żaden sposób zmienione, a API wyrzuci wyjątek.
Aplikacja może wykonać szereg operacji w jednej transakcji. By tego dokonać należy użyć db.run_in_transaction() z funkcją do wykonania jako argument:
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)
Jeżeli funkcja zostanie wykonana pomyślnie transakcja jest wykonywana i wszystkie zmiany w datastore są zapisywane. Jeżeli funkcja wyrzuci wyjątek wszystkie operacje są "cofane" i nie następują żadne zmiany w datastore.
Jeżeli funkcja wyrzuci wyjątek Rollback to run_in_transaction zwróci None. Każdy inny wyjątek jest przekazywany do run_in_transaction.
Wszystkie operacje na datastore biegnące w transakcji muszą dotyczyć encji z tej samej grupy wliczając w to zapytania po przodkach encji, pobieranie encji po kluczu, aktualizacja encji i kasowanie encji. Zwróć uwagę że każda encja-korzeń należny do oddzielnej grupy encji, więc jedna transakcja nie może operować na więcej niż jednej encji-korzeniu.
Aplikacja nie może stworzyć lub zaktualizować encji więcej niż raz w jednej transakcji.
Izolacja i spójność
Poziom izolacji datastore poza transakcją zbliżony jest do READ_COMMITTED. Wewnątrz transakcji poziom izolacji to SERIALIZABLE. Zapytania wewnątrz transakcji mają gwarancję dostępu do jednej, spójnej "kopii" (snapshot) datastore z początku transakcji.
Snapshot wewnątrz transakcji dostępny jest także do zapytań wykonany po operacjach zapisu. Zapytania nie widzą zmian wprowadzonych przez wcześniej wykonane zapytania w tej samej transakcji.
Zastosowania transakcji
Poniższy przykład demonstruje jedno z zastosowań transakcji - aktualizację encji o nowe wartości właściwości względem już istniejących danych:
def increment_counter(key, amount):
obj = db.get(key)
obj.counter += amount
obj.put()
Innym zastosowaniem transakcji jest aktualizacja lub stworzenie gdy nie istnieje encji po nazwanym kluczu:
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()
Transakcję można także wykorzystać do odczytania spójnego zbioru danych, który często się zmienia. Pobierając wszystkie dane w transakcji mamy pewność że dane będą spójne.
Na podstawie Transactions.
Comment article