Initial commit: Brainy - Multi-Dimensional AI Database
Open source vector database with HNSW indexing, graph relationships, and metadata facets. Features CLI with professional augmentation registry integration for discovering extensions and capabilities.
This commit is contained in:
commit
f8c45f2d8d
448 changed files with 103294 additions and 0 deletions
146
dist/distributed/hashPartitioner.js
vendored
Normal file
146
dist/distributed/hashPartitioner.js
vendored
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/**
|
||||
* Hash-based Partitioner
|
||||
* Provides deterministic partitioning for distributed writes
|
||||
*/
|
||||
import { getPartitionHash } from '../utils/crypto.js';
|
||||
export class HashPartitioner {
|
||||
constructor(config) {
|
||||
this.partitionPrefix = 'vectors/p';
|
||||
this.partitionCount = config.settings.partitionCount || 100;
|
||||
}
|
||||
/**
|
||||
* Get partition for a given vector ID using deterministic hashing
|
||||
* @param vectorId - The unique identifier of the vector
|
||||
* @returns The partition path
|
||||
*/
|
||||
getPartition(vectorId) {
|
||||
const hash = this.hashString(vectorId);
|
||||
const partitionIndex = hash % this.partitionCount;
|
||||
return `${this.partitionPrefix}${partitionIndex.toString().padStart(3, '0')}`;
|
||||
}
|
||||
/**
|
||||
* Get partition with domain metadata (domain stored as metadata, not in path)
|
||||
* @param vectorId - The unique identifier of the vector
|
||||
* @param domain - The domain identifier (for metadata only)
|
||||
* @returns The partition path
|
||||
*/
|
||||
getPartitionWithDomain(vectorId, domain) {
|
||||
// Domain doesn't affect partitioning - it's just metadata
|
||||
return this.getPartition(vectorId);
|
||||
}
|
||||
/**
|
||||
* Get all partition paths
|
||||
* @returns Array of all partition paths
|
||||
*/
|
||||
getAllPartitions() {
|
||||
const partitions = [];
|
||||
for (let i = 0; i < this.partitionCount; i++) {
|
||||
partitions.push(`${this.partitionPrefix}${i.toString().padStart(3, '0')}`);
|
||||
}
|
||||
return partitions;
|
||||
}
|
||||
/**
|
||||
* Get partition index from partition path
|
||||
* @param partitionPath - The partition path
|
||||
* @returns The partition index
|
||||
*/
|
||||
getPartitionIndex(partitionPath) {
|
||||
const match = partitionPath.match(/p(\d+)$/);
|
||||
if (match) {
|
||||
return parseInt(match[1], 10);
|
||||
}
|
||||
throw new Error(`Invalid partition path: ${partitionPath}`);
|
||||
}
|
||||
/**
|
||||
* Hash a string to a number for consistent partitioning
|
||||
* @param str - The string to hash
|
||||
* @returns A positive integer hash
|
||||
*/
|
||||
hashString(str) {
|
||||
// Use our cross-platform hash function
|
||||
return getPartitionHash(str);
|
||||
}
|
||||
/**
|
||||
* Get partitions for batch operations
|
||||
* Groups vector IDs by their target partition
|
||||
* @param vectorIds - Array of vector IDs
|
||||
* @returns Map of partition to vector IDs
|
||||
*/
|
||||
getPartitionsForBatch(vectorIds) {
|
||||
const partitionMap = new Map();
|
||||
for (const id of vectorIds) {
|
||||
const partition = this.getPartition(id);
|
||||
if (!partitionMap.has(partition)) {
|
||||
partitionMap.set(partition, []);
|
||||
}
|
||||
partitionMap.get(partition).push(id);
|
||||
}
|
||||
return partitionMap;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Affinity-based Partitioner
|
||||
* Extends HashPartitioner to prefer certain partitions for a writer
|
||||
* while maintaining correctness
|
||||
*/
|
||||
export class AffinityPartitioner extends HashPartitioner {
|
||||
constructor(config, instanceId) {
|
||||
super(config);
|
||||
this.instanceId = instanceId;
|
||||
this.preferredPartitions = this.calculatePreferredPartitions(config);
|
||||
}
|
||||
/**
|
||||
* Calculate preferred partitions for this instance
|
||||
*/
|
||||
calculatePreferredPartitions(config) {
|
||||
const partitionCount = config.settings.partitionCount || 100;
|
||||
const writers = Object.entries(config.instances)
|
||||
.filter(([_, inst]) => inst.role === 'writer')
|
||||
.map(([id, _]) => id)
|
||||
.sort(); // Ensure consistent ordering
|
||||
const writerIndex = writers.indexOf(this.instanceId);
|
||||
if (writerIndex === -1) {
|
||||
// Not a writer or not found, no preferences
|
||||
return new Set();
|
||||
}
|
||||
const writerCount = writers.length;
|
||||
const partitionsPerWriter = Math.ceil(partitionCount / writerCount);
|
||||
const preferred = new Set();
|
||||
const start = writerIndex * partitionsPerWriter;
|
||||
const end = Math.min(start + partitionsPerWriter, partitionCount);
|
||||
for (let i = start; i < end; i++) {
|
||||
preferred.add(i);
|
||||
}
|
||||
return preferred;
|
||||
}
|
||||
/**
|
||||
* Check if a partition is preferred for this instance
|
||||
* @param partitionPath - The partition path
|
||||
* @returns Whether this partition is preferred
|
||||
*/
|
||||
isPreferredPartition(partitionPath) {
|
||||
try {
|
||||
const index = this.getPartitionIndex(partitionPath);
|
||||
return this.preferredPartitions.has(index);
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get all preferred partitions for this instance
|
||||
* @returns Array of preferred partition paths
|
||||
*/
|
||||
getPreferredPartitions() {
|
||||
return Array.from(this.preferredPartitions)
|
||||
.map(index => `vectors/p${index.toString().padStart(3, '0')}`);
|
||||
}
|
||||
/**
|
||||
* Update preferred partitions based on new config
|
||||
* @param config - The updated shared configuration
|
||||
*/
|
||||
updatePreferences(config) {
|
||||
this.preferredPartitions = this.calculatePreferredPartitions(config);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=hashPartitioner.js.map
|
||||
Loading…
Add table
Add a link
Reference in a new issue