My understanding is that ndb.transactional is used to ensure a function is working on the most current data. I tested the following code in the Interactive Console of my local Google App Engine development server:
from google.appengine.ext import ndb
class UserModel(ndb.Model):
level = ndb.IntegerProperty(default=0)
@ndb.transactional(retries=0)
def inc_user_lvl(user_key, recurse=True):
user = user_key.get()
print(user.level)
user.level += 1
if recurse:
inc_user_lvl(user_key, recurse=False)
user.put()
user_key = UserModel().put()
inc_user_lvl(user_key)
user = user_key.get()
print(user.level)
The docs say
There is a limit (default 3) to the number of retries attempted; if the transaction still does not succeed, NDB raises
TransactionFailedError.
In this case the number of retries is 0 so I expected the user's level to be incremented to 1 and a TransactionFailedError to be raised.
Instead the function calls both succeeded and the second call to inc_user_lvl operated on a user with level 1 (before the first call had put the entity). The user's level was 2 after both calls finished. Why is this the case?