Files
Ekklesia/infrastructure/compose.yaml
T
ekklesiaandClaude Opus 5 af4a8009fb
CI / verify (pull_request) Successful in 36s
CI / infrastructure-smoke (pull_request) Successful in 21s
CI / migration-smoke (pull_request) Successful in 51s
fix: bound provider exchanges and stop the storage suite starving its server
The live storage suite failed in CI with three failures and a 30s runner
timeout while passing locally in under three seconds. Two independent
defects produced that, one of them a real runtime defect.

Provider calls had no deadline. `S3Client` was built with no
`requestHandler` and no `maxAttempts`, and the SDK's `connectionTimeout`
and `socketTimeout` both default to 0 — disabled. A provider that accepts
a connection and then goes silent therefore held the caller open forever,
bounded in the suite only by vitest and in production by nothing at all.
ADR 0030 promises such a failure reaches a module as the transient
`STORAGE_PROVIDER_UNAVAILABLE` it retries through; `translateStorageError`
already mapped the SDK's `TimeoutError` onto that code, but the branch was
unreachable because nothing ever timed out. The bound is on socket
idleness rather than total elapsed time so a module streaming a large
object is never cut off for being big, only for going quiet.

The suite provisioned a fresh bucket per test. Every S3 bucket becomes its
own SeaweedFS collection claiming a block of volumes from a budget the
server derives from free disk, and `mini` auto-configures a
production-shaped ~1GB volume — so the ceiling is small and varies with
the runner. Twelve buckets per run exhausted it, and an exhausted budget
is not a failed write: the master retries allocation internally and never
replies, which is the hang CI saw. Tests now share one bucket and isolate
by key namespace, which exercises prefix scoping at the same time;
bucket identity is provisioned only where it is the thing under test.
Pinning a small volume size makes the budget depend far less sharply on
the host's disk.

Verified by starving a server on purpose: the same exhaustion that used to
hang until the runner killed it now fails in ~15s with a structured error
and no runner timeout, so the failure is bounded rather than hidden. Ten
consecutive runs pass in ~2.1s each, using 28 volumes per run against a
5212 budget where the old suite used 84 against 161.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 14:54:58 -05:00

54 lines
2.1 KiB
YAML

name: ekklesia-development
services:
postgres:
image: postgres:18.4-alpine3.24
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB:?POSTGRES_DB is required}
POSTGRES_INITDB_ARGS: --data-checksums --auth-host=scram-sha-256 --auth-local=trust
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
POSTGRES_USER: ${POSTGRES_USER:?POSTGRES_USER is required}
TZ: UTC
ports:
- "127.0.0.1:${POSTGRES_PORT:-5432}:5432"
volumes:
- postgres-data:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 12
start_period: 10s
object-storage:
image: chrislusf/seaweedfs:4.28
restart: unless-stopped
# Every S3 bucket becomes its own SeaweedFS collection, and each collection claims a block of
# volumes from a budget the server derives as free-disk ÷ volume-size. Left to auto-configure,
# `mini` picks a production-shaped ~1GB volume, so a machine with a modest amount of free disk
# is left with room for only a handful of buckets — and an exhausted budget is not reported as a
# failed write, it simply stalls. Pinning a small volume size suits the object sizes this
# development and test service actually holds and makes the bucket budget depend on the host's
# disk far less sharply. It caps neither total stored bytes nor individual object size.
command: ["mini", "-dir=/data", "-master.volumeSizeLimitMB=32"]
environment:
AWS_ACCESS_KEY_ID: ${S3_ACCESS_KEY:?S3_ACCESS_KEY is required}
AWS_SECRET_ACCESS_KEY: ${S3_SECRET_KEY:?S3_SECRET_KEY is required}
S3_BUCKET: ${S3_BUCKET:?S3_BUCKET is required}
TZ: UTC
ports:
- "127.0.0.1:${S3_PORT:-8333}:8333"
volumes:
- object-storage-data:/data
healthcheck:
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:9333/cluster/status"]
interval: 5s
timeout: 5s
retries: 18
start_period: 15s
volumes:
object-storage-data:
postgres-data: