From 55dce99d3424c3180f02014884a4190f1f2f80d7 Mon Sep 17 00:00:00 2001 From: 3252a8 <3252a8@proton.me> Date: Sat, 16 May 2026 00:02:22 +0300 Subject: [PATCH] refactor: ack panel webhooks immediately and run event handling in bounded background task --- bot/services/panel_webhook_service.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/bot/services/panel_webhook_service.py b/bot/services/panel_webhook_service.py index 30c7be3..63be592 100644 --- a/bot/services/panel_webhook_service.py +++ b/bot/services/panel_webhook_service.py @@ -1,3 +1,4 @@ +import asyncio import hashlib import hmac import json @@ -29,6 +30,10 @@ EVENT_MAP = { class PanelWebhookService: + # Cap parallel background event handlers so an expiry burst from the panel + # cannot exhaust the DB pool or the YooKassa client. + _MAX_CONCURRENT_EVENTS = 50 + def __init__( self, bot: Bot, @@ -42,6 +47,7 @@ class PanelWebhookService: self.i18n = i18n self.async_session_factory = async_session_factory self.panel_service = panel_service + self._event_semaphore = asyncio.Semaphore(self._MAX_CONCURRENT_EVENTS) if not self.settings.PANEL_WEBHOOK_SECRET: logging.error( "PANEL_WEBHOOK_SECRET is not configured. Panel webhooks will be rejected." @@ -243,9 +249,24 @@ class PanelWebhookService: telegram_id if telegram_id is not None else "N/A", ) - await self.handle_event(event_name, user_data) + # Acknowledge immediately so the panel does not retry while we run + # auto-renew charges, DB lookups and SMTP. Heavy work is bounded by a + # semaphore to survive expiry bursts. + asyncio.create_task( + self._run_event_in_background(event_name, user_data), + name=f"panel_event_{event_name}", + ) return web.Response(status=200, text="ok") + async def _run_event_in_background(self, event_name: str, user_payload: dict) -> None: + async with self._event_semaphore: + try: + await self.handle_event(event_name, user_payload) + except Exception: + logging.exception( + "Panel webhook background handler failed for event %s", event_name + ) + async def panel_webhook_route(request: web.Request): service: PanelWebhookService = request.app["panel_webhook_service"]