Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions app/adapters/http/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ async def lifespan(app: FastAPI):
redis_url = os.environ.get("REDIS_URL", "").strip()
if redis_url:
try:
from app.infra.queue.queue import create_queue
queue = await create_queue(redis_url)
if queue is not None:
_deps.get_async_service()._queue = queue
except Exception:
pass # arq not installed or Redis unreachable — fall back silently
from arq import create_pool
from arq.connections import RedisSettings
pool = await create_pool(RedisSettings.from_dsn(redis_url))
from app.infra.queue.queue import ArqJobQueue
_deps.get_async_service()._queue = ArqJobQueue(pool)
except Exception as exc:
logger.error("REDIS_URL is set but Redis is unreachable: %s", exc)
raise RuntimeError(f"REDIS_URL is set but Redis is unreachable: {exc}") from exc

yield

Expand Down
11 changes: 4 additions & 7 deletions app/adapters/http/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,13 @@ async def init_job_store() -> JobStore:

db_url = os.environ.get("DATABASE_URL", "").strip()
if db_url:
from app.infra.jobs.postgres_job_store import PostgresJobStore
try:
from app.infra.jobs.postgres_job_store import PostgresJobStore
_job_store = await PostgresJobStore.create_pool(dsn=db_url)
return _job_store
except Exception as exc:
_log.error(
"Failed to connect to Postgres (%s). "
"Falling back to SQLite — THIS IS NOT SAFE IN PRODUCTION.",
exc,
)
_log.error("DATABASE_URL is set but Postgres is unreachable: %s", exc)
raise RuntimeError(f"DATABASE_URL is set but Postgres is unreachable: {exc}") from exc
return _job_store

from app.infra.jobs.sqlite_job_store import SQLiteJobStore
_job_store = SQLiteJobStore()
Expand Down
9 changes: 8 additions & 1 deletion app/infra/queue/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
Run the worker:
arq app.infra.queue.worker.WorkerSettings
"""
import logging
import os
from typing import Any

logger = logging.getLogger(__name__)


async def run_inference(ctx: dict, job_id: str, model: str, version: str, payload: Any) -> None:
from uuid import UUID
Expand Down Expand Up @@ -61,7 +64,11 @@ async def startup(ctx: dict) -> None:
db_url = os.environ.get("DATABASE_URL", "").strip()
if db_url:
from app.infra.jobs.postgres_job_store import PostgresJobStore
store = await PostgresJobStore.create_pool(dsn=db_url)
try:
store = await PostgresJobStore.create_pool(dsn=db_url)
except Exception as exc:
logger.error("DATABASE_URL is set but Postgres is unreachable: %s", exc)
raise RuntimeError(f"DATABASE_URL is set but Postgres is unreachable: {exc}") from exc
else:
from app.infra.jobs.sqlite_job_store import SQLiteJobStore
store = SQLiteJobStore()
Expand Down
2 changes: 2 additions & 0 deletions docs/guides/production-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ REDIS_URL=redis://localhost:6379/0

When `ENV=production` and `API_KEYS` is not set, the server refuses to start.

When `DATABASE_URL` or `REDIS_URL` is set but the service is unreachable, the server (and arq worker) refuse to start with a clear error instead of silently falling back to SQLite or in-process async. Ensure both services are healthy before starting the application.

---

## Start services
Expand Down
7 changes: 3 additions & 4 deletions tests/test_engine_phase2.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,8 +660,7 @@ async def fake_create_pool(dsn=None):
store = asyncio.run(deps_mod.init_job_store())
assert store is mock_store

def test_falls_back_to_sqlite_when_postgres_unavailable(self):
from app.infra.jobs.sqlite_job_store import SQLiteJobStore
def test_raises_when_database_url_set_but_postgres_unavailable(self):
import app.adapters.http.deps as deps_mod

async def fail(*a, **kw):
Expand All @@ -672,5 +671,5 @@ async def fail(*a, **kw):
"app.infra.jobs.postgres_job_store.PostgresJobStore.create_pool",
side_effect=fail,
):
store = asyncio.run(deps_mod.init_job_store())
assert isinstance(store, SQLiteJobStore)
with pytest.raises(RuntimeError, match="DATABASE_URL is set but Postgres is unreachable"):
asyncio.run(deps_mod.init_job_store())
Loading