The DB is at max_connections=100 and several stack services hold persistent pools (several as the postgres superuser, idle for hours), so peaks hit "too many connections". The MCP is a minor contributor but easy to bound: - Dockerfile: uvicorn --workers 2 → 1. The MCP's connection budget is workers × MCP_POOL_MAX, so this caps it at 8 backends instead of 16. Scale via MCP_POOL_MAX, not workers, so the budget stays obvious. (Pairs with the minconn=0 lazy pool already on this branch: 0 connections held when idle.) - analytics_ro_role.sql: add idle_session_timeout=5min so the DB reaps the MCP's idle POOLED connections (idle_in_transaction never reaps them — they're idle outside a txn) and returns the slots. Safe because the server now discards + transparently retries a reaped connection instead of erroring. Note: the dominant fix is stack-wide (get the superuser app pools onto bounded, timed roles; consider PgBouncer; or raise max_connections) — out of this repo's scope but documented in the review. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
36 lines
1.7 KiB
Docker
36 lines
1.7 KiB
Docker
# fleetanalytics-mcp — read-only Fleet Analytics MCP server.
|
||
# Coolify auto-detects this Dockerfile: set the app port to 8892, attach the
|
||
# domain (e.g. fleetmcp.rahamafresh.com) in the Coolify UI, set DATABASE_URL
|
||
# (analytics_ro DSN) + MCP_AUTH_TOKENS as secrets, and connect the app to the
|
||
# network that can reach timescale_db. See README.md / docs/ANALYTICS_MCP.md.
|
||
FROM python:3.12-slim
|
||
|
||
# uv for fast, reproducible dependency installs.
|
||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
||
|
||
WORKDIR /app
|
||
|
||
# Install ONLY dependencies (flat module — the project itself is not a package).
|
||
# Copy the lockfile and build --frozen so rebuilds are reproducible: without it,
|
||
# `uv sync` re-resolves the >= ranges in pyproject.toml and a redeploy could pull a
|
||
# newer, behaviour-changed mcp/starlette and break the running server.
|
||
COPY pyproject.toml uv.lock ./
|
||
RUN uv sync --no-dev --no-install-project --frozen
|
||
ENV PATH="/app/.venv/bin:$PATH"
|
||
|
||
COPY analytics_mcp.py ./
|
||
|
||
# Run as a non-root user (least privilege; nothing here needs root).
|
||
RUN useradd -m -u 10001 app && chown -R app:app /app
|
||
USER app
|
||
|
||
EXPOSE 8892
|
||
|
||
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
||
CMD python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:8892/healthz').status==200 else 1)" || exit 1
|
||
|
||
# Single worker: this is a low-traffic read-only proxy for a handful of analysts, and
|
||
# the DB connection budget = workers × MCP_POOL_MAX. One worker (× default pool 8) caps
|
||
# the MCP at 8 backends instead of 16, which matters on a shared 100-connection DB.
|
||
# Scale out by raising MCP_POOL_MAX, not workers, so the budget stays obvious.
|
||
CMD ["uvicorn", "analytics_mcp:app", "--host", "0.0.0.0", "--port", "8892", "--workers", "1"]
|