refactor(8.0)!: remove distributed clustering subsystem — inert/orphaned, scale is single-process + native provider
The distributed-clustering subsystem never ran in production: it was inert, orphaned dead code (faked consensus, stub replication, no live wiring, and it did not interoperate with the 8.0 Db API). Brainy 8.0 is a single-process library. Scale is single-process + the optional native provider (@soulcraft/cortex, on-disk DiskANN to 10B+ vectors) + per-tenant pools + horizontal read scaling (many reader processes, one writer). Removed: - src/distributed/ entirely (coordinator, shardManager, cacheSync, readWriteSeparation, queryPlanner, healthMonitor, configManager, hashPartitioner, shardMigration, domainDetector, storageDiscovery, http/network transports). ReaderMode/HybridMode relocated to src/storage/operationalModes.ts (slimmed to the live surface). - src/types/distributedTypes.ts; config.distributed field + JSDoc; coreTypes distributedConfig; memoryStorage distributedConfig persistence. - DistributedRole enum + src/config/distributedPresets.ts and the orphaned src/config/extensibleConfig.ts (config/augmentation registry built on removed cloud adapters + distributed presets), plus their src/index.ts re-exports. - 13 BRAINY_* cluster env vars; the storage setDistributedComponents hook; enableDistributedSearch (dead config flag); the metadata partition field; the distributed_ reserved key prefix. - Orphaned src/storage/readOnlyOptimizations.ts (zero importers). - Tests targeting the subsystem: distributed-demo, distributed-cluster helper, distributed-transactions, sharding-transactions. - Docs: EXTENDING_STORAGE.md (deleted); scrubbed distributed/cluster/Raft/ shard-manager/multi-node prose from v3-features, enterprise-for-everyone, augmentations-actual, complete-feature-list, vfs/README, vfs/ROADMAP, vfs/VFS_CORE, capacity-planning, transactions, MIGRATION-V3-TO-V4, storage-architecture; reframed scale prose to the 8.0 model. Kept: src/storage/sharding.ts (local-disk 256-bucket directory sharding via getShardIdFromUuid — used live by baseStorage, unrelated to clustering); the multi-process mode: 'reader' | 'writer' roles; semantic/HNSW clustering. RELEASES.md: added a removed-surfaces row documenting the cut and the 8.0 scale model.
This commit is contained in:
parent
f8e0079d3f
commit
00d3203d68
51 changed files with 153 additions and 9889 deletions
167
src/brainy.ts
167
src/brainy.ts
|
|
@ -72,14 +72,10 @@ import {
|
|||
DeleteVerbMetadataOperation
|
||||
} from './transaction/operations/index.js'
|
||||
import {
|
||||
DistributedCoordinator,
|
||||
ShardManager,
|
||||
CacheSync,
|
||||
ReadWriteSeparation,
|
||||
BaseOperationalMode,
|
||||
ReaderMode,
|
||||
HybridMode
|
||||
} from './distributed/index.js'
|
||||
} from './storage/operationalModes.js'
|
||||
import {
|
||||
Entity,
|
||||
Relation,
|
||||
|
|
@ -199,7 +195,6 @@ interface StorageBackgroundInitHooks {
|
|||
type ResolvedBrainyConfig = Required<
|
||||
Omit<
|
||||
BrainyConfig,
|
||||
| 'distributed'
|
||||
| 'maxQueryLimit'
|
||||
| 'reservedQueryMemory'
|
||||
| 'vector'
|
||||
|
|
@ -211,7 +206,6 @@ type ResolvedBrainyConfig = Required<
|
|||
> &
|
||||
Pick<
|
||||
BrainyConfig,
|
||||
| 'distributed'
|
||||
| 'maxQueryLimit'
|
||||
| 'reservedQueryMemory'
|
||||
| 'vector'
|
||||
|
|
@ -329,12 +323,6 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
/** Cap for {@link Brainy.verbIntWarmCache} (~100k entries ≈ a few MB). */
|
||||
private static readonly VERB_INT_WARM_CACHE_MAX = 100_000
|
||||
|
||||
// Distributed components (optional)
|
||||
private coordinator?: DistributedCoordinator
|
||||
private shardManager?: ShardManager
|
||||
private cacheSync?: CacheSync
|
||||
private readWriteSeparation?: ReadWriteSeparation
|
||||
|
||||
// Silent mode state
|
||||
private originalConsole?: {
|
||||
log: typeof console.log
|
||||
|
|
@ -414,9 +402,9 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
this.config = this.normalizeConfig(config)
|
||||
|
||||
// Multi-process mode — default 'writer' uses HybridMode (read + write),
|
||||
// 'reader' uses ReaderMode (read-only, all mutations throw).
|
||||
// `WriterMode` from operationalModes.ts blocks reads, which is too strict
|
||||
// for a Brainy instance — a writer needs to read its own data.
|
||||
// 'reader' uses ReaderMode (read-only, all mutations throw). A writer needs
|
||||
// to read its own data, so the writer role is read-write rather than
|
||||
// write-only.
|
||||
this.operationalMode = this.config.mode === 'reader'
|
||||
? new ReaderMode()
|
||||
: new HybridMode()
|
||||
|
|
@ -435,11 +423,6 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
this.embedder = this.setupEmbedder()
|
||||
this.transactionManager = new TransactionManager()
|
||||
|
||||
// Setup distributed components if enabled
|
||||
if (this.config.distributed?.enabled) {
|
||||
this.setupDistributedComponents()
|
||||
}
|
||||
|
||||
// Initialize ready Promise
|
||||
// This allows consumers to await brain.ready before using the database
|
||||
this._readyPromise = new Promise<void>((resolve, reject) => {
|
||||
|
|
@ -846,9 +829,6 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
// Check for pending data migrations
|
||||
await this.checkMigrations()
|
||||
|
||||
// Connect distributed components to storage
|
||||
await this.connectDistributedStorage()
|
||||
|
||||
// Register shutdown hooks for graceful count flushing (once globally)
|
||||
if (!Brainy.shutdownHooksRegisteredGlobally) {
|
||||
this.registerShutdownHooks()
|
||||
|
|
@ -10511,13 +10491,8 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
)
|
||||
}
|
||||
|
||||
// Distributed mode is explicit opt-in only (config or BRAINY_DISTRIBUTED
|
||||
// env var) — never inferred from deployment heuristics.
|
||||
const distributedConfig = this.resolveDistributedConfig(config?.distributed)
|
||||
|
||||
return {
|
||||
storage: config?.storage || { type: 'auto' },
|
||||
distributed: distributedConfig,
|
||||
verbose: config?.verbose ?? false,
|
||||
silent: config?.silent ?? false,
|
||||
// false = auto-decide based on dataset size (inline vs lazy rebuild)
|
||||
|
|
@ -11144,140 +11119,6 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
// operation (ensureInitialized() throws once this is set).
|
||||
this.closed = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Intelligently auto-detect distributed configuration
|
||||
* Zero-config: Automatically determines best distributed settings
|
||||
*/
|
||||
/**
|
||||
* Resolve distributed-cluster configuration. **Explicit opt-in only**:
|
||||
* either `config.distributed.enabled === true` or the
|
||||
* `BRAINY_DISTRIBUTED=true` environment variable. Once opted in, the
|
||||
* remaining fields default sensibly (64 shards, 3 replicas, raft, http,
|
||||
* hostname-derived nodeId).
|
||||
*
|
||||
* Earlier versions auto-enabled cluster mode from deployment heuristics
|
||||
* (`NODE_ENV=production`, a Kubernetes service host, `CLUSTER_SIZE`).
|
||||
* That violated zero-config safety: a single-process production
|
||||
* deployment — the overwhelmingly common case — would silently start a
|
||||
* coordinator, bind a port, and run raft against itself. Deployment
|
||||
* environment does not imply a multi-node Brainy cluster, so the
|
||||
* heuristics are gone.
|
||||
*/
|
||||
private resolveDistributedConfig(config?: BrainyConfig['distributed']): BrainyConfig['distributed'] {
|
||||
// Env-var opt-in for deployments that can't touch code (the ONLY
|
||||
// environment signal honoured — it names brainy explicitly).
|
||||
if (!config && process.env.BRAINY_DISTRIBUTED === 'true') {
|
||||
return {
|
||||
enabled: true,
|
||||
nodeId: process.env.HOSTNAME || process.env.NODE_ID || `node-${Date.now()}`,
|
||||
nodes: process.env.BRAINY_NODES?.split(',') || [],
|
||||
coordinatorUrl: process.env.BRAINY_COORDINATOR || undefined,
|
||||
shardCount: parseInt(process.env.BRAINY_SHARDS || '64'),
|
||||
replicationFactor: parseInt(process.env.BRAINY_REPLICAS || '3'),
|
||||
consensus: (process.env.BRAINY_CONSENSUS as 'raft' | 'none' | undefined) || 'raft',
|
||||
transport: (process.env.BRAINY_TRANSPORT as 'tcp' | 'http' | 'udp' | undefined) || 'http'
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit config: fill the per-field defaults.
|
||||
return config ? {
|
||||
...config,
|
||||
nodeId: config.nodeId || process.env.HOSTNAME || `node-${Date.now()}`,
|
||||
shardCount: config.shardCount || 64,
|
||||
replicationFactor: config.replicationFactor || 3,
|
||||
consensus: config.consensus || 'raft',
|
||||
transport: config.transport || 'http'
|
||||
} : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup distributed components with zero-config intelligence
|
||||
*/
|
||||
private setupDistributedComponents(): void {
|
||||
const distConfig = this.config.distributed
|
||||
if (!distConfig?.enabled) return
|
||||
|
||||
console.log('🌍 Initializing distributed mode:', {
|
||||
nodeId: distConfig.nodeId,
|
||||
shards: distConfig.shardCount,
|
||||
replicas: distConfig.replicationFactor
|
||||
})
|
||||
|
||||
// Initialize coordinator for consensus
|
||||
this.coordinator = new DistributedCoordinator({
|
||||
nodeId: distConfig.nodeId,
|
||||
address: distConfig.coordinatorUrl?.split(':')[0] || 'localhost',
|
||||
port: parseInt(distConfig.coordinatorUrl?.split(':')[1] || '8080'),
|
||||
nodes: distConfig.nodes
|
||||
})
|
||||
|
||||
// Start the coordinator to establish leadership
|
||||
this.coordinator.start().catch(err => {
|
||||
console.warn('Coordinator start failed (will retry on init):', err.message)
|
||||
})
|
||||
|
||||
// Initialize shard manager for data distribution
|
||||
this.shardManager = new ShardManager({
|
||||
shardCount: distConfig.shardCount,
|
||||
replicationFactor: distConfig.replicationFactor,
|
||||
virtualNodes: 150, // Optimal for consistent distribution
|
||||
autoRebalance: true
|
||||
})
|
||||
|
||||
// Initialize cache synchronization
|
||||
this.cacheSync = new CacheSync({
|
||||
nodeId: distConfig.nodeId!,
|
||||
syncInterval: 1000
|
||||
})
|
||||
|
||||
// Initialize read/write separation if we have replicas
|
||||
// Note: Will be properly initialized after coordinator starts
|
||||
if (distConfig.replicationFactor && distConfig.replicationFactor > 1) {
|
||||
// Defer creation until coordinator is ready
|
||||
setTimeout(() => {
|
||||
this.readWriteSeparation = new ReadWriteSeparation(
|
||||
{
|
||||
nodeId: distConfig.nodeId!,
|
||||
consistencyLevel: 'eventual',
|
||||
role: 'replica', // Start as replica, will promote if leader
|
||||
syncInterval: 5000
|
||||
},
|
||||
this.coordinator!,
|
||||
this.shardManager!,
|
||||
this.cacheSync!
|
||||
)
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass distributed components to storage adapter
|
||||
*/
|
||||
private async connectDistributedStorage(): Promise<void> {
|
||||
if (!this.config.distributed?.enabled) return
|
||||
|
||||
// Check if storage supports distributed operations. No in-repo adapter
|
||||
// implements this hook — it exists for external/plugin adapters, so the
|
||||
// `in` guard above is the contract and this cast just names its shape.
|
||||
if ('setDistributedComponents' in this.storage) {
|
||||
(this.storage as BaseStorage & {
|
||||
setDistributedComponents(components: {
|
||||
coordinator?: DistributedCoordinator
|
||||
shardManager?: ShardManager
|
||||
cacheSync?: CacheSync
|
||||
readWriteSeparation?: ReadWriteSeparation
|
||||
}): void
|
||||
}).setDistributedComponents({
|
||||
coordinator: this.coordinator,
|
||||
shardManager: this.shardManager,
|
||||
cacheSync: this.cacheSync,
|
||||
readWriteSeparation: this.readWriteSeparation
|
||||
})
|
||||
|
||||
console.log('✅ Distributed storage connected')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue