73 lines
2.1 KiB
Python
73 lines
2.1 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import time
|
|
from datetime import timedelta
|
|
|
|
from sqlmodel import Session
|
|
|
|
from app.core.config import settings
|
|
from app.core.db import engine
|
|
from app.models import TerminalMessageRaw, get_datetime_utc
|
|
from app.mqtt.parsers.mixed import parse_mixed_payload
|
|
from app.mqtt.repository import (
|
|
fetch_pending_rows_for_update,
|
|
mark_failed,
|
|
mark_processing,
|
|
mark_succeeded,
|
|
write_events_and_update_state,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def _next_retry_delay_seconds(attempts: int) -> int:
|
|
# Bounded exponential backoff: 30s, 60s, 120s, ... capped to 15m.
|
|
return int(min(30 * (2 ** max(attempts - 1, 0)), 900))
|
|
|
|
|
|
def _process_one_row(*, session: Session, row: TerminalMessageRaw) -> None:
|
|
mark_processing(session=session, row=row)
|
|
session.commit()
|
|
|
|
try:
|
|
events = parse_mixed_payload(row.payload_bytes)
|
|
write_events_and_update_state(session=session, row=row, events=events)
|
|
mark_succeeded(session=session, row=row)
|
|
except Exception as exc: # pragma: no cover - safety net path
|
|
delay = _next_retry_delay_seconds(row.parse_attempts)
|
|
mark_failed(
|
|
session=session,
|
|
row=row,
|
|
parse_error=str(exc),
|
|
next_retry_at=get_datetime_utc() + timedelta(seconds=delay),
|
|
)
|
|
|
|
session.commit()
|
|
|
|
|
|
def process_pending_batch(*, session: Session, batch_size: int) -> int:
|
|
rows = fetch_pending_rows_for_update(session=session, batch_size=batch_size)
|
|
for row in rows:
|
|
_process_one_row(session=session, row=row)
|
|
return len(rows)
|
|
|
|
|
|
def run() -> None:
|
|
logging.basicConfig(level=logging.INFO)
|
|
sleep_seconds = max(settings.PARSER_POLL_INTERVAL_MS, 50) / 1000.0
|
|
|
|
while True:
|
|
with Session(engine) as session:
|
|
processed = process_pending_batch(
|
|
session=session, batch_size=settings.PARSER_BATCH_SIZE
|
|
)
|
|
if processed == 0:
|
|
time.sleep(sleep_seconds)
|
|
else:
|
|
logger.info("Processed %s pending MQTT raw messages", processed)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
run()
|