feat(ops): vision-manager sidecar to autoscale the llama.cpp container
Add a tiny privileged sidecar (vision-manager/) that polls the analysis backlog in Postgres and starts/stops the mangalord-vision container by name: start when analyze_page jobs are pending, stop after STOP_DEBOUNCE idle. It is the single owner of the vision lifecycle and the only component with Docker access — scoped through tecnativa/docker-socket-proxy (CONTAINERS+POST only) on an internal-only network, so the internet-facing backend never touches the socket. Both helpers sit behind `profiles: [ai]` (vanilla `compose up` is unaffected). The manager debounces the stop, gates the first request on GET /health==200 after a cold start, honours a crawl RAM-mutex on the 8 GiB box, and owns its own idle timer (leak-safe if the backend dies). Backlog query uses the real schema (state + payload->>'kind'); a read-only DB role (readonly-role.sql) keeps it off the backend creds. Bump 0.80.0 -> 0.81.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
32
vision-manager/readonly-role.sql
Normal file
32
vision-manager/readonly-role.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
-- Read-only DB role for vision-manager.
|
||||
--
|
||||
-- The sidecar only needs to count pending analysis work; give it SELECT on
|
||||
-- crawler_jobs and nothing else. It must NOT reuse the backend's credentials.
|
||||
--
|
||||
-- The Postgres service mounts no init dir and the data volume already exists,
|
||||
-- so this is applied ONCE by an operator (it is idempotent):
|
||||
--
|
||||
-- docker compose exec -T postgres \
|
||||
-- psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -v pw="<a-strong-password>" \
|
||||
-- -f - < vision-manager/readonly-role.sql
|
||||
--
|
||||
-- Then point the manager at it (VISION_MANAGER_DATABASE_URL in .env):
|
||||
-- postgres://vision_manager:<a-strong-password>@postgres:5432/<POSTGRES_DB>
|
||||
--
|
||||
-- The password is passed via psql's -v pw=... ; psql substitutes :'pw' as a
|
||||
-- quoted literal and :"DBNAME" (psql's built-in) as the current db identifier.
|
||||
-- These substitutions only happen in plain statements, NOT inside a
|
||||
-- dollar-quoted DO block — hence the \gexec form below.
|
||||
|
||||
-- Create the LOGIN role only if it is absent (idempotent).
|
||||
SELECT 'CREATE ROLE vision_manager LOGIN'
|
||||
WHERE NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'vision_manager')
|
||||
\gexec
|
||||
|
||||
-- (Re)set the password every run so rotating it is just a re-apply.
|
||||
ALTER ROLE vision_manager LOGIN PASSWORD :'pw';
|
||||
|
||||
-- CONNECT to the current database, and read-only on the one table we poll.
|
||||
GRANT CONNECT ON DATABASE :"DBNAME" TO vision_manager;
|
||||
GRANT USAGE ON SCHEMA public TO vision_manager;
|
||||
GRANT SELECT ON crawler_jobs TO vision_manager;
|
||||
Reference in New Issue
Block a user