def transactional(*, adapter=None, retries=3, propagation=Transaction.Propagation.Nested):
    """Decorates functions so that all of their operations (except for
    queries) run inside a Datastore transaction.

    Parameters:
      adapter(Adapter, optional): The Adapter to use when running the
        transaction.  Defaults to the current adapter.
      retries(int, optional): The number of times to retry the
        transaction if it couldn't be committed.
      propagation(Transaction.Propagation, optional): The propagation
        strategy to use. By default, transactions are nested, but you
        can force certain transactions to always run independently.

    Raises:
      anom.RetriesExceeded: When the decorator runbs out of retries
        while trying to commit the transaction.

    Returns:
      callable: The decorated function.
    """
    def decorator(fn):
        @wraps(fn)
        def inner(*args, **kwargs):
            nonlocal adapter
            adapter = adapter or get_adapter()
            attempts, cause = 0, None
            while attempts <= retries:
                attempts += 1
                transaction = adapter.transaction(propagation)

                try:
                    transaction.begin()
                    res = fn(*args, **kwargs)
                    transaction.commit()
                    return res

                except TransactionFailed as e:
                    cause = e
                    continue

                except Exception as e:
                    transaction.rollback()
                    raise e

                finally:
                    transaction.end()

            raise RetriesExceeded(cause)
        return inner
    return decorator