brainy/docs/distributed-usage-guide.md

703 lines
16 KiB
Markdown
Raw Normal View History

feat(distributed): add distributed mode with multi-instance coordination Implements Phase 1 and Phase 2 of distributed enhancements for horizontal scaling: Phase 1 - Zero-Config Distributed Mode: - Add DistributedConfigManager for shared S3 configuration coordination - Implement explicit role configuration (reader/writer/hybrid) for safety - Add instance registration with heartbeat and health monitoring - Create hash-based partitioner for deterministic data distribution Phase 2 - Intelligent Data Management: - Add DomainDetector for automatic data categorization (medical, legal, product, etc.) - Implement domain-aware search filtering for improved relevance - Create role-based operational modes with specific optimizations - Add HealthMonitor for comprehensive metrics tracking Key Features: - Multi-writer support with consistent hash partitioning - Reader instances optimize for 80% cache utilization - Writer instances optimize for batched writes - Automatic domain detection and tagging - Real-time health monitoring across all instances - Cross-platform crypto utilities for browser compatibility Safety Improvements: - Require explicit role configuration (no automatic assignment) - Validate role compatibility on startup - Track instance health and performance metrics Testing: - Add comprehensive test suite for distributed features - All 25 distributed tests passing - Fixed domain filtering in search functionality Documentation: - Update README with distributed mode highlights - Add examples showing reader/writer setup - Document new capabilities and benefits 🤖 Generated with Claude Code https://claude.ai/code Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-04 12:18:58 -07:00
# Brainy Distributed Mode - Complete Usage Guide
## Table of Contents
1. [Overview](#overview)
2. [Quick Start](#quick-start)
3. [Configuration](#configuration)
4. [Deployment Patterns](#deployment-patterns)
5. [Domain Management](#domain-management)
6. [Health Monitoring](#health-monitoring)
7. [Performance Optimization](#performance-optimization)
8. [Troubleshooting](#troubleshooting)
9. [Migration Guide](#migration-guide)
## Overview
Brainy's distributed mode enables you to scale your vector database across multiple instances, each optimized for specific workloads. This guide covers everything you need to know to deploy and manage Brainy at scale.
### Key Benefits
- **Horizontal Scaling**: Add readers for query performance, writers for ingestion throughput
- **Zero Coordination Overhead**: Simple shared JSON config in S3
- **Automatic Optimization**: Each role self-optimizes for its workload
- **Multi-Domain Support**: Handle different data types without conflicts
## Quick Start
### 1. Basic Setup
Distributed mode requires explicit role configuration for safety:
```javascript
import { createAutoBrainy } from 'brainy'
// Option 1: Environment variable (recommended for production)
process.env.BRAINY_ROLE = 'writer' // or 'reader' or 'hybrid'
const brainy = createAutoBrainy({
storage: {
type: 's3',
bucket: 'my-bucket',
region: 'us-east-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
},
distributed: true
})
// Option 2: Explicit configuration
const brainy = createAutoBrainy({
storage: { /* s3 config */ },
distributed: {
role: 'writer' // Must specify role
}
})
// Option 3: Inferred from read/write mode
const brainy = createAutoBrainy({
storage: { /* s3 config */ },
writeOnly: true, // Role inferred as 'writer'
distributed: true
})
await brainy.init()
```
### 2. Understanding Roles
Roles must be explicitly configured for each instance:
| Role | Purpose | Optimizations | When to Use |
|------|---------|---------------|-------------|
| **Writer** | Data ingestion | Write batching, minimal cache | ETL pipelines, data import |
| **Reader** | Query serving | Aggressive caching, prefetching | API servers, search services |
| **Hybrid** | Both operations | Adaptive caching | Small deployments, development |
### 3. Role Configuration Methods
```javascript
// Priority order for role determination:
// 1. BRAINY_ROLE environment variable (highest priority)
// 2. Explicit role in distributed config
// 3. Inferred from readOnly/writeOnly mode
// 4. ERROR - role must be explicitly set
// Examples:
// Environment variable (production recommended)
BRAINY_ROLE=writer node app.js
// Explicit in code
distributed: { role: 'reader' }
// Inferred from mode
writeOnly: true // becomes 'writer'
readOnly: true // becomes 'reader'
```
## Configuration
### Basic Configuration
```javascript
const brainy = createAutoBrainy({
storage: { /* S3 config */ },
distributed: {
enabled: true, // Enable distributed mode
role: 'reader', // Optional: explicit role
instanceId: 'reader-01', // Optional: custom ID
configPath: '_brainy/config.json', // Config location
heartbeatInterval: 30000, // Health check interval
instanceTimeout: 60000 // Dead instance timeout
}
})
```
### Environment Variables
```bash
# Set role via environment
export BRAINY_ROLE=writer
# Custom instance ID
export BRAINY_INSTANCE_ID=prod-writer-01
# Service endpoint for health checks
export SERVICE_ENDPOINT=http://writer-01:3000
```
### Shared Configuration Structure
The shared config file (`_brainy/config.json`) in S3:
```json
{
"version": 1,
"updated": "2024-01-15T10:30:00Z",
"settings": {
"partitionStrategy": "hash",
"partitionCount": 100,
"embeddingModel": "text-embedding-ada-002",
"dimensions": 1536,
"distanceMetric": "cosine"
},
"instances": {
"writer-01": {
"role": "writer",
"status": "active",
"lastHeartbeat": "2024-01-15T10:29:50Z",
"metrics": {
"vectorCount": 1000000,
"cacheHitRate": 0.2,
"memoryUsage": 512000000
}
},
"reader-01": {
"role": "reader",
"status": "active",
"lastHeartbeat": "2024-01-15T10:29:55Z",
"metrics": {
"vectorCount": 1000000,
"cacheHitRate": 0.95,
"memoryUsage": 1024000000
}
}
}
}
```
## Deployment Patterns
### Pattern 1: Simple Read/Write Split
Best for: Most applications with clear ingestion vs query workloads
```yaml
# docker-compose.yml
version: '3.8'
services:
writer:
image: myapp:latest
environment:
BRAINY_ROLE: writer
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
deploy:
replicas: 1 # Usually one writer
reader:
image: myapp:latest
environment:
BRAINY_ROLE: reader
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
deploy:
replicas: 5 # Scale readers as needed
```
### Pattern 2: Multi-Domain Ingestion
Best for: Multiple data sources with different schemas
```javascript
// Medical data writer
const medicalWriter = createAutoBrainy({
storage: s3Config,
distributed: {
role: 'writer',
instanceId: 'medical-writer'
}
})
// Legal data writer
const legalWriter = createAutoBrainy({
storage: s3Config,
distributed: {
role: 'writer',
instanceId: 'legal-writer'
}
})
// Unified reader for all domains
const reader = createAutoBrainy({
storage: s3Config,
distributed: { role: 'reader' }
})
```
### Pattern 3: Lambda + ECS Hybrid
Best for: Serverless search with persistent ingestion
```javascript
// Lambda function (configure as reader)
export const handler = async (event) => {
const brainy = createAutoBrainy({
storage: s3Config,
distributed: { role: 'reader' } // Explicit role required
// OR use readOnly mode:
// readOnly: true,
// distributed: true
})
const results = await brainy.search(event.query, 10)
return { statusCode: 200, body: JSON.stringify(results) }
}
// ECS task (writer)
const writer = createAutoBrainy({
storage: s3Config,
distributed: { role: 'writer' }
// OR use writeOnly mode:
// writeOnly: true,
// distributed: true
})
```
### Pattern 4: Kubernetes StatefulSet + Deployment
```yaml
# Writer StatefulSet (persistent identity)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: brainy-writer
spec:
replicas: 1
template:
spec:
containers:
- name: writer
env:
- name: BRAINY_ROLE
value: writer
- name: BRAINY_INSTANCE_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
---
# Reader Deployment (stateless, scalable)
apiVersion: apps/v1
kind: Deployment
metadata:
name: brainy-readers
spec:
replicas: 10
template:
spec:
containers:
- name: reader
env:
- name: BRAINY_ROLE
value: reader
```
## Domain Management
### Automatic Domain Detection
Brainy automatically detects data domains based on content:
```javascript
// These are automatically tagged with appropriate domains
await brainy.add({
symptoms: "headache",
diagnosis: "migraine"
}) // Tagged as 'medical'
await brainy.add({
contract: "lease agreement",
parties: ["John", "Jane"]
}) // Tagged as 'legal'
await brainy.add({
price: 99.99,
sku: "PROD-123"
}) // Tagged as 'product'
```
### Custom Domain Patterns
```javascript
import { DomainDetector } from 'brainy/distributed'
const detector = new DomainDetector()
// Add custom domain pattern
detector.addCustomPattern({
domain: 'automotive',
patterns: {
fields: ['make', 'model', 'year', 'vin'],
keywords: ['car', 'vehicle', 'automobile', 'engine'],
regex: /\b[A-Z0-9]{17}\b/ // VIN pattern
},
priority: 1
})
```
### Searching by Domain
```javascript
// Search all domains
const allResults = await brainy.search("treatment options", 10)
// Search specific domain
const medicalOnly = await brainy.search("treatment options", 10, {
filter: { domain: 'medical' }
})
// Multiple domain search
const results = await Promise.all([
brainy.search(query, 5, { filter: { domain: 'medical' } }),
brainy.search(query, 5, { filter: { domain: 'legal' } })
])
```
## Health Monitoring
### Getting Health Status
```javascript
const health = brainy.getHealthStatus()
console.log(health)
// {
// status: 'healthy',
// instanceId: 'reader-01',
// role: 'reader',
// uptime: 3600,
// metrics: {
// vectorCount: 1000000,
// cacheHitRate: 0.95,
// memoryUsageMB: 1024,
// cpuUsagePercent: 45,
// requestsPerSecond: 150,
// averageLatencyMs: 25,
// errorRate: 0.001
// },
// warnings: ['High memory usage detected']
// }
```
### Health Check Endpoint
```javascript
// Express.js health endpoint
app.get('/health', (req, res) => {
const health = brainy.getHealthStatus()
// Return appropriate HTTP status
const httpStatus = health.status === 'healthy' ? 200 :
health.status === 'degraded' ? 503 : 500
res.status(httpStatus).json(health)
})
```
### Monitoring Dashboard
```javascript
// Collect metrics for monitoring
setInterval(async () => {
const health = brainy.getHealthStatus()
// Send to monitoring service
await prometheus.gauge('brainy_vector_count', health.metrics.vectorCount)
await prometheus.gauge('brainy_cache_hit_rate', health.metrics.cacheHitRate)
await prometheus.gauge('brainy_memory_usage', health.metrics.memoryUsageMB)
await prometheus.gauge('brainy_rps', health.metrics.requestsPerSecond)
}, 30000)
```
## Performance Optimization
### Reader Optimization
```javascript
// Readers benefit from aggressive caching
const reader = createAutoBrainy({
storage: s3Config,
distributed: { role: 'reader' },
cache: {
hotCacheMaxSize: 50000, // Large cache for frequent items
hotCacheEvictionThreshold: 0.9, // Keep cache full
warmCacheTTL: 3600000, // 1 hour TTL
readOnlyMode: {
prefetchStrategy: 'aggressive' // Prefetch related vectors
}
}
})
```
### Writer Optimization
```javascript
// Writers benefit from batching
const writer = createAutoBrainy({
storage: s3Config,
distributed: { role: 'writer' },
cache: {
hotCacheMaxSize: 5000, // Small cache
batchSize: 1000, // Large batch writes
autoTune: false // Consistent write performance
}
})
// Batch insertions for better performance
const batch = []
for (const item of items) {
batch.push(writer.add(item, metadata))
if (batch.length >= 100) {
await Promise.all(batch)
batch.length = 0
}
}
```
### Network Optimization
```javascript
// Use connection pooling for S3
const s3Config = {
type: 's3',
bucket: 'my-bucket',
maxRetries: 3,
httpOptions: {
agent: new https.Agent({
keepAlive: true,
maxSockets: 50
})
}
}
```
## Troubleshooting
### Common Issues
#### 1. Role Conflicts
**Problem**: Multiple instances trying to be writers
**Solution**: Explicitly set roles
```javascript
// Use environment variables
BRAINY_ROLE=writer node writer.js
BRAINY_ROLE=reader node reader.js
```
#### 2. Stale Instances
**Problem**: Dead instances not being cleaned up
**Solution**: Check heartbeat settings
```javascript
const brainy = createAutoBrainy({
distributed: {
heartbeatInterval: 15000, // More frequent heartbeats
instanceTimeout: 45000 // Shorter timeout
}
})
```
#### 3. Configuration Conflicts
**Problem**: Instances have incompatible settings
**Solution**: Check the shared config
```bash
# Download and inspect config
aws s3 cp s3://my-bucket/_brainy/config.json ./config.json
cat config.json
# Fix and upload if needed
aws s3 cp ./config.json s3://my-bucket/_brainy/config.json
```
#### 4. Performance Issues
**Problem**: Slow searches in distributed mode
**Solution**: Check role distribution
```javascript
// Ensure you have enough readers
const config = await brainy.getDistributedConfig()
const readers = Object.values(config.instances)
.filter(i => i.role === 'reader' && i.status === 'active')
console.log(`Active readers: ${readers.length}`)
```
### Debug Mode
```javascript
// Enable verbose logging
const brainy = createAutoBrainy({
storage: s3Config,
distributed: true,
logging: { verbose: true }
})
// Logs will show:
// - Role detection process
// - Configuration updates
// - Partition assignments
// - Domain detection
// - Health check results
```
## Migration Guide
### From Single Instance to Distributed
#### Step 1: Prepare S3 Storage
```javascript
// Before: Local or single S3 instance
const brainy = createAutoBrainy({
storage: { type: 'opfs' } // or single S3
})
// After: S3 with distributed config
const brainy = createAutoBrainy({
storage: {
type: 's3',
bucket: 'my-bucket',
region: 'us-east-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
},
distributed: true
})
```
#### Step 2: Migrate Data
```javascript
// Export from old instance
const allData = await oldBrainy.exportAll()
// Import to new distributed instance
const writer = createAutoBrainy({
storage: s3Config,
distributed: { role: 'writer' }
})
for (const item of allData) {
await writer.add(item.vector, item.metadata, { id: item.id })
}
```
#### Step 3: Update Application Code
```javascript
// Add distributed initialization
const brainy = createAutoBrainy({
storage: s3Config,
distributed: true
})
// Add cleanup on shutdown
process.on('SIGTERM', async () => {
await brainy.cleanup()
process.exit(0)
})
```
#### Step 4: Deploy Readers
```yaml
# Scale out readers gradually
kubectl scale deployment brainy-readers --replicas=2
# Monitor performance
kubectl scale deployment brainy-readers --replicas=5
# Continue scaling as needed
kubectl scale deployment brainy-readers --replicas=10
```
### Best Practices
1. **Start Small**: Begin with 1 writer and 2-3 readers
2. **Monitor Metrics**: Watch cache hit rates and latency
3. **Scale Gradually**: Add instances based on actual load
4. **Use Health Checks**: Integrate with load balancers
5. **Clean Shutdown**: Always call `cleanup()` on shutdown
6. **Regular Backups**: Backup S3 bucket regularly
## Advanced Topics
### Custom Partitioning
```javascript
// Override default hash partitioning
class CustomPartitioner extends HashPartitioner {
getPartition(vectorId) {
// Custom logic for partition assignment
if (vectorId.startsWith('priority-')) {
return 'vectors/p000' // Hot partition
}
return super.getPartition(vectorId)
}
}
```
### Multi-Region Deployment
```javascript
// Region-specific readers
const usReader = createAutoBrainy({
storage: {
type: 's3',
bucket: 'my-bucket',
region: 'us-east-1'
},
distributed: {
role: 'reader',
instanceId: 'us-reader-01'
}
})
const euReader = createAutoBrainy({
storage: {
type: 's3',
bucket: 'my-bucket-eu',
region: 'eu-west-1'
},
distributed: {
role: 'reader',
instanceId: 'eu-reader-01'
}
})
```
### Hybrid Cloud Deployment
```javascript
// On-premise writer
const onPremWriter = createAutoBrainy({
storage: {
type: 'customS3',
endpoint: 'https://minio.internal:9000',
bucket: 'brainy-data'
},
distributed: { role: 'writer' }
})
// Cloud readers
const cloudReader = createAutoBrainy({
storage: {
type: 's3',
bucket: 'brainy-data-replicated'
},
distributed: { role: 'reader' }
})
```
## Conclusion
Brainy's distributed mode provides a simple yet powerful way to scale your vector database. With automatic role detection, zero-coordination overhead, and built-in optimizations, you can focus on building your application while Brainy handles the complexity of distributed systems.
For more information, see:
- [Architecture Documentation](./distributed-deployment-scenario.md)
- [Implementation Details](./brainy-distributed-enhancements-revised.md)
- [API Reference](../README.md#api-reference)