RkBlog

Hardware, programming and astronomy tutorials and reviews.

Transakcje w datastore

Opis mechnizmu i zastosowań transakcji 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)
run_in_transaction przyjmuje jako argument obiekt funkcji oraz argumenty do przekazania do tej funkcji. Jeżeli wykonywana w transakcji funkcja zwraca jakąś wartość - zwróci ją także funkcja run_in_transaction.

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()
Ta operacja wymaga transakcji, gdyż wartość w encji mogłaby zostać zaktualizowana przez innego użytkownika po popraniu obiektu, a przed jego zapisaniem. W przypadku transakcji i zaktualizowania encji po rozpoczęciu transakcji - zostanie ona ponowiona na nowej wersji encji.

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()
W tym przypadku bez transakcji gdyby drugi użytkownik chciał stworzyć encję o takiej samej nazwie - nadpisałby dane pierwszego użytkownika. W przypadku transakcji - zostałaby ona ponowiona uwzględniając pojawienie się encji o takiej samej nazwie.

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.
RkBlog

4 August 2009;

Comment article