Google Code offered in: English - Español - 日本語 - 한국어 - Português - Pусский - 中文(简体) - 中文(繁體)
NDB is an experimental, innovative, and rapidly changing new feature for App Engine. Unfortunately, being on the bleeding edge means that we may make backwards-incompatible changes to NDB. We will inform the community when this feature is no longer experimental.
A transaction is an operation or set of operations that either
succeeds completely or fails completely. An application can perform
multiple operations and calculations in a single transaction.
Using the NDB asynchronous API,
an application can manage multiple
transactions simultaneously if they are independent.
The synchronous API offers a simplified API using the
ndb.transaction() function.
The transaction() function takes a callback
function that is executed in the context of the transaction.
This simple example acts as a transactional get-or-insert operation:
key = ndb.Key(Greeting, 'joe')
def callback():
# 'key' here uses the key variable in the outer scope;
# the callback function is a closure.
ent = key.get()
if ent is None:
ent = Greeting(key=key, message='Hey Joe')
ent.put()
return ent
ent = ndb.transaction(callback)
If the transaction "collides" with another, it fails; NDB automatically
retries such failed transactions a few times.
Thus, the callback function may be called multiple times if the transaction
is retried. There is a limit (default 3) to the number of retries
attempted; if the transaction still does not succeed, the transaction() call
raises TransactionFailedError. You can change the retry
count by passing retries=N to the
transaction() call. A retry count of 0 means the transaction
is attempted once but not retried if it fails; a retry count of
N means that the transaction may be attempted a total of
N+1 times. Example:
ent = ndb.transaction(callback, retries=1) # Total of 2 tries
By default, a transaction can only work with entities in the same entity group (entities whose keys have the same "ancestor").
In transactions, only ancestor queries are allowed.
You can specify
cross-group ("XG") transactions
(which allow up to five entity groups), by passing
xg=True:
ent = ndb.transaction(callback, xg=True)
If the transaction's callback function raises an exception,
the transaction is immediately aborted and the
transaction() call re-raises the exception.
You can force a transaction to fail silently by raising the
ndb.Rollback exception (the
transaction() call returns None
in this case). There is no mechanism to force a retry.
Often you have a function that must be run inside a transaction.
The @transactional decorator automatically runs the
decorated function inside a transaction, with the added twist that
if a transaction is already in effect, it doesn't start a new
transaction but just calls the function. For example:
@ndb.transactional
def get_or_insert(keyname):
key = ndb.Key(Greeting, keyname)
ent = key.get()
if ent is None:
ent = Greeting(key=key, message='Hey Rodrigo')
ent.put()
return ent
moraes = get_or_insert('rodrigo')
The @transactional decorator does not support
passing a retry count or other options.
To test whether some code is running inside a transaction,
use the in_transaction() function:
def maybe_run_in_transaction(func):
if ndb.in_transaction():
return func()
else:
return transaction(func)