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()
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)
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()
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.')
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)
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.')
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)
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)
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()
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()
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()
def _leave_transaction_management(self, managed): logger.info('Leaving Transaction') self.connection = self.old_connection _SetConnection(self.connection)
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()
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.')
def _leave_transaction_management(self, managed): logger.info('Leaving Transaction') self.connection = self.old_connection _SetConnection(self.connection)
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))