async def run(self) -> None: """ Call chain is: - multiprocessing.Process -> _run_process * isolates to a new process - _run_process -> run_process * sets up subprocess logging - run_process -> _do_run * runs the event loop and transitions into async context - _do_run -> do_run * sets up event bus and then enters user function. """ process = ctx.Process( target=self.run_process, args=(self._boot_info, ), ) loop = asyncio.get_event_loop() await loop.run_in_executor(None, process.start) try: await loop.run_in_executor(None, process.join) finally: kill_process_gracefully( process, get_logger('trinity.extensibility.TrioIsolatedComponent'), )
def trinity_boot(args: Namespace, trinity_config: TrinityConfig, extra_kwargs: Dict[str, Any], listener: logging.handlers.QueueListener, logger: logging.Logger) -> Tuple[multiprocessing.Process, ...]: # start the listener thread to handle logs produced by other processes in # the local logger. listener.start() ensure_beacon_dirs(trinity_config.get_app_config(BeaconAppConfig)) # First initialize the database process. database_server_process = ctx.Process( name="DB", target=run_database_process, args=( trinity_config, LevelDB, ), kwargs=extra_kwargs, ) # start the processes database_server_process.start() logger.info("Started DB server process (pid=%d)", database_server_process.pid) try: wait_for_ipc(trinity_config.database_ipc_path) except TimeoutError as e: logger.error("Timeout waiting for database to start. Exiting...") kill_process_gracefully(database_server_process, logger) ArgumentParser().error(message="Timed out waiting for database start") return None return (database_server_process,)
def trinity_boot(boot_info: BootInfo) -> Tuple[multiprocessing.Process]: trinity_config = boot_info.trinity_config ensure_eth1_dirs(trinity_config.get_app_config(Eth1AppConfig)) logger = logging.getLogger('trinity') # First initialize the database process. database_server_process: multiprocessing.Process = ctx.Process( name="DB", target=run_database_process, args=( boot_info, LevelDB, ), ) # start the processes database_server_process.start() logger.info("Started DB server process (pid=%d)", database_server_process.pid) # networking process needs the IPC socket file provided by the database process try: wait_for_ipc(trinity_config.database_ipc_path) except TimeoutError: logger.error("Timeout waiting for database to start. Exiting...") kill_process_gracefully(database_server_process, logger) ArgumentParser().error(message="Timed out waiting for database start") return None return (database_server_process,)
def kill_trinity_gracefully(trinity_config: TrinityConfig, logger: logging.Logger, processes: Iterable[multiprocessing.Process], component_manager_service: ComponentManagerService, reason: str = None) -> None: # When a user hits Ctrl+C in the terminal, the SIGINT is sent to all processes in the # foreground *process group*, so both our networking and database processes will terminate # at the same time and not sequentially as we'd like. That shouldn't be a problem but if # we keep getting unhandled BrokenPipeErrors/ConnectionResetErrors like reported in # https://github.com/ethereum/py-evm/issues/827, we might want to change the networking # process' signal handler to wait until the DB process has terminated before doing its # thing. # Notice that we still need the kill_process_gracefully() calls here, for when the user # simply uses 'kill' to send a signal to the main process, but also because they will # perform a non-gracefull shutdown if the process takes too long to terminate. hint = f"({reason})" if reason else f"" logger.info('Shutting down Trinity %s', hint) component_manager_service.cancel_nowait() for process in processes: # Our sub-processes will have received a SIGINT already (see comment above), so here we # wait 2s for them to finish cleanly, and if they fail we kill them for real. process.join(2) if process.is_alive(): kill_process_gracefully(process, logger) logger.info('%s process (pid=%d) terminated', process.name, process.pid) remove_dangling_ipc_files(logger, trinity_config.ipc_dir) ArgumentParser().exit(message=f"Trinity shutdown complete {hint}\n")
def database_server_ipc_path(): core_db = AtomicDB() core_db[b'key-a'] = b'value-a' chaindb = ChainDB(core_db) # TODO: use a custom chain class only for testing. chaindb.persist_header(ROPSTEN_GENESIS_HEADER) with tempfile.TemporaryDirectory() as temp_dir: trinity_config = TrinityConfig( network_id=ROPSTEN_NETWORK_ID, trinity_root_dir=temp_dir, ) trinity_config.add_app_config(Eth1AppConfig(trinity_config, None)) initialize_data_dir(trinity_config) manager = create_db_server_manager(trinity_config, core_db) chaindb_server_process = multiprocessing.Process( target=serve_chaindb, args=(manager, ), ) chaindb_server_process.start() wait_for_ipc(trinity_config.database_ipc_path) try: yield trinity_config.database_ipc_path finally: kill_process_gracefully(chaindb_server_process, logging.getLogger())
def trinity_boot(args: Namespace, trinity_config: TrinityConfig, extra_kwargs: Dict[str, Any], plugin_manager: PluginManager, listener: logging.handlers.QueueListener, event_bus: EventBus, main_endpoint: Endpoint, logger: logging.Logger) -> None: # start the listener thread to handle logs produced by other processes in # the local logger. listener.start() event_bus.start() # First initialize the database process. database_server_process = ctx.Process( name="DB", target=run_database_process, args=( trinity_config, LevelDB, ), kwargs=extra_kwargs, ) # start the processes database_server_process.start() logger.info("Started DB server process (pid=%d)", database_server_process.pid) # networking process needs the IPC socket file provided by the database process try: wait_for_ipc(trinity_config.database_ipc_path) except TimeoutError as e: logger.error("Timeout waiting for database to start. Exiting...") kill_process_gracefully(database_server_process, logger) ArgumentParser().error(message="Timed out waiting for database start") def kill_trinity_with_reason(reason: str) -> None: kill_trinity_gracefully(logger, (database_server_process, ), plugin_manager, main_endpoint, event_bus, reason=reason) main_endpoint.subscribe(ShutdownRequest, lambda ev: kill_trinity_with_reason(ev.reason)) plugin_manager.prepare(args, trinity_config, extra_kwargs) kill_trinity_with_reason("No beacon support yet. SOON!") try: loop = asyncio.get_event_loop() loop.add_signal_handler(signal.SIGTERM, lambda: kill_trinity_with_reason("SIGTERM")) loop.run_forever() loop.close() except KeyboardInterrupt: kill_trinity_with_reason("CTRL+C / Keyboard Interrupt")
def trinity_boot( args: Namespace, trinity_config: TrinityConfig, extra_kwargs: Dict[str, Any], plugin_manager: PluginManager, listener: logging.handlers.QueueListener, main_endpoint: TrinityMainEventBusEndpoint, logger: logging.Logger) -> Tuple[multiprocessing.Process, ...]: # start the listener thread to handle logs produced by other processes in # the local logger. listener.start() ensure_eth1_dirs(trinity_config.get_app_config(Eth1AppConfig)) # First initialize the database process. database_server_process: multiprocessing.Process = ctx.Process( name="DB", target=run_database_process, args=( trinity_config, LevelDB, ), kwargs=extra_kwargs, ) networking_process: multiprocessing.Process = ctx.Process( name="networking", target=launch_node, args=( args, trinity_config, ), kwargs=extra_kwargs, ) # start the processes database_server_process.start() logger.info("Started DB server process (pid=%d)", database_server_process.pid) # networking process needs the IPC socket file provided by the database process try: wait_for_ipc(trinity_config.database_ipc_path) except TimeoutError as e: logger.error("Timeout waiting for database to start. Exiting...") kill_process_gracefully(database_server_process, logger) ArgumentParser().error(message="Timed out waiting for database start") return None networking_process.start() logger.info("Started networking process (pid=%d)", networking_process.pid) return (database_server_process, networking_process)