# Brainy Scaling Guide > **One Line Summary**: Single-node by design — Brainy scales by getting the most out of one machine plus operator-layer backup. ## Table of Contents - [Quick Start](#quick-start) - [How Brainy Scales](#how-brainy-scales) - [Storage Configurations](#storage-configurations) - [Scaling Patterns](#scaling-patterns) - [Real World Examples](#real-world-examples) ## Quick Start ### In-Memory ```typescript import Brainy from '@soulcraft/brainy' const brain = new Brainy({ storage: { type: 'memory' } }) ``` ### On-Disk (Default for Node) ```typescript const brain = new Brainy({ storage: { type: 'filesystem', path: './brainy-data' } }) ``` ## How Brainy Scales Brainy 8.0 is a **single-node library**. There is no cluster, no peer discovery, no S3 coordination. Scaling means: - **Up**: give the process more RAM, CPU, and IOPS - **Out**: stand up multiple independent Brainy instances behind your own service layer - **Cold storage**: snapshot the on-disk artifact off-site so you can rehydrate elsewhere The three knobs that matter most: 1. **`config.vector.recall`** — `'fast'`, `'balanced'`, or `'accurate'` (default `'balanced'`) 2. **`config.vector.persistMode`** — `'immediate'` for durability, `'deferred'` for throughput The native vector provider (via the optional `@soulcraft/cor` package) extends this with a higher-performing index — and its own at-scale acceleration such as on-disk compressed indexing — when installed. ## Measured Performance Numbers below are **measured** by `tests/benchmarks/find-composition-scale.js` (a single Node 22 process, in-memory storage, 384-dim vectors, `balanced` recall). They are the open-core (pure-TypeScript) path — what you get from `@soulcraft/brainy` with no native provider installed. Run it yourself: `node --max-old-space-size=8192 tests/benchmarks/find-composition-scale.js 100000`. `find()` query latency, p50 / p95 (200 queries each): | Query | 5,000 entities | 100,000 entities | |---|---|---| | Vector similarity (`{ vector }`) | 0.8 / 1.3 ms | 1.4 / 4.7 ms | | Graph 1-hop (`{ connected }`) | 0.5 / 0.7 ms | 0.7 / 0.8 ms | | Metadata filter (`{ where }`, low-selectivity) | 0.7 / 1.2 ms | 23.5 / 30.1 ms | | Vector + metadata | 7.7 / 8.3 ms | 78.8 / 93.8 ms | What the shape tells you: - **Vector and graph lookups scale ~logarithmically** — they barely move from 5k to 100k, because HNSW search is ~O(ef·log n) and graph adjacency is O(degree). - **Metadata-filtered paths scale with the size of the match set, not the database.** The benchmark's `category` filter matches ~10% of rows (10,000 at 100k); the cost is materializing that candidate set and running the vector search *inside* it (`find()` does metadata-first hard filtering, then ranks within the candidates — see [How find works](./FIND_SYSTEM.md)). A **high-selectivity** filter (few matches) is far cheaper; a 10%-of-everything filter is the worst case. This candidate-restricted search is precisely the path the native provider accelerates (Rust roaring-bitmap candidate intersection). - **Composition is correct, not lossy.** Combining vector + metadata + graph returns exactly the entities satisfying all constraints — verified by `tests/integration/find-triple-composition.test.ts`. Memory: ~62 KB resident per entity at 100k (6.2 GB RSS for 100k × 384-dim including the HNSW graph, metadata index, and 100k edges). **Scale ceiling (open-core).** The pure-JS HNSW *build* cost (~100 inserts/s at 384-dim on one core) makes the in-process open-core path most appropriate up to ~10⁵–10⁶ entities. *Query* latency stays low well beyond that, but for the 10⁸–10¹⁰ regime install the native provider (`@soulcraft/cor`, on-disk DiskANN) — same API, no code change. _Projected from the two measured points, vector p50 at 1M is ~2 ms; metadata-heavy composition grows with match-set size and is the path to move onto the native provider first._ ## Storage Configurations ### Filesystem (Recommended for Production) ```typescript const brain = new Brainy({ storage: { type: 'filesystem', path: '/var/lib/brainy' } }) ``` - Stores everything in a sharded JSON tree under `path` - Atomic writes via rename - Survives process restarts - Snapshot it off-site with `gsutil rsync`, `aws s3 sync`, `rclone`, or `tar` from your scheduler ### Memory ```typescript const brain = new Brainy({ storage: { type: 'memory' } }) ``` - Zero I/O, fastest possible - No persistence — process exit discards everything - Use for tests and ephemeral caches ### Auto ```typescript const brain = new Brainy({ storage: { type: 'auto', path: './data' } }) ``` - Picks `filesystem` when running on Node with a writable `path` - Falls back to `memory` otherwise ## Scaling Patterns ### Stage 1: Prototype (Memory) ```typescript const brain = new Brainy({ storage: { type: 'memory' } }) // Development, tests, <100K items ``` ### Stage 2: Production (Filesystem) ```typescript const brain = new Brainy({ storage: { type: 'filesystem', path: '/var/lib/brainy' } }) // Most production workloads up to ~10M entities on a single host ``` ### Stage 3: Higher Throughput (Tune the Vector Index) ```typescript const brain = new Brainy({ storage: { type: 'filesystem', path: '/var/lib/brainy' }, vector: { recall: 'fast', // Trade recall for latency persistMode: 'deferred' // Batch persistence } }) ``` ### Stage 4: Multi-Instance (Operator-Layer) Run multiple Brainy processes behind your own routing/service layer. Each process owns its own `path`. Sync each artifact off-site independently. Brainy itself does not coordinate between processes. ## Real World Examples ### Example 1: Single-Node App With Backup ```typescript const brain = new Brainy({ storage: { type: 'filesystem', path: '/var/lib/brainy' } }) ``` Schedule (cron / systemd timer): ```bash */15 * * * * rclone sync /var/lib/brainy remote:brainy-backup ``` ### Example 2: Tests ```typescript const brain = new Brainy({ storage: { type: 'memory' } }) // Fast, no cleanup needed between runs ``` ### Example 3: Multi-Tenant Service Spin up one Brainy instance per tenant, each in its own directory: ```typescript function brainForTenant(tenantId: string) { return new Brainy({ storage: { type: 'filesystem', path: `/var/lib/brainy/${tenantId}` } }) } ``` Your service layer handles routing and isolation; Brainy stays simple. ### Example 4: Higher Recall at Scale ```typescript const brain = new Brainy({ storage: { type: 'filesystem', path: '/var/lib/brainy' }, vector: { recall: 'accurate' } }) ``` ## Tuning Knobs Summary | Setting | Values | When to change | |---------|--------|----------------| | `vector.recall` | `'fast'` / `'balanced'` / `'accurate'` | Trade recall for latency | | `vector.persistMode` | `'immediate'` / `'deferred'` | Throughput vs. durability | | `storage.cache.maxSize` | integer | Hot-path read cache size | | `storage.cache.ttl` | ms | Cache freshness | ## Monitoring & Observability ```typescript const stats = await brain.stats() // { // nounCount: 50000, // verbCount: 80000, // vectorIndex: { ... }, // storage: { used: '45GB' } // } ``` ## Troubleshooting ### Issue: Slow queries 1. Switch to `vector.recall: 'fast'` 2. Increase the read cache (`storage.cache.maxSize`) 3. Consider the optional native vector provider via `@soulcraft/cor` ### Issue: Memory pressure 1. Reduce `storage.cache.maxSize` 2. Move to `vector.persistMode: 'deferred'` to batch writes 3. Consider the optional native vector provider via `@soulcraft/cor` for at-scale index acceleration ### Issue: Slow startup after a crash 1. Use `vector.persistMode: 'immediate'` so the index file stays in sync with storage 2. Verify backup integrity periodically ## Best Practices 1. **One process = one `path`** — never share a directory between processes 2. **Snapshot from your scheduler** — Brainy doesn't ship cloud SDKs; use `rclone` / `aws s3 sync` / `gsutil` 3. **Profile before tuning** — `recall: 'balanced'` is right for most workloads 4. **Install the native vector provider only when measured profiling shows it pays off** ## Summary - Brainy 8.0 is a **library**, not a cluster - Storage adapters: `filesystem`, `memory`, `auto` - Vector tuning: `recall`, `persistMode` - Backup is an operator-layer concern — snapshot `path`