update
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import threading
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import AsyncIterator
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
from ocr_sprint import __version__
|
||||
@@ -11,7 +15,10 @@ from ocr_sprint.api.routes import documents, ground_truth, health
|
||||
from ocr_sprint.config import get_settings
|
||||
from ocr_sprint.db import models as _models # noqa: F401 (register ORM tables)
|
||||
from ocr_sprint.db.base import Base, get_engine
|
||||
from ocr_sprint.utils.logging import configure_logging
|
||||
from ocr_sprint.utils.logging import configure_logging, get_logger
|
||||
|
||||
|
||||
_startup_logger = get_logger(__name__)
|
||||
|
||||
|
||||
def _ensure_schema() -> None:
|
||||
@@ -24,6 +31,42 @@ def _ensure_schema() -> None:
|
||||
Base.metadata.create_all(bind=get_engine())
|
||||
|
||||
|
||||
def _warmup_models_background() -> None:
|
||||
"""Load PaddleOCR and PP-Structure models in a background thread.
|
||||
|
||||
Running in a thread keeps the lifespan non-blocking so uvicorn can
|
||||
start accepting health-check requests immediately while the heavy models
|
||||
load (~5-15s on CPU). Requests that arrive before warmup completes will
|
||||
wait on the existing _lock in each module rather than racing to load.
|
||||
"""
|
||||
from ocr_sprint.config import get_settings as _gs
|
||||
from ocr_sprint.pipeline import ocr as _ocr
|
||||
from ocr_sprint.pipeline import table as _table
|
||||
|
||||
s = _gs()
|
||||
try:
|
||||
_ocr.warmup()
|
||||
except Exception as exc:
|
||||
_startup_logger.warning("paddleocr.warmup.failed", error=str(exc))
|
||||
|
||||
if s.tables_enabled:
|
||||
try:
|
||||
_table.warmup()
|
||||
except Exception as exc:
|
||||
_startup_logger.warning("pp_structure.warmup.failed", error=str(exc))
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
||||
"""FastAPI lifespan: warm OCR models on startup in a background thread."""
|
||||
_startup_logger.info("startup.warmup.begin")
|
||||
t = threading.Thread(target=_warmup_models_background, name="ocr-warmup", daemon=True)
|
||||
t.start()
|
||||
yield
|
||||
# Shutdown: nothing to clean up (models are process-global singletons).
|
||||
_startup_logger.info("shutdown.complete")
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
"""Application factory — keeps top-level state easy to test."""
|
||||
settings = get_settings()
|
||||
@@ -34,6 +77,7 @@ def create_app() -> FastAPI:
|
||||
root_path = getattr(settings, "root_path", "")
|
||||
|
||||
app = FastAPI(
|
||||
lifespan=lifespan,
|
||||
title="OCR Sprint Service",
|
||||
version=__version__,
|
||||
description="OCR + structured extraction for Indonesian police 'surat sprint' documents.",
|
||||
|
||||
Reference in New Issue
Block a user