Ejemplo n.º 1
0
    def _do_enter(self):
        if IsInTransaction():
            if self.independent:
                self.conn_stack.append(_PopConnection())
                try:
                    return self._do_enter()
                except:
                    _PushConnection(self.conn_stack.pop())
                    raise
            else:
                # App Engine doesn't support nested transactions, so if there is a nested
                # atomic() call we just don't do anything. This is how RunInTransaction does it
                return
        elif self.mandatory:
            raise TransactionFailedError("You've specified that an outer transaction is mandatory, but one doesn't exist")

        options = CreateTransactionOptions(
            xg=self.xg,
            propagation=TransactionOptions.INDEPENDENT if self.independent else None
        )

        conn = _GetConnection()

        self.transaction_started = True
        new_conn = conn.new_transaction(options)

        _PushConnection(None)
        _SetConnection(new_conn)

        assert(_GetConnection())

        # Clear the context cache at the start of a transaction
        caching._context.stack.push()
Ejemplo n.º 2
0
 def _enter_transaction_management(self, managed):
     logger.info('Entering Transaction')
     self.managed(managed)
     self.old_connection = _GetConnection()
     # TODO: optionally pass a config 
     self.connection = self.old_connection.new_transaction()
     _SetConnection(self.connection)
Ejemplo n.º 3
0
    def _do_enter(self):
        if IsInTransaction():
            if self.independent:
                self.conn_stack.append(_PopConnection())
                try:
                    return self._do_enter()
                except:
                    _PushConnection(self.conn_stack.pop())
                    raise
            else:
                # App Engine doesn't support nested transactions, so if there is a nested
                # atomic() call we just don't do anything. This is how RunInTransaction does it
                return
        elif self.mandatory:
            raise TransactionFailedError(
                "You've specified that an outer transaction is mandatory, but one doesn't exist"
            )

        options = CreateTransactionOptions(
            xg=self.xg,
            propagation=TransactionOptions.INDEPENDENT
            if self.independent else None)

        conn = _GetConnection()

        self.transaction_started = True
        new_conn = conn.new_transaction(options)

        _PushConnection(None)
        _SetConnection(new_conn)

        assert (_GetConnection())

        # Clear the context cache at the start of a transaction
        caching._context.stack.push()
Ejemplo n.º 4
0
  def transaction(self, callback, **ctx_options):
    # Will invoke callback() one or more times with the default
    # context set to a new, transactional Context.  Returns a Future.
    # Callback may be a tasklet.
    options = _make_ctx_options(ctx_options)
    app = ContextOptions.app(options) or key_module._DefaultAppId()
    # Note: zero retries means try it once.
    retries = ContextOptions.retries(options)
    if retries is None:
      retries = 3
    yield self.flush()
    for _ in xrange(1 + max(0, retries)):
      transaction = yield self._conn.async_begin_transaction(options, app)
      tconn = datastore_rpc.TransactionalConnection(
        adapter=self._conn.adapter,
        config=self._conn.config,
        transaction=transaction)
      old_ds_conn = datastore._GetConnection()
      tctx = self.__class__(conn=tconn,
                            auto_batcher_class=self._auto_batcher_class)
      try:
        # Copy memcache policies.  Note that get() will never use
        # memcache in a transaction, but put and delete should do their
        # memcache thing (which is to mark the key as deleted for
        # _LOCK_TIME seconds).  Also note that the in-process cache and
        # datastore policies keep their default (on) state.
        tctx.set_memcache_policy(self.get_memcache_policy())
        tctx.set_memcache_timeout_policy(self.get_memcache_timeout_policy())
        tasklets.set_context(tctx)
        datastore._SetConnection(tconn)  # For taskqueue coordination
        try:
          try:
            result = callback()
            if isinstance(result, tasklets.Future):
              result = yield result
          finally:
            yield tctx.flush()
        except GeneratorExit:
          raise
        except Exception:
          t, e, tb = sys.exc_info()
          yield tconn.async_rollback(options)  # TODO: Don't block???
          if issubclass(t, datastore_errors.Rollback):
            # TODO: Raise value using tasklets.get_return_value(t)?
            return
          else:
            raise t, e, tb
        else:
          ok = yield tconn.async_commit(options)
          if ok:
            # TODO: This is questionable when self is transactional.
            self._cache.update(tctx._cache)
            yield self._clear_memcache(tctx._cache)
            raise tasklets.Return(result)
      finally:
        datastore._SetConnection(old_ds_conn)

    # Out of retries
    raise datastore_errors.TransactionFailedError(
      'The transaction could not be committed. Please try again.')
Ejemplo n.º 5
0
 def _enter_transaction_management(self, managed):
     logger.info('Entering Transaction')
     self.managed(managed)
     self.old_connection = _GetConnection()
     # TODO: optionally pass a config
     self.connection = self.old_connection.new_transaction()
     _SetConnection(self.connection)
Ejemplo n.º 6
0
    def transaction(self, callback, **ctx_options):
        # Will invoke callback() one or more times with the default
        # context set to a new, transactional Context.  Returns a Future.
        # Callback may be a tasklet.
        options = _make_ctx_options(ctx_options)
        app = ContextOptions.app(options) or key_module._DefaultAppId()
        # Note: zero retries means try it once.
        retries = ContextOptions.retries(options)
        if retries is None:
            retries = 3
        yield self.flush()
        for _ in xrange(1 + max(0, retries)):
            transaction = yield self._conn.async_begin_transaction(
                options, app)
            tconn = datastore_rpc.TransactionalConnection(
                adapter=self._conn.adapter,
                config=self._conn.config,
                transaction=transaction)
            old_ds_conn = datastore._GetConnection()
            tctx = self.__class__(conn=tconn,
                                  auto_batcher_class=self._auto_batcher_class)
            try:
                # Copy memcache policies.  Note that get() will never use
                # memcache in a transaction, but put and delete should do their
                # memcache thing (which is to mark the key as deleted for
                # _LOCK_TIME seconds).  Also note that the in-process cache and
                # datastore policies keep their default (on) state.
                tctx.set_memcache_policy(self.get_memcache_policy())
                tctx.set_memcache_timeout_policy(
                    self.get_memcache_timeout_policy())
                tasklets.set_context(tctx)
                datastore._SetConnection(tconn)  # For taskqueue coordination
                try:
                    try:
                        result = callback()
                        if isinstance(result, tasklets.Future):
                            result = yield result
                    finally:
                        yield tctx.flush()
                except Exception:
                    t, e, tb = sys.exc_info()
                    yield tconn.async_rollback(options)  # TODO: Don't block???
                    if issubclass(t, datastore_errors.Rollback):
                        return
                    else:
                        raise t, e, tb
                else:
                    ok = yield tconn.async_commit(options)
                    if ok:
                        # TODO: This is questionable when self is transactional.
                        self._cache.update(tctx._cache)
                        yield self._clear_memcache(tctx._cache)
                        raise tasklets.Return(result)
            finally:
                datastore._SetConnection(old_ds_conn)

        # Out of retries
        raise datastore_errors.TransactionFailedError(
            'The transaction could not be committed. Please try again.')
Ejemplo n.º 7
0
 def transaction(self, callback, retry=3, entity_group=None, **ctx_options):
     # Will invoke callback() one or more times with the default
     # context set to a new, transactional Context.  Returns a Future.
     # Callback may be a tasklet.
     options = _make_ctx_options(ctx_options)
     if entity_group is not None:
         app = entity_group.app()
     else:
         app = key_module._DefaultAppId()
     yield self.flush()
     for i in range(1 + max(0, retry)):
         transaction = yield self._conn.async_begin_transaction(
             options, app)
         tconn = datastore_rpc.TransactionalConnection(
             adapter=self._conn.adapter,
             config=self._conn.config,
             transaction=transaction,
             entity_group=entity_group)
         tctx = self.__class__(conn=tconn,
                               auto_batcher_class=self._auto_batcher_class)
         tctx.set_memcache_policy(False)
         tasklets.set_context(tctx)
         old_ds_conn = datastore._GetConnection()
         try:
             datastore._SetConnection(tconn)  # For taskqueue coordination
             try:
                 try:
                     result = callback()
                     if isinstance(result, tasklets.Future):
                         result = yield result
                 finally:
                     yield tctx.flush()
             except Exception, err:
                 t, e, tb = sys.exc_info()
                 yield tconn.async_rollback(options)  # TODO: Don't block???
                 if issubclass(t, datastore_errors.Rollback):
                     return
                 else:
                     raise t, e, tb
             else:
                 ok = yield tconn.async_commit(options)
                 if ok:
                     # TODO: This is questionable when self is transactional.
                     self._cache.update(tctx._cache)
                     self._clear_memcache(tctx._cache)
                     raise tasklets.Return(result)
Ejemplo n.º 8
0
 def transaction(self, callback, retry=3, entity_group=None, **ctx_options):
   # Will invoke callback() one or more times with the default
   # context set to a new, transactional Context.  Returns a Future.
   # Callback may be a tasklet.
   options = _make_ctx_options(ctx_options)
   if entity_group is not None:
     app = entity_group.app()
   else:
     app = key_module._DefaultAppId()
   yield self.flush()
   for i in range(1 + max(0, retry)):
     transaction = yield self._conn.async_begin_transaction(options, app)
     tconn = datastore_rpc.TransactionalConnection(
       adapter=self._conn.adapter,
       config=self._conn.config,
       transaction=transaction,
       entity_group=entity_group)
     tctx = self.__class__(conn=tconn,
                           auto_batcher_class=self._auto_batcher_class)
     tctx.set_memcache_policy(False)
     tasklets.set_context(tctx)
     old_ds_conn = datastore._GetConnection()
     try:
       datastore._SetConnection(tconn)  # For taskqueue coordination
       try:
         try:
           result = callback()
           if isinstance(result, tasklets.Future):
             result = yield result
         finally:
           yield tctx.flush()
       except Exception, err:
         t, e, tb = sys.exc_info()
         yield tconn.async_rollback(options)  # TODO: Don't block???
         if issubclass(t, datastore_errors.Rollback):
           return
         else:
           raise t, e, tb
       else:
         ok = yield tconn.async_commit(options)
         if ok:
           # TODO: This is questionable when self is transactional.
           self._cache.update(tctx._cache)
           self._clear_memcache(tctx._cache)
           raise tasklets.Return(result)
Ejemplo n.º 9
0
    def _begin(self):
        options = CreateTransactionOptions(
            xg = True if self.xg else False,
            propagation = TransactionOptions.INDEPENDENT if self.independent else None
        )

        if IsInTransaction() and not self.independent:
            raise RuntimeError("Nested transactions are not supported")
        elif self.independent:
            #If we're running an independent transaction, pop the current one
            self.parent_conn = _PopConnection()

        #Push a new connection, start a new transaction
        conn = _GetConnection()
        _PushConnection(None)
        _SetConnection(conn.new_transaction(options))

        #Clear the context cache at the start of a transaction
        caching.clear_context_cache()
Ejemplo n.º 10
0
    def _begin(self):
        options = CreateTransactionOptions(
            xg=True if self.xg else False,
            propagation=TransactionOptions.INDEPENDENT
            if self.independent else None)

        if IsInTransaction() and not self.independent:
            raise RuntimeError("Nested transactions are not supported")
        elif self.independent:
            #If we're running an independent transaction, pop the current one
            self.parent_conn = _PopConnection()

        #Push a new connection, start a new transaction
        conn = _GetConnection()
        _PushConnection(None)
        _SetConnection(conn.new_transaction(options))

        #Clear the context cache at the start of a transaction
        caching.clear_context_cache()
Ejemplo n.º 11
0
        except Exception, err:
          t, e, tb = sys.exc_info()
          yield tconn.async_rollback(options)  # TODO: Don't block???
          if issubclass(t, datastore_errors.Rollback):
            return
          else:
            raise t, e, tb
        else:
          ok = yield tconn.async_commit(options)
          if ok:
            # TODO: This is questionable when self is transactional.
            self._cache.update(tctx._cache)
            self._clear_memcache(tctx._cache)
            raise tasklets.Return(result)
      finally:
        datastore._SetConnection(old_ds_conn)

    # Out of retries
    raise datastore_errors.TransactionFailedError(
      'The transaction could not be committed. Please try again.')

  def in_transaction(self):
    """Return whether a transaction is currently active."""
    return isinstance(self._conn, datastore_rpc.TransactionalConnection)

  def clear_cache(self):
    """Clears the in-memory cache.

    NOTE: This does not affect memcache.
    """
    self._cache.clear()
Ejemplo n.º 12
0
 def _leave_transaction_management(self, managed):
     logger.info('Leaving Transaction')
     self.connection = self.old_connection
     _SetConnection(self.connection)
Ejemplo n.º 13
0
                except Exception, err:
                    t, e, tb = sys.exc_info()
                    yield tconn.async_rollback(options)  # TODO: Don't block???
                    if issubclass(t, datastore_errors.Rollback):
                        return
                    else:
                        raise t, e, tb
                else:
                    ok = yield tconn.async_commit(options)
                    if ok:
                        # TODO: This is questionable when self is transactional.
                        self._cache.update(tctx._cache)
                        self._clear_memcache(tctx._cache)
                        raise tasklets.Return(result)
            finally:
                datastore._SetConnection(old_ds_conn)

        # Out of retries
        raise datastore_errors.TransactionFailedError(
            'The transaction could not be committed. Please try again.')

    def in_transaction(self):
        """Return whether a transaction is currently active."""
        return isinstance(self._conn, datastore_rpc.TransactionalConnection)

    def clear_cache(self):
        """Clears the in-memory cache.

    NOTE: This does not affect memcache.
    """
        self._cache.clear()
Ejemplo n.º 14
0
    def transaction(self, callback, **ctx_options):

        options = _make_ctx_options(ctx_options, TransactionOptions)
        propagation = TransactionOptions.propagation(options)
        if propagation is None:
            propagation = TransactionOptions.NESTED

        mode = datastore_rpc.TransactionMode.READ_WRITE
        if ctx_options.get('read_only', False):
            mode = datastore_rpc.TransactionMode.READ_ONLY

        parent = self
        if propagation == TransactionOptions.NESTED:
            if self.in_transaction():
                raise datastore_errors.BadRequestError(
                    'Nested transactions are not supported.')
        elif propagation == TransactionOptions.MANDATORY:
            if not self.in_transaction():
                raise datastore_errors.BadRequestError(
                    'Requires an existing transaction.')
            result = callback()
            if isinstance(result, tasklets.Future):
                result = yield result
            raise tasklets.Return(result)
        elif propagation == TransactionOptions.ALLOWED:
            if self.in_transaction():
                result = callback()
                if isinstance(result, tasklets.Future):
                    result = yield result
                raise tasklets.Return(result)
        elif propagation == TransactionOptions.INDEPENDENT:
            while parent.in_transaction():
                parent = parent._parent_context
                if parent is None:
                    raise datastore_errors.BadRequestError(
                        'Context without non-transactional ancestor')
        else:
            raise datastore_errors.BadArgumentError(
                'Invalid propagation value (%s).' % (propagation, ))

        app = TransactionOptions.app(options) or key_module._DefaultAppId()

        retries = TransactionOptions.retries(options)
        if retries is None:
            retries = 3
        yield parent.flush()

        transaction = None
        tconn = None
        for _ in range(1 + max(0, retries)):
            previous_transaction = (transaction if mode
                                    == datastore_rpc.TransactionMode.READ_WRITE
                                    else None)
            transaction = yield (parent._conn.async_begin_transaction(
                options, app, previous_transaction, mode))
            tconn = datastore_rpc.TransactionalConnection(
                adapter=parent._conn.adapter,
                config=parent._conn.config,
                transaction=transaction,
                _api_version=parent._conn._api_version)
            tctx = parent.__class__(
                conn=tconn,
                auto_batcher_class=parent._auto_batcher_class,
                parent_context=parent)
            tctx._old_ds_conn = datastore._GetConnection()
            ok = False
            try:

                tctx.set_memcache_policy(parent.get_memcache_policy())
                tctx.set_memcache_timeout_policy(
                    parent.get_memcache_timeout_policy())
                tasklets.set_context(tctx)
                datastore._SetConnection(tconn)
                try:
                    try:
                        result = callback()
                        if isinstance(result, tasklets.Future):
                            result = yield result
                    finally:
                        yield tctx.flush()
                except GeneratorExit:
                    raise
                except Exception:
                    t, e, tb = sys.exc_info()
                    tconn.async_rollback(options)
                    if issubclass(t, datastore_errors.Rollback):

                        return
                    else:
                        six.reraise(t, e, tb)
                else:
                    ok = yield tconn.async_commit(options)
                    if ok:
                        parent._cache.update(tctx._cache)
                        yield parent._clear_memcache(tctx._cache)
                        raise tasklets.Return(result)

            finally:
                datastore._SetConnection(tctx._old_ds_conn)
                del tctx._old_ds_conn
                if ok:

                    for on_commit_callback in tctx._on_commit_queue:
                        on_commit_callback()

        tconn.async_rollback(options)
        raise datastore_errors.TransactionFailedError(
            'The transaction could not be committed. Please try again.')
Ejemplo n.º 15
0
 def _leave_transaction_management(self, managed):
     logger.info('Leaving Transaction')
     self.connection = self.old_connection
     _SetConnection(self.connection)
Ejemplo n.º 16
0
    def _help_tasklet_along(self,
                            ns,
                            ds_conn,
                            gen,
                            val=None,
                            exc=None,
                            tb=None):

        info = utils.gen_info(gen)

        __ndb_debug__ = info
        try:
            save_context = get_context()
            save_namespace = namespace_manager.get_namespace()
            save_ds_connection = datastore._GetConnection()
            try:
                set_context(self._context)
                if ns != save_namespace:
                    namespace_manager.set_namespace(ns)
                if ds_conn is not save_ds_connection:
                    datastore._SetConnection(ds_conn)
                if exc is not None:
                    _logging_debug('Throwing %s(%s) into %s',
                                   exc.__class__.__name__, exc, info)
                    value = gen.throw(exc.__class__, exc, tb)
                else:
                    _logging_debug('Sending %r to %s', val, info)
                    value = gen.send(val)
                    self._context = get_context()
            finally:
                ns = namespace_manager.get_namespace()
                ds_conn = datastore._GetConnection()
                set_context(save_context)
                if save_namespace != ns:
                    namespace_manager.set_namespace(save_namespace)
                if save_ds_connection is not ds_conn:
                    datastore._SetConnection(save_ds_connection)

        except (StopIteration, Return) as err:
            result = get_return_value(err)
            _logging_debug('%s returned %r', info, result)
            self.set_result(result)
            return

        except GeneratorExit:

            raise

        except Exception as err:
            _, _, tb = sys.exc_info()
            if isinstance(err, _flow_exceptions):

                _logging_debug('%s raised %s(%s)', info,
                               err.__class__.__name__, err)
            elif utils.DEBUG and logging.getLogger().level < logging.DEBUG:

                logging.warning('%s raised %s(%s)',
                                info,
                                err.__class__.__name__,
                                err,
                                exc_info=True)
            else:

                logging.warning('%s raised %s(%s)', info,
                                err.__class__.__name__, err)
            self.set_exception(err, tb)
            return

        else:
            _logging_debug('%s yielded %r', info, value)
            if isinstance(value,
                          (apiproxy_stub_map.UserRPC, datastore_rpc.MultiRpc)):

                eventloop.queue_rpc(value, self._on_rpc_completion, value, ns,
                                    ds_conn, gen)
                return
            if isinstance(value, Future):

                if self._next:
                    raise RuntimeError(
                        'Future has already completed yet next is %r' %
                        self._next)
                self._next = value
                self._geninfo = utils.gen_info(gen)
                _logging_debug('%s is now blocked waiting for %s', self, value)
                value.add_callback(self._on_future_completion, value, ns,
                                   ds_conn, gen)
                return
            if isinstance(value, (tuple, list)):

                info = 'multi-yield from %s' % utils.gen_info(gen)
                mfut = MultiFuture(info)
                try:
                    for subfuture in value:
                        mfut.add_dependent(subfuture)
                    mfut.complete()
                except GeneratorExit:
                    raise
                except Exception as err:
                    _, _, tb = sys.exc_info()
                    mfut.set_exception(err, tb)
                mfut.add_callback(self._on_future_completion, mfut, ns,
                                  ds_conn, gen)
                return
            if _is_generator(value):

                raise NotImplementedError('Cannot defer to another generator.')
            raise RuntimeError('A tasklet should not yield a plain value: '
                               '%.200s yielded %.200r' % (info, value))