Repository navigation
v2.0.0-beta.1 — multi-core
Pre-release
Pre-release
Multi-core (roadmap Phase 3). Brainstem runs as several processes on one Postgres database: input polling, decisions and deliveries are shared across cores, a lost core loses nothing, and config is versioned data that every core hot-reloads. Single-node mode (--role all, SQLite) works as before. See Cluster mode.
Upgrading (breaking):
- The API no longer writes or executes the config file.
PUT /api/configreturns 409 withconfigSource: file(the default), and the Web UI shows the file read-only. To edit config in the UI, runbrainstem config importand setBRAINSTEM_CONFIG_SOURCE=database. Mount the config file read-only. - Inputs are scheduled by leases in the database when the pipeline uses the database transport (the default). The schedule survives restarts, so a restart no longer polls every input immediately; it polls when the input is next due. Set
runtime.inputs.scheduling: "timer"for the 1.x behavior. --role coreand--role apinever migrate. Runbrainstem migrateonce per upgrade, or setBRAINSTEM_MIGRATE=true.--role all(the default) andbrainstem-api.mjsstill migrate on start.GET /api/pipelinereportstransport: "postgres"on Postgres.- Migration
0005_clusteraddsinputs,config_versions,active_configandcores.config_version.
Changes:
- Roles and queues:
brainstem --role all|core|api|weband--queues poll,decide,deliver(BRAINSTEM_ROLE,BRAINSTEM_QUEUES). Onlydecidecores load the Laya model. Cores register their role, queues and config version. - Postgres cluster driver:
LISTEN/NOTIFYwakeups for jobs, inputs, config and telemetry (src/db/notifier.mjs); claims keepFOR UPDATE SKIP LOCKED.runtime.queue.transport: "postgres"requires Postgres. - Lease-based input scheduling with fencing epochs: each input is polled by one core at a time, and its checkpoint commit is refused once another core holds the lease. Leases of stopped or lost cores are taken over at once. Inputs get
ctx.lease.GET /api/inputsshows the schedule and lease owners. - Advisory-lock singletons for delivery-log pruning and API maintenance (
src/db/locks.mjs). Newinputs.prunemaintenance task. - Laya worker pool:
runtime.laya.workers/BRAINSTEM_LAYA_WORKERS/--decide-concurrencyevaluates observations in parallel in worker threads, off the event loop.BRAINSTEM_LAYA_MODEL_DIRuses a mounted model. The model is published as thebrainstem-modelsOCI image (Dockerfile.models, CI). - Config as versioned data:
config_versionsvalidated against a JSON Schema (GET /api/config/schema); saves guarded byIf-Match; rollback by activating an older version;brainstem config import|export|list|show|activate|validate. Cores reload the active version without a restart and keep the previous one when a new one fails to load. - SSE across API replicas: telemetry is announced by id and streamed by every replica. Events carry ids, and reconnecting clients resume from
Last-Event-IDon any replica. - Graceful drain on SIGTERM: readiness fails, claiming stops, in-flight polls and jobs finish (
runtime.shutdown.timeoutMs,--drain-timeout), and leases are released. The API ends SSE streams and closes. - Probes on every role:
/livez,/readyzand/startupz(core:--health-port, default 8081; API: its own port). - Web UI: versioned config editor with history and rollback; the Cores panel shows each core's config version.
- Topology merges cores that run different queues and is consistent when running cores share a config version.
- Failed input polls emit
brainstem.input.poll.failedand count as input node errors. - Jobs held by a core that stopped or missed 3 heartbeats are reclaimed at once, like its input leases, instead of after the job lease expires.
- The multi-core acceptance tests pass (no longer
todo) on SQLite and Postgres, including killing a core mid-run.