MemBrain uses four independent types of SQLAlchemy Engines to manage PostgreSQL connections without resource contention. Each engine type is specialized for a specific lifecycle and responsibility:
- Main Engine (
SessionLocal): Used for general administrative tasks, CLI operations, and frontend GET routes. It manages the public schema and handles schema switching via SET LOCAL search_path. - Per-Run Engine (
create_run_engine()): Created on-demand for each experimental run worker thread. It is strictly bound to a specific task schema (task_{pk}__{run_tag}) and is designed for single-writer scenarios. - Search Engine: Used for high-concurrency read operations. It has two implementations:
- Local Path:
LocalSearchRunner used by QA evaluation worker threads. - HTTP Path:
SearchServiceManager used by the Frontend and API search endpoints.
- Conversation Ingestion Engine (
TaskServiceManager): Used for writing and digesting chat sessions in HTTP mode. It follows a similar structure to the Per-Run engine but is optimized for ingestion workflows.
Critical Note on Schema Isolation: Tables like entities, facts, and entity_tree_nodes (the per_task tables) do not exist in the public schema. They are only created and accessible within the specific task_{pk}__{run_tag} schema. This prevents accidental data leakage or silent fallbacks to the public schema.
┌──────────────────────────────────────────────────────────────────┐
│ PostgreSQL max_connections = 150 │
│ │
│ Main Engine (SessionLocal) pool_size=5 overflow=10 │
│ ├── Frontend Viewer GET routes (SET LOCAL search_path switches schema) │
│ ├── CLI ls / delete / dataset │
│ └── evaluation/memory.py helper queries (session count, PKs, etc.) │
│ │
│ Per-Run Engine (LocalRunner) pool_size=1 overflow=0 │
│ └── Created on-demand, one for each exp run worker thread │
│ schema: task_{pk}__{run_tag} │
│ │
│ Search Engine pool_size=20 overflow=10 │
│ ├── [Local] LocalSearchRunner ← Shared by QA eval worker threads │
│ └── [HTTP] SearchServiceManager ← Frontend search + API search │
│ Both use SET LOCAL search_path to switch schema, read-only │
│ │
│ Conversation Ingestion Engine (TaskServiceManager) │
│ └── Same structure as Per-Run Engine, used for write in HTTP session mode │
│ │
│ ⚠️ per_task tables do **NOT exist** in the public schema │
│ (entities, facts, entity_tree_nodes, etc.) │
│ They are only created and queried within the task_{pk}__{run_tag} schema │
└──────────────────────────────────────────────────────────────────┘