def execute_futures(self, scope: str = ''): ''' Execute all the registered futures in a new task :param scope: scoped futures to execute. Leave default for normal behavior ''' return execute.execute_futures(scope)
async def initialize(self, app=None): # loop self.app = app while True: got_obj = False try: func, tvars = await self.queue.get() got_obj = True load_task_vars(tvars) txn = get_transaction() tm = get_tm() if txn is None or ( txn.status in (Status.ABORTED, Status.COMMITTED, Status.CONFLICT) and txn._db_conn is None ): txn = await tm.begin() else: # still finishing current transaction, this connection # will be cut off, so we need to wait until we no longer # have an active transaction on the reqeust... await self.add((func, tvars)) await asyncio.sleep(1) continue try: await func() await tm.commit(txn=txn) except Exception as e: logger.error("Exception on writing execution", exc_info=e) await tm.abort(txn=txn) except ( RuntimeError, SystemExit, GeneratorExit, KeyboardInterrupt, asyncio.CancelledError, KeyboardInterrupt, ): # dive, these errors mean we're exit(ing) self._exceptions = True return except Exception as e: # noqa self._exceptions = True logger.error("Worker call failed", exc_info=e) finally: if got_obj: execute.execute_futures() self.queue.task_done()
async def __aexit__(self, exc_type, exc, tb): if self.adopt_parent_txn and self.previous_txn is not None: # take on parent's modified, added, deleted objects if necessary # before we commit or abort this transaction. # this is necessary because inside this block, the outer transaction # could have been attached to an object that changed. # we're ready to commit and we want to potentially commit everything # where, we we're adopted those objects with this transaction if self.previous_txn != self.txn: # try adopting currently registered objects self.txn.modified = { **self.previous_txn.modified, **self.txn.modified } self.txn.deleted = { **self.previous_txn.deleted, **self.txn.deleted } self.txn.added = {**self.previous_txn.added, **self.txn.added} self.adopt_objects(self.previous_txn.modified, self.txn) self.adopt_objects(self.previous_txn.deleted, self.txn) self.adopt_objects(self.previous_txn.added, self.txn) if self.abort_when_done: await self.tm.abort(txn=self.txn) else: await self.tm.commit(txn=self.txn) if self.adopt_parent_txn and self.previous_txn is not None: # restore transaction ownership of item from adoption done above if self.previous_txn != self.txn: # we adopted previously detetected transaction so now # we need to clear changed objects and restore ownership self.previous_txn.modified = {} self.previous_txn.deleted = {} self.previous_txn.added = {} for ob in self.adopted: ob.__txn__ = self.previous_txn if self.execute_futures: from guillotina.utils import execute execute.execute_futures()
async def evolve(self, container: IContainer): async with transaction(): cur_gen = await self._get_curr_gen() evolvers = self._get_evolvers(cur_gen) if len(evolvers) > 0: logger.info(f"Start evolving container {container}") for gen, evolver in evolvers: async with transaction(): logger.info( f"Evolving from generation '{cur_gen}' to '{gen}'") await evolver(container) cur_gen = await self._update_curr_gen(gen) logger.info("Executing futures") task = execute_futures() if task: await task clear_futures() logger.info(f"Container {container} is now at generation {gen}") else: logger.info(f"Container already at latest generation")