diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index c2bf29e..d0fa8a7 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -96,13 +96,13 @@ jobs: echo 'Skipping L2 (no changes)' fi - # Deploy stacks + # Deploy stacks (--resolve-image always forces re-pull of :latest) if [ \"\$BUILD_L1\" = true ]; then - cd l1 && source .env && docker stack deploy -c docker-compose.yml celery && cd .. + cd l1 && source .env && docker stack deploy --resolve-image always -c docker-compose.yml celery && cd .. echo 'L1 stack deployed' fi if [ \"\$BUILD_L2\" = true ]; then - cd l2 && source .env && docker stack deploy -c docker-compose.yml activitypub && cd .. + cd l2 && source .env && docker stack deploy --resolve-image always -c docker-compose.yml activitypub && cd .. echo 'L2 stack deployed' fi diff --git a/l2/app/__init__.py b/l2/app/__init__.py index e4938e2..fbc713a 100644 --- a/l2/app/__init__.py +++ b/l2/app/__init__.py @@ -160,6 +160,11 @@ def create_app() -> FastAPI: template_dir = Path(__file__).parent / "templates" app.state.templates = create_jinja_env(template_dir) + # Health check (skips auth middleware via _SKIP_PREFIXES) + @app.get("/health") + async def health(): + return JSONResponse({"status": "ok"}) + # Custom 404 handler @app.exception_handler(404) async def not_found_handler(request: Request, exc): diff --git a/l2/db.py b/l2/db.py index 205271d..465826c 100644 --- a/l2/db.py +++ b/l2/db.py @@ -187,9 +187,16 @@ async def init_pool(): max_size=10, command_timeout=60 ) - # Create tables if they don't exist + # Create tables if they don't exist (advisory lock prevents deadlock + # when multiple uvicorn workers start simultaneously) async with _pool.acquire() as conn: - await conn.execute(SCHEMA) + acquired = await conn.fetchval("SELECT pg_try_advisory_lock(42)") + if acquired: + try: + await conn.execute(SCHEMA) + finally: + await conn.execute("SELECT pg_advisory_unlock(42)") + # If another worker holds the lock, schema is being created — skip async def close_pool(): diff --git a/l2/docker-compose.yml b/l2/docker-compose.yml index 9c91ea4..afb5644 100644 --- a/l2/docker-compose.yml +++ b/l2/docker-compose.yml @@ -60,7 +60,7 @@ services: - OAUTH_LOGOUT_URL=https://account.rose-ash.com/auth/sso-logout/ # DATABASE_URL, ARTDAG_DOMAIN, ARTDAG_USER, JWT_SECRET, SECRET_KEY from .env file healthcheck: - test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8200/')"] + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8200/health')"] interval: 10s timeout: 5s retries: 3