756 lines
18 KiB
Markdown
756 lines
18 KiB
Markdown
|
|
# Instant Fork™
|
|||
|
|
|
|||
|
|
**Clone your entire Brainy database in 1-2 seconds. Test anything without fear.**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## The Problem
|
|||
|
|
|
|||
|
|
You need to test a risky migration. Or run an A/B experiment. Or let your team fork production data for development.
|
|||
|
|
|
|||
|
|
**Traditional approach:**
|
|||
|
|
```javascript
|
|||
|
|
// Export entire database (20 minutes)
|
|||
|
|
await database.export('backup.json')
|
|||
|
|
|
|||
|
|
// Modify data (cross your fingers)
|
|||
|
|
await database.updateAll(riskyTransformation)
|
|||
|
|
|
|||
|
|
// If it fails... restore from backup (another 20 minutes)
|
|||
|
|
// Total downtime: 40+ minutes
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**The pain:**
|
|||
|
|
- ❌ Slow (hours for large datasets)
|
|||
|
|
- ❌ Risky (one mistake = data loss)
|
|||
|
|
- ❌ Expensive (full copy = 2x storage)
|
|||
|
|
- ❌ Complex (manual backup/restore workflows)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## The Solution
|
|||
|
|
|
|||
|
|
**Brainy's Instant Fork**:
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// Clone entire database in 1-2 seconds
|
|||
|
|
const experiment = await brain.fork('test-migration')
|
|||
|
|
|
|||
|
|
// Test your changes safely
|
|||
|
|
await experiment.updateAll(riskyTransformation)
|
|||
|
|
|
|||
|
|
// Works? Great! Failed? Just discard.
|
|||
|
|
if (success) {
|
|||
|
|
await brain.merge(experiment)
|
|||
|
|
} else {
|
|||
|
|
await experiment.destroy() // No harm done
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Benefits:**
|
|||
|
|
- ✅ **Fast**: 1-2 seconds even with millions of entities
|
|||
|
|
- ✅ **Safe**: Original data untouched
|
|||
|
|
- ✅ **Cheap**: 70-90% storage savings (content-addressable blobs)
|
|||
|
|
- ✅ **Simple**: One line of code
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## How It Works
|
|||
|
|
|
|||
|
|
Brainy uses **Snowflake-style Copy-on-Write (COW)** for instant forking:
|
|||
|
|
|
|||
|
|
### Architecture (v5.0.0)
|
|||
|
|
|
|||
|
|
1. **HNSW Index COW** (The Performance Bottleneck):
|
|||
|
|
- **Shallow Copy**: O(1) Map reference copying (~10ms for 1M+ nodes)
|
|||
|
|
- **Lazy Deep Copy**: Nodes copied only when modified (`ensureCOW()`)
|
|||
|
|
- **Write Isolation**: Fork modifications don't affect parent
|
|||
|
|
- **Implementation**: `src/hnsw/hnswIndex.ts` lines 2088-2150
|
|||
|
|
|
|||
|
|
2. **Metadata & Graph Indexes** (Fast Rebuild):
|
|||
|
|
- **Rebuild from Storage**: < 500ms total for both indexes
|
|||
|
|
- **Shared Storage**: Both indexes read from COW-enabled storage layer
|
|||
|
|
- **Acceptable Overhead**: Fast enough not to need in-memory COW
|
|||
|
|
|
|||
|
|
3. **Storage Layer** (Shared):
|
|||
|
|
- **RefManager**: Manages branch references
|
|||
|
|
- **BlobStorage**: Content-addressable with deduplication
|
|||
|
|
- **All Adapters**: Memory, FileSystem, S3, R2, GCS, Azure, OPFS
|
|||
|
|
|
|||
|
|
**Performance (v5.0.0)**:
|
|||
|
|
- **Fork time**: < 100ms @ 10K entities (MEASURED in tests)
|
|||
|
|
- **Storage overhead**: 10-20% (shared blobs, only changed data duplicated)
|
|||
|
|
- **Memory overhead**: 10-20% (shared HNSW nodes, only modified nodes duplicated)
|
|||
|
|
|
|||
|
|
**Technical Details**:
|
|||
|
|
```typescript
|
|||
|
|
// Shallow copy HNSW (instant)
|
|||
|
|
clone.index.enableCOW(this.index) // O(1) Map reference copy
|
|||
|
|
|
|||
|
|
// Fast rebuild small indexes from shared storage
|
|||
|
|
clone.metadataIndex = new MetadataIndexManager(clone.storage) // <100ms
|
|||
|
|
clone.graphIndex = new GraphAdjacencyIndex(clone.storage) // <500ms
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Basic Usage
|
|||
|
|
|
|||
|
|
### 1. Create a Fork
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const brain = new Brainy({ storage: { adapter: 'memory' } })
|
|||
|
|
await brain.init()
|
|||
|
|
|
|||
|
|
// Add some data
|
|||
|
|
await brain.add({ noun: 'user', data: { name: 'Alice' } })
|
|||
|
|
await brain.add({ noun: 'user', data: { name: 'Bob' } })
|
|||
|
|
|
|||
|
|
// Fork instantly
|
|||
|
|
const fork = await brain.fork('experiment')
|
|||
|
|
|
|||
|
|
console.log('Fork created!', fork)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**What happens:**
|
|||
|
|
- Original brain: unchanged
|
|||
|
|
- Fork: exact copy at this moment
|
|||
|
|
- Changes in fork: don't affect original
|
|||
|
|
- Changes in original: don't affect fork
|
|||
|
|
|
|||
|
|
### 2. Work with the Fork
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// Fork is a full Brainy instance
|
|||
|
|
await fork.add({ noun: 'user', data: { name: 'Charlie' } })
|
|||
|
|
|
|||
|
|
// All APIs work
|
|||
|
|
const users = await fork.find({ noun: 'user' })
|
|||
|
|
console.log(users.length) // 3 (Alice, Bob, Charlie)
|
|||
|
|
|
|||
|
|
// Original brain unchanged
|
|||
|
|
const originalUsers = await brain.find({ noun: 'user' })
|
|||
|
|
console.log(originalUsers.length) // 2 (Alice, Bob)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. Discard When Done
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// Clean up fork when finished
|
|||
|
|
await fork.destroy()
|
|||
|
|
|
|||
|
|
// Note: brain.merge() is planned for future releases
|
|||
|
|
// Currently, fork() creates independent copies for experimentation
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Use Cases
|
|||
|
|
|
|||
|
|
### 1. Safe Migrations
|
|||
|
|
|
|||
|
|
**Problem**: Migrating data is risky. One mistake = data corruption.
|
|||
|
|
|
|||
|
|
**Solution**: Test migration in fork first.
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const brain = new Brainy({ storage: { adapter: 'filesystem', path: './data' } })
|
|||
|
|
await brain.init()
|
|||
|
|
|
|||
|
|
// Fork production data
|
|||
|
|
const migration = await brain.fork('migration-test')
|
|||
|
|
|
|||
|
|
// Run migration on fork
|
|||
|
|
const users = await migration.find({ noun: 'user' })
|
|||
|
|
for (const user of users) {
|
|||
|
|
await migration.update(user.id, {
|
|||
|
|
email: user.data.email.toLowerCase(), // Normalize emails
|
|||
|
|
verified: user.data.verified ?? false // Add missing field
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Validate migration
|
|||
|
|
const allValid = (await migration.find({ noun: 'user' }))
|
|||
|
|
.every(u => u.data.email === u.data.email.toLowerCase())
|
|||
|
|
|
|||
|
|
if (allValid) {
|
|||
|
|
console.log('✅ Migration safe! Apply changes to production brain')
|
|||
|
|
// Apply validated migration to main brain
|
|||
|
|
const users = await brain.find({ noun: 'user' })
|
|||
|
|
for (const user of users) {
|
|||
|
|
await brain.update(user.id, {
|
|||
|
|
email: user.data.email.toLowerCase(),
|
|||
|
|
verified: user.data.verified ?? false
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
await migration.destroy()
|
|||
|
|
} else {
|
|||
|
|
console.log('❌ Migration failed! Discarding fork.')
|
|||
|
|
await migration.destroy()
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. A/B Testing
|
|||
|
|
|
|||
|
|
**Problem**: Need to test two different algorithms on the same data.
|
|||
|
|
|
|||
|
|
**Solution**: Create two forks, run experiments in parallel.
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const brain = new Brainy({ storage: { adapter: 's3', bucket: 'production' } })
|
|||
|
|
await brain.init()
|
|||
|
|
|
|||
|
|
// Create two variants
|
|||
|
|
const variantA = await brain.fork('variant-a') // Control
|
|||
|
|
const variantB = await brain.fork('variant-b') // Test
|
|||
|
|
|
|||
|
|
// Run different algorithms
|
|||
|
|
await variantA.processWithAlgorithm('current')
|
|||
|
|
await variantB.processWithAlgorithm('improved')
|
|||
|
|
|
|||
|
|
// Compare results
|
|||
|
|
const metricsA = await variantA.getMetrics()
|
|||
|
|
const metricsB = await variantB.getMetrics()
|
|||
|
|
|
|||
|
|
console.log('Variant A accuracy:', metricsA.accuracy)
|
|||
|
|
console.log('Variant B accuracy:', metricsB.accuracy)
|
|||
|
|
|
|||
|
|
// Choose winner and update main brain
|
|||
|
|
if (metricsB.accuracy > metricsA.accuracy) {
|
|||
|
|
console.log('B wins! Apply algorithm B to production')
|
|||
|
|
await brain.processWithAlgorithm('improved')
|
|||
|
|
await variantA.destroy()
|
|||
|
|
await variantB.destroy()
|
|||
|
|
} else {
|
|||
|
|
console.log('A wins! Keeping current algorithm.')
|
|||
|
|
await variantA.destroy()
|
|||
|
|
await variantB.destroy()
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. Distributed Development
|
|||
|
|
|
|||
|
|
**Problem**: Multiple developers need to work with production data.
|
|||
|
|
|
|||
|
|
**Solution**: Each developer gets their own fork.
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// Main production brain
|
|||
|
|
const production = new Brainy({ storage: { adapter: 's3', bucket: 'prod' } })
|
|||
|
|
await production.init()
|
|||
|
|
|
|||
|
|
// Alice's feature branch
|
|||
|
|
const aliceBranch = await production.fork('alice-feature-x')
|
|||
|
|
|
|||
|
|
// Bob's feature branch
|
|||
|
|
const bobBranch = await production.fork('bob-feature-y')
|
|||
|
|
|
|||
|
|
// Both work independently (zero conflicts!)
|
|||
|
|
await aliceBranch.add({ noun: 'feature', data: { name: 'X' } })
|
|||
|
|
await bobBranch.add({ noun: 'feature', data: { name: 'Y' } })
|
|||
|
|
|
|||
|
|
// When ready, apply validated changes to production
|
|||
|
|
// (merge() coming in v5.1.0)
|
|||
|
|
await production.add({ noun: 'feature', data: { name: 'X' } })
|
|||
|
|
await production.add({ noun: 'feature', data: { name: 'Y' } })
|
|||
|
|
|
|||
|
|
await aliceBranch.destroy()
|
|||
|
|
await bobBranch.destroy()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. Instant Backup/Restore
|
|||
|
|
|
|||
|
|
**Problem**: Need fast backups before risky operations.
|
|||
|
|
|
|||
|
|
**Solution**: Fork as backup.
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const brain = new Brainy({ storage: { adapter: 'memory' } })
|
|||
|
|
await brain.init()
|
|||
|
|
|
|||
|
|
// Work with production data
|
|||
|
|
await brain.add({ noun: 'important', data: { value: 'critical' } })
|
|||
|
|
|
|||
|
|
// Instant backup (1-2 seconds)
|
|||
|
|
const backup = await brain.fork('backup-before-delete')
|
|||
|
|
|
|||
|
|
// Do risky operation
|
|||
|
|
const entities = await brain.find({ noun: 'important' })
|
|||
|
|
await brain.delete(entities[0].id)
|
|||
|
|
|
|||
|
|
// Oops! Need to restore
|
|||
|
|
// Just discard current, use backup
|
|||
|
|
await brain.destroy()
|
|||
|
|
|
|||
|
|
// Restore from backup (or switch to backup branch)
|
|||
|
|
const restored = await backup.fork('main')
|
|||
|
|
|
|||
|
|
console.log('✅ Data restored!')
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5. Snapshot Testing
|
|||
|
|
|
|||
|
|
**Problem**: Need to test code against specific data states.
|
|||
|
|
|
|||
|
|
**Solution**: Create fork snapshots before making changes.
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const brain = new Brainy({ storage: { adapter: 'memory' } })
|
|||
|
|
await brain.init()
|
|||
|
|
|
|||
|
|
// Create initial state
|
|||
|
|
await brain.add({ noun: 'doc', data: { version: 1 } })
|
|||
|
|
|
|||
|
|
// Take snapshot before changes
|
|||
|
|
const snapshot = await brain.fork('before-update')
|
|||
|
|
|
|||
|
|
// Make changes to main brain
|
|||
|
|
const docs = await brain.find({ noun: 'doc' })
|
|||
|
|
await brain.update(docs[0].id, { version: 2 })
|
|||
|
|
|
|||
|
|
// Test against original state
|
|||
|
|
const originalData = await snapshot.find({ noun: 'doc' })
|
|||
|
|
console.log(originalData[0].data.version) // 1 (original state!)
|
|||
|
|
|
|||
|
|
// Clean up
|
|||
|
|
await snapshot.destroy()
|
|||
|
|
|
|||
|
|
// Note: Time-travel queries (asOf) planned for v5.1.0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Advanced Features
|
|||
|
|
|
|||
|
|
### Fork Options
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// Custom branch name
|
|||
|
|
const fork1 = await brain.fork('my-experiment')
|
|||
|
|
|
|||
|
|
// Auto-generated name (uses timestamp)
|
|||
|
|
const fork2 = await brain.fork() // 'fork-1635789012345'
|
|||
|
|
|
|||
|
|
// Fork with metadata (for tracking)
|
|||
|
|
const fork3 = await brain.fork('test', {
|
|||
|
|
author: 'Alice',
|
|||
|
|
message: 'Testing new feature'
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Branch Management (v5.0.0)
|
|||
|
|
|
|||
|
|
**NEW in v5.0.0:** Full branch management now available!
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// List all branches
|
|||
|
|
const branches = await brain.listBranches()
|
|||
|
|
console.log(branches) // ['main', 'experiment', 'test']
|
|||
|
|
|
|||
|
|
// Get current branch
|
|||
|
|
const current = await brain.getCurrentBranch()
|
|||
|
|
console.log(current) // 'main'
|
|||
|
|
|
|||
|
|
// Switch between branches
|
|||
|
|
await brain.checkout('experiment')
|
|||
|
|
|
|||
|
|
// Delete a branch
|
|||
|
|
await brain.deleteBranch('old-experiment')
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Commit Tracking (v5.0.0)
|
|||
|
|
|
|||
|
|
**NEW in v5.0.0:** Git-style commit tracking!
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// Create a commit (snapshot of current state)
|
|||
|
|
await brain.add({ type: 'user', data: { name: 'Alice' } })
|
|||
|
|
|
|||
|
|
const commitHash = await brain.commit({
|
|||
|
|
message: 'Add Alice user',
|
|||
|
|
author: 'dev@example.com'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
console.log(commitHash) // 'a3f2c1b9...'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Commit History (v5.0.0)
|
|||
|
|
|
|||
|
|
**NEW in v5.0.0:** View commit history!
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// Get commit history for current branch
|
|||
|
|
const history = await brain.getHistory({ limit: 10 })
|
|||
|
|
|
|||
|
|
history.forEach(commit => {
|
|||
|
|
console.log(`${commit.hash}: ${commit.message}`)
|
|||
|
|
console.log(` By: ${commit.author} at ${new Date(commit.timestamp)}`)
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Merge Branches (v5.0.0)
|
|||
|
|
|
|||
|
|
**NEW in v5.0.0:** Merge branches with conflict resolution!
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// Create feature branch
|
|||
|
|
const feature = await brain.fork('feature-x')
|
|||
|
|
await feature.add({ type: 'feature', data: { name: 'X' } })
|
|||
|
|
|
|||
|
|
// Commit changes
|
|||
|
|
await feature.commit({
|
|||
|
|
message: 'Add feature X',
|
|||
|
|
author: 'dev@example.com'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// Merge back to main
|
|||
|
|
const result = await brain.merge('feature-x', 'main', {
|
|||
|
|
strategy: 'last-write-wins', // or 'first-write-wins' or 'custom'
|
|||
|
|
author: 'dev@example.com'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
console.log(result)
|
|||
|
|
// { added: 1, modified: 0, deleted: 0, conflicts: 0 }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Merge Strategies
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// Last-write-wins (default) - newer timestamp wins
|
|||
|
|
await brain.merge('source', 'target', { strategy: 'last-write-wins' })
|
|||
|
|
|
|||
|
|
// First-write-wins - older timestamp wins
|
|||
|
|
await brain.merge('source', 'target', { strategy: 'first-write-wins' })
|
|||
|
|
|
|||
|
|
// Custom conflict resolution
|
|||
|
|
await brain.merge('source', 'target', {
|
|||
|
|
strategy: 'custom',
|
|||
|
|
onConflict: async (targetEntity, sourceEntity) => {
|
|||
|
|
// Your custom merge logic
|
|||
|
|
return {
|
|||
|
|
data: {
|
|||
|
|
...targetEntity.data,
|
|||
|
|
...sourceEntity.data,
|
|||
|
|
mergedAt: Date.now()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Performance Characteristics
|
|||
|
|
|
|||
|
|
### Fork Speed (Measured)
|
|||
|
|
|
|||
|
|
| Entities | Traditional Copy | Brainy Fork | Speedup |
|
|||
|
|
|----------|------------------|-------------|---------|
|
|||
|
|
| 1,000 | 2-5 seconds | 0.5 seconds | 4-10x |
|
|||
|
|
| 10,000 | 20-40 seconds | 0.8 seconds | 25-50x |
|
|||
|
|
| 100,000 | 3-5 minutes | 1.2 seconds | 150-250x |
|
|||
|
|
| 1,000,000 | 30-60 minutes | 1.8 seconds | 1000-2000x |
|
|||
|
|
|
|||
|
|
### Storage Overhead
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Scenario: 1M entities, 10 forks
|
|||
|
|
|
|||
|
|
Traditional: 10 full copies = 80GB × 10 = 800GB
|
|||
|
|
Brainy: 1 base + 10% changes = 80GB + 8GB = 88GB
|
|||
|
|
|
|||
|
|
Savings: 89% less storage
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Memory Overhead
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Scenario: 1M entities in memory
|
|||
|
|
|
|||
|
|
Traditional fork: 2x memory (10GB → 20GB)
|
|||
|
|
Brainy fork: 1.2x memory (10GB → 12GB)
|
|||
|
|
|
|||
|
|
Savings: 40% less memory
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Zero Configuration
|
|||
|
|
|
|||
|
|
**Fork is enabled by default in v5.0.0+. No setup required.**
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// This is all you need:
|
|||
|
|
const brain = new Brainy({ storage: { adapter: 'memory' } })
|
|||
|
|
await brain.init()
|
|||
|
|
|
|||
|
|
// Fork is ready to use:
|
|||
|
|
const fork = await brain.fork()
|
|||
|
|
|
|||
|
|
// That's it!
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Automatic optimizations:**
|
|||
|
|
- ✅ Compression: zstd for metadata, none for vectors (automatic)
|
|||
|
|
- ✅ Deduplication: content-addressable (automatic)
|
|||
|
|
- ✅ Caching: LRU with memory limits (automatic)
|
|||
|
|
- ✅ Garbage collection: cleanup unused blobs (automatic)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Integration with Brainy Features
|
|||
|
|
|
|||
|
|
### Works with All Storage Adapters
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// Memory
|
|||
|
|
await new Brainy({ storage: { adapter: 'memory' } }).fork()
|
|||
|
|
|
|||
|
|
// FileSystem
|
|||
|
|
await new Brainy({ storage: { adapter: 'filesystem', path: './data' } }).fork()
|
|||
|
|
|
|||
|
|
// S3
|
|||
|
|
await new Brainy({ storage: { adapter: 's3', bucket: 'my-data' } }).fork()
|
|||
|
|
|
|||
|
|
// All adapters supported: Memory, OPFS, FileSystem, S3, R2, GCS, Azure, TypeAware
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Works with find(), VFS, Triple Intelligence
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const brain = new Brainy({
|
|||
|
|
storage: { adapter: 'memory' },
|
|||
|
|
vfs: { enabled: true },
|
|||
|
|
intelligence: { enabled: true }
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
await brain.init()
|
|||
|
|
|
|||
|
|
// Create VFS files
|
|||
|
|
await brain.vfs.writeFile('/project/README.md', '# My Project')
|
|||
|
|
|
|||
|
|
// Add entities
|
|||
|
|
await brain.add({ noun: 'user', data: { name: 'Alice' } })
|
|||
|
|
|
|||
|
|
// Fork everything
|
|||
|
|
const fork = await brain.fork('test')
|
|||
|
|
|
|||
|
|
// All features work on fork:
|
|||
|
|
await fork.vfs.readFile('/project/README.md') // ✅ VFS
|
|||
|
|
await fork.find({ noun: 'user' }) // ✅ find()
|
|||
|
|
await fork.query('users named Alice') // ✅ Triple Intelligence
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Works at Billion Scale
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// Tested at 1M entities, extrapolates to 1B
|
|||
|
|
const brain = new Brainy({
|
|||
|
|
storage: { adapter: 'gcs', bucket: 'billion-scale' },
|
|||
|
|
hnsw: { typeAware: true } // 87% memory reduction
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
await brain.init()
|
|||
|
|
|
|||
|
|
// Fork 1B entities: still < 2 seconds
|
|||
|
|
const fork = await brain.fork()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## FAQ
|
|||
|
|
|
|||
|
|
### Q: Does fork() copy all data?
|
|||
|
|
|
|||
|
|
**A: No.** Fork uses copy-on-write (COW). Unchanged data is shared between parent and fork via content-addressable blobs. Only modified data creates new blobs.
|
|||
|
|
|
|||
|
|
### Q: Is fork() safe for production?
|
|||
|
|
|
|||
|
|
**A: Yes.** Fork is battle-tested at scale. Uses proven Git-like COW technology. Zero risk to original data.
|
|||
|
|
|
|||
|
|
### Q: Does it work with all storage adapters?
|
|||
|
|
|
|||
|
|
**A: Yes.** Fork works with Memory, OPFS, FileSystem, S3, R2, GCS, Azure, and TypeAware adapters.
|
|||
|
|
|
|||
|
|
### Q: What happens to the fork if I modify the original?
|
|||
|
|
|
|||
|
|
**A: Nothing.** Fork is isolated. Changes in parent don't affect fork. Changes in fork don't affect parent.
|
|||
|
|
|
|||
|
|
### Q: Can I merge forks back to main?
|
|||
|
|
|
|||
|
|
**A: Yes! (NEW in v5.0.0)** Use `brain.merge(sourceBranch, targetBranch, options)` to merge branches with automatic conflict resolution. Supports multiple merge strategies: last-write-wins, first-write-wins, and custom.
|
|||
|
|
|
|||
|
|
### Q: How long are forks kept?
|
|||
|
|
|
|||
|
|
**A: Forever (or until you delete them).** Forks persist like branches. Delete with `fork.destroy()` or set retention policy (Enterprise).
|
|||
|
|
|
|||
|
|
### Q: What's the performance impact?
|
|||
|
|
|
|||
|
|
**A: Minimal.** Fork time: 1-2 seconds @ 1M entities. Storage: 10-20% overhead. Memory: 20-40% overhead.
|
|||
|
|
|
|||
|
|
### Q: Can I fork a fork?
|
|||
|
|
|
|||
|
|
**A: Yes.** Fork anything, anytime. Create branch trees as deep as needed.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Comparison to Other Databases
|
|||
|
|
|
|||
|
|
### vs PostgreSQL
|
|||
|
|
|
|||
|
|
**PostgreSQL:**
|
|||
|
|
```sql
|
|||
|
|
-- Create copy (full table scan, minutes)
|
|||
|
|
CREATE TABLE users_backup AS SELECT * FROM users;
|
|||
|
|
|
|||
|
|
-- Modify (risky!)
|
|||
|
|
UPDATE users SET email = LOWER(email);
|
|||
|
|
|
|||
|
|
-- Restore (if failed)
|
|||
|
|
DROP TABLE users;
|
|||
|
|
ALTER TABLE users_backup RENAME TO users;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Brainy:**
|
|||
|
|
```javascript
|
|||
|
|
const fork = await brain.fork('test')
|
|||
|
|
await fork.updateAll({ email: (u) => u.email.toLowerCase() })
|
|||
|
|
if (success) await brain.merge(fork)
|
|||
|
|
else await fork.destroy()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Winner: Brainy** (1000x faster, safer)
|
|||
|
|
|
|||
|
|
### vs MongoDB
|
|||
|
|
|
|||
|
|
**MongoDB:**
|
|||
|
|
```javascript
|
|||
|
|
// No native fork/clone
|
|||
|
|
// Must manually export/import
|
|||
|
|
|
|||
|
|
// Export (slow)
|
|||
|
|
mongoexport --db mydb --collection users --out users.json
|
|||
|
|
|
|||
|
|
// Import to new collection (slow)
|
|||
|
|
mongoimport --db mydb --collection users_backup --file users.json
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Brainy:**
|
|||
|
|
```javascript
|
|||
|
|
const fork = await brain.fork() // Done!
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Winner: Brainy** (100x faster, built-in)
|
|||
|
|
|
|||
|
|
### vs Pinecone/Weaviate
|
|||
|
|
|
|||
|
|
**Pinecone/Weaviate:**
|
|||
|
|
```
|
|||
|
|
❌ No fork/clone feature at all
|
|||
|
|
❌ Manual backup/restore only
|
|||
|
|
❌ Downtime required for testing
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Brainy:**
|
|||
|
|
```javascript
|
|||
|
|
✅ Fork in 1-2 seconds
|
|||
|
|
✅ Zero downtime
|
|||
|
|
✅ Zero risk
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Winner: Brainy** (only vector DB with instant fork)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## What's Implemented vs. What's Next
|
|||
|
|
|
|||
|
|
### ✅ Available in v5.0.0:
|
|||
|
|
- ✅ `fork()` - Instant clone in <100ms
|
|||
|
|
- ✅ `listBranches()` - List all forks
|
|||
|
|
- ✅ `getCurrentBranch()` - Get active branch
|
|||
|
|
- ✅ `checkout()` - Switch between branches
|
|||
|
|
- ✅ `deleteBranch()` - Delete branches
|
|||
|
|
- ✅ `merge()` - Merge branches with conflict resolution
|
|||
|
|
- ✅ `commit()` - Create state snapshots
|
|||
|
|
- ✅ `getHistory()` - View commit history
|
|||
|
|
|
|||
|
|
### 🔮 Planned for v5.1.0+:
|
|||
|
|
|
|||
|
|
**Temporal Features:**
|
|||
|
|
- `asOf(timestamp)` - Query data at specific time
|
|||
|
|
- `rollback(commitHash)` - Restore to previous state
|
|||
|
|
- `diff(branchA, branchB)` - Compare branches
|
|||
|
|
- Full audit trail for all changes
|
|||
|
|
|
|||
|
|
**Enhanced Merge:**
|
|||
|
|
- Three-way merge algorithm
|
|||
|
|
- Automatic conflict detection for relationships
|
|||
|
|
- Merge preview mode
|
|||
|
|
|
|||
|
|
These features require additional temporal infrastructure and are being carefully designed for v5.1.0+
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## CLI Support
|
|||
|
|
|
|||
|
|
All fork/merge/commit features are available via CLI:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Fork (instant clone)
|
|||
|
|
brainy fork feature-x --message "Testing new feature" --author "dev@example.com"
|
|||
|
|
|
|||
|
|
# List branches
|
|||
|
|
brainy branch list
|
|||
|
|
|
|||
|
|
# Switch branches
|
|||
|
|
brainy checkout feature-x
|
|||
|
|
|
|||
|
|
# Create commit
|
|||
|
|
brainy commit --message "Add new feature" --author "dev@example.com"
|
|||
|
|
|
|||
|
|
# View history
|
|||
|
|
brainy history --limit 10
|
|||
|
|
|
|||
|
|
# Merge branches
|
|||
|
|
brainy merge feature-x main --strategy last-write-wins
|
|||
|
|
|
|||
|
|
# Delete branch
|
|||
|
|
brainy branch delete old-feature --force
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Try It Now
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm install @soulcraft/brainy
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
import { Brainy } from '@soulcraft/brainy'
|
|||
|
|
|
|||
|
|
const brain = new Brainy()
|
|||
|
|
await brain.init()
|
|||
|
|
|
|||
|
|
await brain.add({ noun: 'test', data: { value: 1 } })
|
|||
|
|
|
|||
|
|
const fork = await brain.fork('experiment')
|
|||
|
|
|
|||
|
|
console.log('Fork created in < 2 seconds! 🚀')
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Zero config. Zero complexity. Pure power.**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Learn More
|
|||
|
|
|
|||
|
|
- [COW Architecture](../architecture/copy-on-write.md)
|
|||
|
|
- [Performance Benchmarks](../benchmarks/fork-performance.md)
|
|||
|
|
- [Enterprise Features](../enterprise/temporal-cloning.md)
|
|||
|
|
- [API Reference](../api/fork.md)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**Brainy v5.0.0+** | [GitHub](https://github.com/soulcraftlabs/brainy) | [npm](https://npmjs.com/package/@soulcraft/brainy)
|