class CarbonSender(Service): host = "127.0.0.1" # type: str port = 2003 # type: int send_interval = 5 # type: int protocol = "udp" # type: str namespace = ("",) # type: t.Iterable[str] storage = TotalStorage _handle = None # type: PeriodicCallback async def start(self) -> None: namespace = ".".join( strip_carbon_ns(item) for item in self.namespace ) client = PROTOCOLS[self.protocol]( self.host, self.port, namespace=namespace, storage=self.storage(), ) set_client(client) self._handle = PeriodicCallback(client.send) self._handle.start(self.send_interval, loop=self.loop) log.info( "Periodic carbon metrics sender started. Send to %s://%s:%d with " "interval %rs", self.protocol, self.host, self.port, self.send_interval, ) async def stop(self, exc: Exception = None) -> None: self._handle.stop()
async def start(self) -> None: addr = self._get_socket_addr() if addr is None: log.debug( "NOTIFY_SOCKET not exported. Skipping service %r", self, ) return None self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) self.socket.connect(addr) self.socket.setblocking(False) await self._send("STATUS=starting") if self.watchdog_interval is None: return if self.watchdog_interval != WATCHDOG_INTERVAL: watchdog_usec = int(self.watchdog_interval * 1000000) await self._send(f"WATCHDOG_USEC={watchdog_usec}") self.start_event.set() self._watchdog_timer = PeriodicCallback( self._send, "WATCHDOG=1", ) # Send notifications twice as often self._watchdog_timer.start(self.watchdog_interval / 2) # Removing signals from entrypoint factory because the currently # running entrypoint instance has been cloned the signals. entrypoint.POST_START.disconnect(self._post_start) entrypoint.PRE_STOP.disconnect(self._pre_stop)
class RespawningProcessService(ProcessService, ABC): process_poll_timeout: int = 5 _supervisor: PeriodicCallback async def __supervise(self) -> None: if not hasattr(self, "process"): return if await self.loop.run_in_executor(None, self.process.is_alive): return log.info( "Process in service %r exited with code %r, respawning.", self, self.process.exitcode, ) await super().start() async def start(self) -> None: await super().start() self._supervisor = PeriodicCallback(self.__supervise) self._supervisor.start(self.process_poll_timeout, ) async def stop(self, exception: Exception = None) -> Any: await self._supervisor.stop() await super().stop(exception)
class CarbonSender(Service): host = '127.0.0.1' # type: str port = 2003 # type: int send_interval = 5 # type: int protocol = 'udp' # type: str namespace = '' # type: List[str] storage = TotalStorage _handle = None # type: PeriodicCallback async def start(self): namespace = ".".join(strip_carbon_ns(item) for item in self.namespace) client = PROTOCOLS[self.protocol]( self.host, self.port, namespace=namespace, storage=self.storage(), ) set_client(client) self._handle = PeriodicCallback(client.send) self._handle.start(self.send_interval, loop=self.loop) log.info( 'Periodic carbon metrics sender started. Send to %s://%s:%d with ' 'interval %rs', self.protocol, self.host, self.port, self.send_interval) async def stop(self, *_): self._handle.stop()
async def test_shield(event_loop): counter = 0 async def task(): nonlocal counter counter += 1 await asyncio.sleep(2) periodic = PeriodicCallback(task) periodic.start(0.1, event_loop, shield=True) await asyncio.sleep(0) with pytest.raises(asyncio.CancelledError): await periodic.stop() assert counter == 1 # No shield counter = 0 periodic = PeriodicCallback(task) periodic.start(0.1, event_loop, shield=False) await asyncio.sleep(0) with pytest.raises(asyncio.CancelledError): await periodic.stop() assert counter == 0
async def start(self): namespace = ".".join(strip_carbon_ns(item) for item in self.namespace) client = PROTOCOLS[self.protocol](self.host, self.port, namespace=namespace, storage=self.storage(), loop=self.loop) set_client(client) self._handle = PeriodicCallback(client.send) self._handle.start(self.send_interval, loop=self.loop) log.info( 'Periodic carbon metrics sender started. Send to %s://%s:%d with ' 'interval %rs', self.protocol, self.host, self.port, self.send_interval)
def wrap_logging_handler(handler: logging.Handler, loop: asyncio.AbstractEventLoop = None, buffer_size: int = 1024, flush_interval: float = 0.1): loop = loop or asyncio.get_event_loop() buffered_handler = AsyncMemoryHandler(buffer_size, target=handler) periodic = PeriodicCallback(buffered_handler.flush_async) loop.call_soon_threadsafe(periodic.start, flush_interval, loop) return buffered_handler
async def test_long_func(event_loop): counter = 0 async def task(): nonlocal counter counter += 1 await asyncio.sleep(0.5) periodic = PeriodicCallback(task) periodic.start(0.1, event_loop) await asyncio.sleep(1, loop=event_loop) periodic.stop() assert 1 < counter < 3
async def test_periodic(event_loop): counter = 0 def task(): nonlocal counter counter += 1 periodic = PeriodicCallback(task) periodic.start(0.1, event_loop) await asyncio.sleep(0.5, loop=event_loop) periodic.stop() assert 4 < counter < 7 await asyncio.sleep(0.5, loop=event_loop) assert 4 < counter < 7
class SDWatchdogService(Service): socket: socket.socket watchdog_interval: Optional[TimeoutType] _watchdog_timer: PeriodicCallback async def _send(self, payload: str) -> None: try: await self.loop.sock_sendall( self.socket, payload.encode(), ) except (ConnectionError, OSError) as e: log.warning("SystemD notify socket communication problem: %r", e) async def _post_start(self, services: Tuple[Service, ...], **__: Any) -> None: if not hasattr(self, "socket"): return await self._send(f"STATUS=Started {len(services)} services") await self._send("READY=1") async def _pre_stop(self, *_: Any, **__: Any) -> None: if not hasattr(self, "socket"): return await self._send("STOPPING=1") def __init__(self, *, watchdog_interval: Optional[TimeoutType] = WATCHDOG_INTERVAL, **kwargs: Any): self.watchdog_interval = watchdog_interval entrypoint.POST_START.connect(self._post_start) entrypoint.PRE_STOP.connect(self._pre_stop) super().__init__(**kwargs) @staticmethod def _get_socket_addr() -> Optional[str]: addr = os.getenv("NOTIFY_SOCKET") if addr is None: return None if addr[0] == "@": addr = "\0" + addr[1:] return addr async def start(self) -> None: addr = self._get_socket_addr() if addr is None: log.debug( "NOTIFY_SOCKET not exported. Skipping service %r", self, ) return None self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) self.socket.connect(addr) self.socket.setblocking(False) await self._send("STATUS=starting") if self.watchdog_interval is None: return if self.watchdog_interval != WATCHDOG_INTERVAL: watchdog_usec = int(self.watchdog_interval * 1000000) await self._send(f"WATCHDOG_USEC={watchdog_usec}") self.start_event.set() self._watchdog_timer = PeriodicCallback( self._send, "WATCHDOG=1", ) # Send notifications twice as often self._watchdog_timer.start(self.watchdog_interval / 2) # Removing signals from entrypoint factory because the currently # running entrypoint instance has been cloned the signals. entrypoint.POST_START.disconnect(self._post_start) entrypoint.PRE_STOP.disconnect(self._pre_stop) async def stop(self, exception: Exception = None) -> Any: await self._watchdog_timer.stop()
async def start(self) -> None: await super().start() self._supervisor = PeriodicCallback(self.__supervise) self._supervisor.start(self.process_poll_timeout, )