feat: ingest burst hardening — defer lifecycle/auth, workers, nginx (v0.5.5)

Defer notify_lifecycle and notify_auth_login after commit; SAC_UVICORN_WORKERS via sac-api-start.sh; nginx /api/v1/events timeout; mass-update confirm in Hosts UI.
This commit is contained in:
2026-07-08 13:16:13 +10:00
parent 1ad01534f1
commit b148b558c3
14 changed files with 189 additions and 12 deletions
+10 -2
View File
@@ -52,7 +52,9 @@ from app.services.notify_dispatch import (
notify_lifecycle,
notify_problem,
notify_rdg_connection,
schedule_notify_auth_login,
schedule_notify_daily_report,
schedule_notify_lifecycle,
)
router = APIRouter(prefix="/events", tags=["events"])
@@ -100,15 +102,17 @@ def post_event(
problem = None
problem_created = False
deferred_daily_report_id: int | None = None
deferred_lifecycle_id: int | None = None
deferred_auth_login_id: int | None = None
if created:
problem, problem_created = maybe_create_problem(db, event)
maybe_auto_disconnect_stuck_rdp_session(db, event)
if event.type in DAILY_REPORT_EVENT_TYPES:
deferred_daily_report_id = event.id
elif event.type == LIFECYCLE_EVENT_TYPE:
notify_lifecycle(event, db=db)
deferred_lifecycle_id = event.id
elif event.type in AUTH_LOGIN_SUCCESS_TYPES:
notify_auth_login(event, db=db)
deferred_auth_login_id = event.id
elif event.type in RDG_CONNECTION_TYPES:
notify_rdg_connection(event, db=db)
else:
@@ -122,6 +126,10 @@ def post_event(
if deferred_daily_report_id is not None:
background_tasks.add_task(schedule_notify_daily_report, deferred_daily_report_id)
if deferred_lifecycle_id is not None:
background_tasks.add_task(schedule_notify_lifecycle, deferred_lifecycle_id)
if deferred_auth_login_id is not None:
background_tasks.add_task(schedule_notify_auth_login, deferred_auth_login_id)
body = _ingest_response(event, created=created)
if problem is not None:
+1
View File
@@ -36,6 +36,7 @@ class Settings(BaseSettings):
database_url: str = "postgresql+psycopg2://sac:sac@localhost:5432/sac"
sac_db_pool_size: int = 15
sac_db_max_overflow: int = 25
sac_uvicorn_workers: int = 4
sac_public_url: str = "http://localhost:8000"
# URL для скачивания RDP bundle с ПК (WinRM). По умолчанию = SAC_PUBLIC_URL.
sac_agent_bundle_base_url: str = ""
+17 -3
View File
@@ -216,16 +216,30 @@ def notify_rdg_connection(event: Event, *, db: Session | None = None) -> None:
def schedule_notify_daily_report(event_db_id: int) -> None:
"""Отложенное оповещение по суточному отчёту (после commit ingest, вне горячего POST)."""
_schedule_deferred_event_notify(event_db_id, notify_daily_report, label="daily report")
def schedule_notify_lifecycle(event_db_id: int) -> None:
"""Отложенное lifecycle-оповещение (после commit ingest)."""
_schedule_deferred_event_notify(event_db_id, notify_lifecycle, label="lifecycle")
def schedule_notify_auth_login(event_db_id: int) -> None:
"""Отложенное оповещение об успешном RDP/SSH входе (после commit ingest)."""
_schedule_deferred_event_notify(event_db_id, notify_auth_login, label="auth login")
def _schedule_deferred_event_notify(event_db_id: int, handler, *, label: str) -> None:
from app.database import SessionLocal
db = SessionLocal()
try:
event = db.get(Event, event_db_id)
if event is None:
logger.warning("deferred daily report notify: event id=%s not found", event_db_id)
logger.warning("deferred %s notify: event id=%s not found", label, event_db_id)
return
notify_daily_report(event, db=db)
handler(event, db=db)
except Exception:
logger.exception("deferred daily report notify failed event_db_id=%s", event_db_id)
logger.exception("deferred %s notify failed event_db_id=%s", label, event_db_id)
finally:
db.close()
+1 -1
View File
@@ -1,5 +1,5 @@
"""Единый источник версии SAC (API, health, логи, OpenAPI)."""
APP_NAME = "Security Alert Center"
APP_VERSION = "0.5.4"
APP_VERSION = "0.5.5"
APP_VERSION_LABEL = f"{APP_NAME} v.{APP_VERSION}"