Google Code disponible en: English - Español - 日本語 - 한국어 - Português - Pусский - 中文(简体) - 中文(繁體)
El almacén de datos de App Engine admite las transacciones. Una transacción es una operación o un conjunto de operaciones que se resuelven correcta o incorrectamente en su totalidad. Una aplicación puede realizar varias operaciones y cálculos en una única transacción.
Una transacción es una operación o un conjunto de operaciones del almacén de datos que se resuelven correcta o incorrectamente en su totalidad. Si la transacción se resuelve correctamente, todos los efectos deseados se aplicarán al almacén de datos. Si la transacción no se resuelve correctamente, no se aplicará ninguno de los efectos.
Todas las operaciones de escritura del almacén de datos son atómicas. Un intento de crear, actualizar o eliminar una entidad se aplica o no se aplica. Una operación puede fallar debido a una elevada tasa de contención provocada por el hecho de que muchos usuarios estén intentando modificar una entidad al mismo tiempo. También se pueden producir fallos en las operaciones si una aplicación alcanza un límite de cuota o si se produce un error interno en el almacén de datos. En cualquiera de los casos, los efectos de la operación no se aplicarán y el API del almacén de datos generará una excepción.
Una aplicación puede ejecutar un conjunto de instrucciones y operaciones del almacén de datos en una sola transacción pero, si cualquiera de las instrucciones u operaciones genera una excepción, no se aplicará ninguna de las operaciones del conjunto de operaciones del almacén de datos. La aplicación define las acciones que se deben realizar en la transacción mediante una función Python y, a continuación, ejecuta db.run_in_transaction() con la función como argumento:
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)
"db.run_in_transaction()" transmite el objeto de función y los argumentos posicionales y de palabra clave a la función. Si la función devuelve un valor, "db.run_in_transaction()" lo mostrará.
Si la función devuelve un valor, la transacción se confirmará y se aplicarán todos los efectos de las operaciones del almacén de datos. Si la función genera una excepción, la transacción se "deshará" y no se aplicarán los efectos.
Si la función genera la excepción Rollback, "db.run_in_transaction()" devolverá el valor None. Si se produce cualquier otra excepción, "db.run_in_transaction()", volverá a generar la excepción.
El almacén de datos impone una serie de restricciones respecto a las acciones que se pueden realizar en una transacción.
Todas las operaciones del almacén de datos incluidas en una transacción se deben realizar en entidades pertenecientes a un mismo grupo. Esto incluye la recuperación de entidades por clave, la actualización y la eliminación de entidades. Ten en cuenta que cada entidad raíz pertenece a un grupo de entidades independiente, por lo que una transacción no puede crearse ni realizarse en más de una entidad raíz. Para obtener una explicación de los grupos de entidades, consulta la sección Claves y grupos de entidades.
Una aplicación no puede realizar ninguna consulta durante una transacción. No obstante, una aplicación puede recuperar entidades del almacén de datos mediante claves durante una transacción y garantizar que la entidad extraída es consistente con el resto de la transacción. Puedes preparar claves antes de la transacción o puedes crear claves dentro de la transacción con nombres de clave o ID.
Una aplicación no puede crear ni actualizar una entidad más de una vez en una sola transacción.
En una función de transacción se puede utilizar todo el código Python. Una función de transacción no debe tener ningún efecto secundario, aparte de las operaciones del almacén de datos. Una función de transacción se puede ejecutar varias veces si se produce un fallo en la operación del almacén de datos debido a que otro usuario esté actualizando entidades del grupo de entidades al mismo tiempo. Si se da esta circunstancia, el API del almacén de datos intentará realizar de nuevo la transacción un determinado número de veces. Si ninguno de los intentos se resuelve con éxito, "db.run_in_transaction()" generará un error TransactionFailedError. Mediante el uso de db.run_in_transaction_custom_retries() en lugar de "db.run_in_transaction()", puedes ajustar el número de intentos de realización de la transacción.
Una función de transacción tampoco debe tener efectos secundarios que dependan del éxito de la transacción, a menos que el código que ejecuta la función de transacción sepa cómo deshacer esos efectos. Por ejemplo, si la transacción almacena una nueva entidad del almacén de datos, guarda la ID de la entidad creada para utilizarla posteriormente y, a continuación, genera un error, la ID guardada no hará referencia a la entidad deseada, ya que la operación de creación de la entidad se deshará. El código que ejecuta la función deberá tener cuidado de no utilizar la ID guardada en este caso.
Este ejemplo muestra un posible uso de las transacciones: la actualización de una entidad con un valor de propiedad nuevo respecto a su valor actual.
def increment_counter(key, amount): obj = db.get(key) obj.counter += amount obj.put()
Esto requiere una transacción ya que el valor lo puede actualizar otro usuario después de que este código extraiga el objeto, pero antes de que guarde el objeto modificado. Si no se realiza una transacción, la solicitud del usuario utilizará el valor de counter antes de la actualización del otro usuario y al guardar, se sobrescribirá el valor nuevo. Si se realiza una transacción, la aplicación tendrá constancia de la actualización realizada por el otro usuario. Si la entidad se actualiza durante la transacción, se intentará volver a realizar la transacción hasta que todos los pasos se hayan completado sin interrupción.
Las transacciones también se suelen utilizar para actualizar una entidad con una clave con nombre o crearla si no existe:
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()
Como en el caso anterior, es necesario realizar una transacción para resolver una situación en la que otro usuario esté intentando crear o actualizar una entidad con la misma ID de cadena. En el caso de que no se realice una transacción, si la entidad no existe y dos usuarios intentan crearla, el segundo sobrescribirá al primero sin saber lo que ha pasado. Si se realiza una transacción, se intentará crear la segunda entidad y, al comprobar que ya existe, la entidad se actualizará.
La operación de "creación o actualización" es tan útil que existe un método integrado para ejecutarla: Model.get_or_insert() transmite un nombre de clave, un elemento principal opcional y una serie de argumentos al constructor de modelos si no existe ninguna entidad con ese nombre y con esa ruta. El intento de obtención y la creación se producen en una transacción. Por tanto, si la transacción se resuelve correctamente, el método siempre devuelve una instancia de modelo representativa de una entidad real.
Sugerencia: las transacciones se deben realizar lo más rápido posible para reducir las probabilidades de que las entidades utilizadas por la transacción cambien y haya que intentar realizar de nuevo la transacción. En la medida de lo posible, te recomendamos que prepares los datos antes de ejecutar la transacción para poder realizar operaciones del almacén de datos que dependan de un estado uniforme. La aplicación deberá preparar claves para los objetos que se utilicen en la transacción y, a continuación, extraerá las entidades de la transacción.