database fix
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# STAGE 1: BUILDER
|
# STAGE 1: BUILDER
|
||||||
# This stage installs build tools and Python dependencies
|
# This stage installs build tools and Python dependencies
|
||||||
FROM python:3.13.7-slim AS builder
|
FROM python:3.12.11-slim AS builder
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1 \
|
ENV PYTHONUNBUFFERED=1 \
|
||||||
DEBIAN_FRONTEND=noninteractive
|
DEBIAN_FRONTEND=noninteractive
|
||||||
@@ -23,7 +23,7 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|||||||
|
|
||||||
# STAGE 2: FINAL
|
# STAGE 2: FINAL
|
||||||
# This is the lean, final image for running the application
|
# This is the lean, final image for running the application
|
||||||
FROM python:3.13.7-slim
|
FROM python:3.12.11-slim
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1 \
|
ENV PYTHONUNBUFFERED=1 \
|
||||||
DEBIAN_FRONTEND=noninteractive
|
DEBIAN_FRONTEND=noninteractive
|
||||||
@@ -49,6 +49,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
jpegoptim \
|
jpegoptim \
|
||||||
libsox-fmt-mp3 \
|
libsox-fmt-mp3 \
|
||||||
lame \
|
lame \
|
||||||
|
libportaudio2 \
|
||||||
|
libportaudiocpp0 \
|
||||||
|
portaudio19-dev \
|
||||||
# Runtime libraries for Python packages
|
# Runtime libraries for Python packages
|
||||||
libxml2 \
|
libxml2 \
|
||||||
# Process manager
|
# Process manager
|
||||||
@@ -59,7 +62,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy installed Python packages from the builder stage
|
# Copy installed Python packages from the builder stage
|
||||||
COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
|
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
|
||||||
COPY --from=builder /usr/local/bin /usr/local/bin
|
COPY --from=builder /usr/local/bin /usr/local/bin
|
||||||
|
|
||||||
# Copy supervisor config and application code
|
# Copy supervisor config and application code
|
||||||
|
|||||||
35
main.py
35
main.py
@@ -22,7 +22,7 @@ import sys
|
|||||||
import re
|
import re
|
||||||
import importlib
|
import importlib
|
||||||
import collections.abc
|
import collections.abc
|
||||||
|
import time
|
||||||
import ocrmypdf
|
import ocrmypdf
|
||||||
import pypdf
|
import pypdf
|
||||||
import pytesseract
|
import pytesseract
|
||||||
@@ -40,6 +40,7 @@ from sqlalchemy import (Column, DateTime, Integer, String, Text,
|
|||||||
create_engine, delete, event)
|
create_engine, delete, event)
|
||||||
from sqlalchemy.orm import Session, declarative_base, sessionmaker
|
from sqlalchemy.orm import Session, declarative_base, sessionmaker
|
||||||
from sqlalchemy.pool import NullPool
|
from sqlalchemy.pool import NullPool
|
||||||
|
from sqlalchemy.exc import OperationalError
|
||||||
from string import Formatter
|
from string import Formatter
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
from typing import List as TypingList
|
from typing import List as TypingList
|
||||||
@@ -1250,7 +1251,37 @@ async def download_kokoro_models_if_missing():
|
|||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI):
|
async def lifespan(app: FastAPI):
|
||||||
logger.info("Application starting up...")
|
logger.info("Application starting up...")
|
||||||
Base.metadata.create_all(bind=engine)
|
# Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
create_attempts = 3
|
||||||
|
for attempt in range(1, create_attempts + 1):
|
||||||
|
try:
|
||||||
|
# use engine.begin() to ensure the DDL runs in a connection/transaction context
|
||||||
|
with engine.begin() as conn:
|
||||||
|
Base.metadata.create_all(bind=conn)
|
||||||
|
logger.info("Database tables ensured (create_all succeeded).")
|
||||||
|
break
|
||||||
|
except OperationalError as oe:
|
||||||
|
# Some SQLite drivers raise an OperationalError when two processes try to create the same table at once.
|
||||||
|
msg = str(oe).lower()
|
||||||
|
# If we see "already exists" we treat this as a race and retry briefly.
|
||||||
|
if "already exists" in msg or ("table" in msg and "already exists" in msg):
|
||||||
|
logger.warning(
|
||||||
|
"Database table creation race detected (attempt %d/%d): %s. Retrying...",
|
||||||
|
attempt,
|
||||||
|
create_attempts,
|
||||||
|
oe,
|
||||||
|
)
|
||||||
|
time.sleep(0.5)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
logger.exception("Database initialization failed with OperationalError.")
|
||||||
|
raise
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Unexpected error during DB initialization.")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
load_app_config()
|
load_app_config()
|
||||||
|
|
||||||
# Download required models on startup
|
# Download required models on startup
|
||||||
|
|||||||
Reference in New Issue
Block a user