[[["易于理解","easyToUnderstand","thumb-up"],["解决了我的问题","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["很难理解","hardToUnderstand","thumb-down"],["信息或示例代码不正确","incorrectInformationOrSampleCode","thumb-down"],["没有我需要的信息/示例","missingTheInformationSamplesINeed","thumb-down"],["翻译问题","translationIssue","thumb-down"],["其他","otherDown","thumb-down"]],["最后更新时间 (UTC):2025-08-20。"],[[["\u003cp\u003eThis page explains the use of legacy bundled services and APIs within the first-generation runtimes of the App Engine standard environment, with a note to consult the migration guide for App Engine Python 3 runtime updates.\u003c/p\u003e\n"],["\u003cp\u003eTransactions are atomic operations that either fully apply or do not apply at all, with a maximum duration of 60 seconds and a 10-second idle expiration time, with NDB providing synchronous and asynchronous APIs for transaction management.\u003c/p\u003e\n"],["\u003cp\u003eThe \u003ccode\u003e@ndb.transactional()\u003c/code\u003e decorator simplifies transaction usage, automatically retrying failed transactions up to a configurable limit and raising a \u003ccode\u003eTransactionFailedError\u003c/code\u003e if it still fails, allowing for single or cross-group transactions.\u003c/p\u003e\n"],["\u003cp\u003eNDB offers control over transaction behavior through decorators like \u003ccode\u003e@ndb.non_transactional\u003c/code\u003e and options like \u003ccode\u003epropagation\u003c/code\u003e, enabling independent transactions or running outside of a current transaction.\u003c/p\u003e\n"],["\u003cp\u003eTransactional task enqueuing allows for tasks to be added as part of a Datastore transaction, only being enqueued if the transaction succeeds, enabling actions like sending emails based on transaction success, with a limit of five transactional tasks per transaction.\u003c/p\u003e\n"]]],[],null,["# NDB Transactions\n\n| This page describes how to use the legacy bundled services and APIs. This API can only run in first-generation runtimes in the App Engine standard environment. If you are updating to the App Engine Python 3 runtime, refer to the [migration guide](/appengine/migration-center/standard/migrate-to-second-gen/python-differences) to learn about your migration options for legacy bundled services.\n\nA transaction is an operation or set of operations that are\nguaranteed to be atomic, which means that transactions are never\npartially applied. Either all of the operations in the transaction\nare applied, or none of them are applied. Transactions have a\nmaximum duration of 60 seconds with a 10 second idle expiration\ntime after 30 seconds.\n\n\nUsing the [NDB asynchronous API](/appengine/docs/legacy/standard/python/ndb/async),\nan application can manage multiple\ntransactions simultaneously if they are independent.\nThe synchronous API offers a simplified API using the\n`@ndb.transactional()` decorator.\nThe decorated function is executed in the context of the transaction. \n\n @ndb.transactional\n def insert_if_absent(note_key, note):\n fetch = note_key.get()\n if fetch is None:\n note.put()\n return True\n return False\n\n note_key = ndb.Key(Note, note_title, parent=parent)\n note = Note(key=note_key, content=note_text)\n\n inserted = insert_if_absent(note_key, note)\n\nIf the transaction \"collides\" with another, it fails; NDB automatically\nretries such failed transactions a few times.\nThe function may be called multiple times if the transaction\nis retried. There is a limit (default 3) to the number of retries\nattempted; if the transaction still does not succeed, NDB\nraises `TransactionFailedError`. You can change the retry\ncount by passing `retries=`\u003cvar translate=\"no\"\u003eN\u003c/var\u003e to the\n`transactional()` decorator.\nA retry count of 0 means the transaction\nis attempted once but not retried if it fails; a retry count of\n\u003cvar translate=\"no\"\u003eN\u003c/var\u003e means that the transaction may be attempted a total of\n\u003cvar translate=\"no\"\u003eN\u003c/var\u003e+1 times. Example:\n\n\n @ndb.transactional(retries=1)\n def insert_if_absent_2_retries(note_key, note):\n # do insert\n\nIn transactions, only ancestor queries are allowed.\nBy default, a transaction can only work with entities in the\nsame entity group (entities whose keys have the same \"ancestor\").\n\nYou can specify cross-group (\"XG\") transactions (which allow up to twenty-five entity groups), by\npassing `xg=True`:\n\n\n @ndb.transactional(xg=True)\n def insert_if_absent_xg(note_key, note):\n # do insert\n\nCross-group transactions operate across multiple entity groups, and behave\nlike single-group transactions, but don't fail if code tries to update\nentities from more than one entity group.\n\nIf the function raises an exception,\nthe transaction is immediately aborted and NDB re-raises the\nexception so that the calling code sees it.\nYou can force a transaction to fail silently by raising the\n`ndb.Rollback` exception (the\nfunction call returns `None`\nin this case). There is no mechanism to force a retry.\n\nYou might have a function that you don't always want to\nrun in a transaction. Instead of decorating such a function\nwith `@ndb.transactional`, pass it as a callback\nfunction to `ndb.transaction()`\n\n\n def insert_if_absent_sometimes(note_key, note):\n # do insert\n\n inserted = ndb.transaction(lambda:\n insert_if_absent_sometimes(note_key, note))\n\nTo test whether some code is running inside a transaction,\nuse the\n[in_transaction()](/appengine/docs/legacy/standard/python/ndb/functions#in_transaction)\nfunction.\n\nYou can specify how a \"transactional\" function should behave if invoked\nby code that's already in a transaction. The\n`@ndb.non_transactional` decorator specifies that a function\nshould not run in a transaction; if called in a transaction, it runs\noutside the transaction. The `@ndb.transactional` decorator\nand `ndb.transaction` function take a\n`propagation` keyword argument. For example, if a function\nshould start a new, independent transaction, decorate it like so:\n\n\n @ndb.transactional(propagation=ndb.TransactionOptions.INDEPENDENT)\n def insert_if_absent_indep(note_key, note):\n # do insert\n\nThe propagation types are listed with the other\n[Context Options and\nTransaction Options](/appengine/docs/legacy/standard/python/ndb/functions#context_options)\n\nTransaction behavior and NDB's caching behavior can combine to confuse you\nif you don't know what's going on.\nIf you modify an entity inside a transaction but have not yet committed\nthe transaction, then NDB's context cache has the modified value but the\nunderlying datastore still has the unmodified value.\n\nTransactional task enqueuing\n----------------------------\n\nYou can enqueue a task as part of a Datastore transaction, so\nthat the task is only enqueued if the\ntransaction is committed successfully. If the transaction does not\nget committed, the task is not enqueued. If the\ntransaction does get committed, the task is\nenqueued. Once enqueued, the task will not execute\nimmediately, so the task is not atomic with the transaction. Still,\nonce enqueued, the task will retry until it succeeds. This applies\nto any task enqueued during a decorated function.\n\nTransactional tasks are useful because they allow you to combine\nnon-Datastore actions to a transaction that depends on the\ntransaction succeeding (such as sending an email to confirm a\npurchase). You can also tie Datastore actions to the transaction,\nsuch as to commit changes to entity groups outside of the\ntransaction if and only if the transaction succeeds.\n\nAn application cannot insert more than\nfive [transactional\ntasks](/appengine/docs/legacy/standard/python/taskqueue#Python_Tasks_within_transactions)\ninto [task\nqueues](/appengine/docs/legacy/standard/python/taskqueue#Python_Queue_concepts) during a single transaction. Transactional tasks must\nnot have user-specified names.\n\n\n from google.appengine.api import taskqueue\n from google.appengine.ext import ndb\n\n @ndb.transactional\n def insert_if_absent_taskq(note_key, note):\n taskqueue.add(url=flask.url_for('taskq_worker'), transactional=True)\n # do insert"]]