Python 2.7 has reached end of supportand will bedeprecatedon January 31, 2026. After deprecation, you won't be able to deploy Python 2.7applications, even if your organization previously used an organization policy tore-enable deployments of legacy runtimes. Your existing Python2.7 applications will continue to run and receive traffic after theirdeprecation date. We recommend thatyoumigrate to the latest supported version of Python.

Transactions

Note:Developers building new applications arestrongly encouraged to use theNDB Client Library, which has several benefitscompared to this client library, such as automatic entity caching via the MemcacheAPI. If you are currently using the older DB Client Library, read theDB to NDB Migration Guide

Datastore supportstransactions. A transaction is an operation orset of operations that is atomic—either all of the operations in the transactionoccur, or none of them occur. An application can perform multiple operations andcalculations in a single transaction.

Using transactions

Atransaction is a set of Datastore operations on one or moreentities. Each transaction is guaranteed to be atomic, which means thattransactions are never partially applied. Either all of the operations in thetransaction are applied, or none of them are applied. Transactions have amaximum duration of 60 seconds with a 10 second idle expiration time after 30seconds.

An operation might fail when:

  • Too many concurrent modifications are attempted on the same entity group.
  • The transaction exceeds a resource limit.
  • Datastore encounters an internal error.

In all these cases, the Datastore APIraises an exception.Note: If your application receives an exception when committing a transaction,it does not always mean that the transaction failed. You can receiveTimeout,TransactionFailedError, orInternalErrorexceptions in cases wheretransactions have been committed and eventually will be applied successfully.Whenever possible, make your Datastore transactions idempotentso that if you repeat a transaction, the end result will be the same.

Transactions are an optional feature of Datastore; you're notrequired to use transactions to perform Datastore operations.

An application can execute a set of statements and data store operations in asingle transaction, such that if any statement or operation raises an exception,none of the Datastore operations in the set are applied. Theapplication defines the actions to perform in the transaction using a Pythonfunction. The application starts the transaction using one of therun_in_transaction methods, depending on whether the transaction accessesentities within a single entity group or whether the transaction is across-group transaction.

For the common use case of a function that is only used within transactions, usethe@db.transactional decorator:

fromgoogle.appengine.extimportdbclassAccumulator(db.Model):counter=db.IntegerProperty(default=0)@db.transactionaldefincrement_counter(key,amount):obj=db.get(key)obj.counter+=amountobj.put()q=db.GqlQuery("SELECT * FROM Accumulator")acc=q.get()increment_counter(acc.key(),5)

If the function is sometimes called without a transaction, then instead ofdecorating it, calldb.run_in_transaction()with the function as an argument:

fromgoogle.appengine.extimportdbclassAccumulator(db.Model):counter=db.IntegerProperty(default=0)defincrement_counter(key,amount):obj=db.get(key)obj.counter+=amountobj.put()q=db.GqlQuery("SELECT * FROM Accumulator")acc=q.get()db.run_in_transaction(increment_counter,acc.key(),5)

db.run_in_transaction() takes the function object, and positional and keywordarguments to pass to the function. If the function returns a value,db.run_in_transaction() returns that value.

If the function returns, the transaction is committed, and all effects ofDatastore operations are applied. If the function raises anexception, the transaction is "rolled back," and the effects are not applied.See the note above about exceptions.

When one transaction function is called from within another transaction,@db.transactional anddb.run_in_transaction() have different default behavior.@db.transactional will allow this, and the inner transaction becomes the same transaction as the outer transaction. Callingdb.run_in_transaction() attempts to "nest" another transaction within the existing transaction; but this behavior is not yet supported, and it raisesdb.BadRequestError. You can specify other behavior; see the function reference ontransaction options for details.

Using Cross-Group (XG) Transactions

Cross-group transactions, which operate across multiple entity groups, behavelike single-group transactions, but don't fail if code tries to updateentities from more than one entity group. To invoke a cross-group transaction,use transaction options.

Using@db.transactional:

fromgoogle.appengine.extimportdb@db.transactional(xg=True)defmake_things():thing1=Thing(a=3)thing1.put()thing2=Thing(a=7)thing2.put()make_things()

Usingdb.run_in_transaction_options:

fromgoogle.appengine.extimportdbxg_on=db.create_transaction_options(xg=True)defmy_txn():x=MyModel(a=3)x.put()y=MyModel(a=7)y.put()db.run_in_transaction_options(xg_on,my_txn)

What can be done in a transaction

Datastore imposes restrictions on what can be done inside a singletransaction.

All Datastore operations in a transaction must operate onentities in the sameentity groupif the transaction is a single-group transaction, or on entities in a maximum oftwenty-five entity groups if the transaction is a cross-group transaction. Thisincludes querying for entities by ancestor, retrieving entities by key, updatingentities, and deleting entities. Notice that each root entity belongs to aseparate entity group, so a single transaction cannot create or operate on morethan one root entity unless it is a cross-group transaction.

When two or more transactions simultaneously attempt to modify entities in oneor more common entity groups, only the first transaction to commit its changescan succeed; all the others will fail on commit. Because of this design, usingentity groups limits the number of concurrent writes you can do on any entity inthe groups. When a transaction starts, Datastore usesoptimistic concurrency control by checking the last update time for the entity groups used in the transaction.Upon committing a transaction for the entity groups, Datastore againchecks the last update time for the entity groups used in the transaction. If ithas changed since the initial check,an exception is thrown.

An app can perform aquery during a transaction, but only if it includes an ancestor filter. An app can also get Datastore entities by key during a transaction. You can prepare keys prior to the transaction, or you can build keys inside the transaction with key names or IDs.

All other Python code is allowed inside a transaction function. You candetermine if the current scope is nested in a transaction function usingdb.is_in_transaction().The transaction function should not have side effects other than theDatastore operations. The transaction function can be calledmultiple times if a Datastore operation fails due to anotheruser updating entities in the entity group at the same time. When this happens,the Datastore API retries the transaction a fixed number oftimes. If they all fail,db.run_in_transaction() raises aTransactionFailedError.You can adjust the number of times the transaction is retried usingdb.run_in_transaction_custom_retries()instead of db.run_in_transaction().

Similarly, the transaction function should not have side effects that depend onthe success of the transaction, unless the code that calls the transactionfunction knows to undo those effects. For example, if the transaction stores anew Datastore entity, saves the created entity's ID for lateruse, then the transaction fails, the saved ID does not refer to the intendedentity because the entity's creation was rolled back. The calling code wouldhave to be careful not to use the saved ID in this case.

Isolation and consistency

Outside of transactions, Datastore's isolation level is closestto read committed. Inside of transactions, serializable isolation is enforced.This means that another transaction cannot concurrently modify the data that isread or modified by this transaction.

In a transaction, all reads reflect the current, consistent state ofDatastore at the time the transaction started. Queries and getsinside a transaction are guaranteed to see a single, consistent snapshot ofDatastore as of the beginning of the transaction. Entities andindex rows in the transaction's entity group are fully updated so that queriesreturn the complete, correct set of result entities, without the false positivesor false negatives that can occur in queries outside of transactions.

This consistent snapshot view also extends to reads after writes insidetransactions. Unlike with most databases, queries and gets inside aDatastore transaction donot see theresults of previous writes inside that transaction. Specifically, if an entityis modified or deleted within a transaction, a query or get returns theoriginal version of the entity as of the beginning of the transaction,or nothing if the entity did not exist then.

Uses for transactions

This example demonstrates one use of transactions: updating an entity with a newproperty value relative to its current value.

defincrement_counter(key,amount):obj=db.get(key)obj.counter+=amountobj.put()
Warning: The above sample depicts transactionally incrementing a counter onlyfor the sake of simplicity. If your app has counters that are updatedfrequently, you should not increment them transactionally, or even within asingle entity. A best practice for working with counters is to shard the counterinto multiple pieces, pick one at random when you need to increment the counter,and sum the pieces to get the total count.

This requires a transaction because the value might be updated by another userafter this code fetches the object, but before it saves the modified object.Without a transaction, the user's request uses the value ofcount prior to theother user's update, and the save overwrites the new value. With atransaction, the application is told about the other user's update. If the entity is updated during thetransaction, then the transaction is retried until all steps are completedwithout interruption.

Another common use for transactions is to fetch an entity with a named key, orcreate it if it doesn't yet exist:

classSalesAccount(db.Model):address=db.PostalAddressProperty()phone_number=db.PhoneNumberProperty()defget_or_create(parent_key,account_id,address,phone_number):obj=db.get(db.Key.from_path("SalesAccount",account_id,parent=parent_key))ifnotobj:obj=SalesAccount(key_name=account_id,parent=parent_key,address=address,phone_number=phone_number)obj.put()else:obj.address=addressobj.phone_number=phone_number

As before, a transaction is necessary to handle the case where another user isattempting to create or update an entity with the same string ID. Without atransaction, if the entity does not exist and two users attempt to create it,the second overwrites the first without knowing that it happened.With a transaction, the second attempt retries, noticesthat the entity now exists, and updates the entity instead.

When a transaction fails, you can have your app retry the transaction until itsucceeds, or you can let your users deal with the error by propagating it toyour app's user interface level. You do not have to create a retry loop aroundevery transaction.

Get-or-create is so useful that there is a built-in method for it:Model.get_or_insert()takes a key name, an optional parent, and arguments to pass to the modelconstructor if an entity of that name and path does not exist. The get attemptand the create happen in one transaction, so (if the transaction is successful)the method always returns a model instance that represents an actual entity.

Note: A transaction should happen as quickly as possible to reduce thelikelihood that the entities used by the transaction will change, causing thetransaction to fail. As much as possible, prepare data outside of thetransaction, then execute the transaction to perform Datastoreoperations that depend on a consistent state. The application should preparekeys for objects used outside the transaction then fetch the entities inside thetransaction.

Finally, you can use a transaction to read a consistent snapshot ofDatastore. This can be useful when multiple reads are needed torender a page or export data that must be consistent. These kinds oftransactions are often calledread-only transactions, since they perform nowrites. Read-only single-group transactions never fail due to concurrentmodifications, so you don't have to implement retries upon failure. However,cross-group transactions can fail due to concurrent modifications, so theseshould have retries. Committing and rolling back a read-only transaction areboth no-ops.

classCustomer(db.Model):user=db.StringProperty()classAccount(db.Model):"""An Account has a Customer as its parent."""address=db.PostalAddressProperty()balance=db.FloatProperty()defget_all_accounts():"""Returns a consistent view of the current user's accounts."""accounts=[]forcustomerinCustomer.all().filter('user =',users.get_current_user().user_id()):accounts.extend(Account.all().ancestor(customer))returnaccounts

Transactional task enqueuing

You can enqueue a task as part of a Datastore transaction, sothat the task is only enqueued if the transaction is committed successfully. Ifthe transaction does not get committed, the task is not enqueued. If thetransaction does get committed, the task is enqueued. Once enqueued, the taskwill not execute immediately, so the task is not atomic with the transaction.Still, once enqueued, the task will retry until it succeeds. This applies to anytask enqueued during arun_in_transaction() function.

Transactional tasks are useful because they allow you to combinenon-Datastore actions to a transaction that depends on thetransaction succeeding (such as sending an email to confirm a purchase). You canalso tie Datastore actions to the transaction, such as tocommit changes to entity groups outside of the transaction if and only if thetransaction succeeds.

An application cannot insert more than fivetransactional tasks intotask queuesduring a single transaction. Transactional tasks must not have user-specifiednames.

defdo_something_in_transaction(...)taskqueue.add(url='/path/to/my/worker',transactional=True)...db.run_in_transaction(do_something_in_transaction,....)

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2025-12-15 UTC.