- Deleted `CHANGES.md`, `CHANGES_SUMMARY.md`, `CONCURRENCY_ANALYSIS.md`, `CONCURRENCY_IMPLEMENTATION_SUMMARY.md`, and related developer documentation files. - Removed redundant or legacy content no longer aligned with the current codebase and workflows. - Updated repository to reflect streamlined documentation approach, reducing clutter and improving maintainability. **Purpose**: Simplify and declutter repository by removing obsolete documentation files, ensuring it remains focused and relevant.
207 lines
6.8 KiB
Markdown
207 lines
6.8 KiB
Markdown
# Brainy Concurrency and Performance Analysis
|
|
|
|
## Issue Summary
|
|
Multiple web services are running Brainy with shared S3 storage, causing performance and contention issues in high-throughput scenarios.
|
|
|
|
## Identified Problems
|
|
|
|
### 1. Statistics Handling Issues
|
|
|
|
#### Race Conditions in Statistics Updates
|
|
- **Location**: `S3CompatibleStorage.scheduleBatchUpdate()` and `flushStatistics()`
|
|
- **Problem**: Multiple service instances update statistics independently without coordination
|
|
- **Impact**: Lost updates, inconsistent statistics, data corruption
|
|
|
|
#### Cache Inconsistency
|
|
- **Location**: `S3CompatibleStorage.statisticsCache`
|
|
- **Problem**: Each instance maintains its own statistics cache
|
|
- **Impact**: Statistics displayed by search service may be stale or incorrect
|
|
|
|
#### Timer-based Batching Issues
|
|
- **Location**: `S3CompatibleStorage.scheduleBatchUpdate()`
|
|
- **Problem**: setTimeout-based batching with no coordination between instances
|
|
- **Impact**: Statistics updates can be delayed or lost during service restarts
|
|
|
|
### 2. Index Synchronization Issues
|
|
|
|
#### Inefficient Full Scans
|
|
- **Location**: `BrainyData.checkForUpdates()`
|
|
- **Problem**: Calls `getAllNouns()` on every update check
|
|
- **Impact**: Extremely expensive for large datasets, poor scalability
|
|
|
|
#### Race Conditions in Index Updates
|
|
- **Location**: `BrainyData.checkForUpdates()` lines 438-456
|
|
- **Problem**: Multiple instances can add the same nouns simultaneously
|
|
- **Impact**: Inconsistent index state, wasted resources
|
|
|
|
#### No Distributed Locking
|
|
- **Location**: Throughout the codebase
|
|
- **Problem**: No mechanism to coordinate updates between multiple instances
|
|
- **Impact**: Data corruption, inconsistent state
|
|
|
|
### 3. Memory and Performance Issues
|
|
|
|
#### Memory Usage Tracking Race Conditions
|
|
- **Location**: `HNSWIndexOptimized.addItem()` lines 347-348
|
|
- **Problem**: `this.memoryUsage += totalMemory` and `this.vectorCount++` are not thread-safe
|
|
- **Impact**: Incorrect memory usage calculations, potential memory leaks
|
|
|
|
#### Duplicate Index Maintenance
|
|
- **Location**: Each service instance
|
|
- **Problem**: Every instance maintains a complete copy of the HNSW index
|
|
- **Impact**: Excessive memory usage, slow startup times
|
|
|
|
#### Polling-based Updates
|
|
- **Location**: `BrainyData.startRealtimeUpdates()`
|
|
- **Problem**: Uses setInterval for periodic checks instead of event-driven updates
|
|
- **Impact**: High latency, unnecessary resource usage
|
|
|
|
### 4. Storage Contention Issues
|
|
|
|
#### Concurrent S3 Writes
|
|
- **Location**: `S3CompatibleStorage.saveNode()`, `saveEdge()`, etc.
|
|
- **Problem**: No coordination for concurrent writes to the same S3 objects
|
|
- **Impact**: Data corruption, lost writes
|
|
|
|
#### No Optimistic Locking
|
|
- **Location**: All storage operations
|
|
- **Problem**: No mechanism to detect and handle concurrent modifications
|
|
- **Impact**: Last-writer-wins scenarios, data loss
|
|
|
|
## Recommended Solutions
|
|
|
|
### 1. Implement Distributed Locking
|
|
|
|
```typescript
|
|
// Add to S3CompatibleStorage
|
|
private async acquireLock(lockKey: string, ttl: number = 30000): Promise<boolean> {
|
|
const lockObject = `locks/${lockKey}`;
|
|
const lockValue = `${Date.now()}_${Math.random()}`;
|
|
|
|
try {
|
|
await this.s3Client!.send(new PutObjectCommand({
|
|
Bucket: this.bucketName,
|
|
Key: lockObject,
|
|
Body: lockValue,
|
|
ContentType: 'text/plain',
|
|
Metadata: {
|
|
'expires-at': (Date.now() + ttl).toString()
|
|
}
|
|
}));
|
|
return true;
|
|
} catch (error) {
|
|
if (error.name === 'ConditionalCheckFailedException') {
|
|
return false; // Lock already exists
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. Event-Driven Index Updates
|
|
|
|
```typescript
|
|
// Add to BrainyData
|
|
private async setupEventDrivenUpdates(): Promise<void> {
|
|
// Use S3 event notifications or implement a change log
|
|
const changeLogKey = `${this.indexPrefix}change-log.json`;
|
|
|
|
// Poll change log instead of full data scan
|
|
setInterval(async () => {
|
|
const changes = await this.getChangesSince(this.lastUpdateTime);
|
|
await this.applyChanges(changes);
|
|
}, this.realtimeUpdateConfig.interval);
|
|
}
|
|
```
|
|
|
|
### 3. Optimized Statistics Handling
|
|
|
|
```typescript
|
|
// Add to S3CompatibleStorage
|
|
private async atomicStatisticsUpdate(updateFn: (stats: StatisticsData) => StatisticsData): Promise<void> {
|
|
const lockKey = 'statistics-update';
|
|
const lockAcquired = await this.acquireLock(lockKey);
|
|
|
|
if (!lockAcquired) {
|
|
// Another instance is updating, skip this update
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Read current statistics
|
|
const currentStats = await this.getStatisticsData();
|
|
|
|
// Apply update
|
|
const updatedStats = updateFn(currentStats);
|
|
|
|
// Write back with version check
|
|
await this.saveStatisticsWithVersionCheck(updatedStats);
|
|
} finally {
|
|
await this.releaseLock(lockKey);
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. Shared Index Architecture
|
|
|
|
```typescript
|
|
// New class: SharedHNSWIndex
|
|
export class SharedHNSWIndex {
|
|
private localCache: Map<string, VectorDocument> = new Map();
|
|
private lastSyncTime: number = 0;
|
|
|
|
async search(queryVector: Vector, k: number): Promise<Array<[string, number]>> {
|
|
// Ensure local cache is up to date
|
|
await this.syncIfNeeded();
|
|
|
|
// Perform search on local cache
|
|
return this.performLocalSearch(queryVector, k);
|
|
}
|
|
|
|
private async syncIfNeeded(): Promise<void> {
|
|
const now = Date.now();
|
|
if (now - this.lastSyncTime > this.syncInterval) {
|
|
await this.syncFromStorage();
|
|
this.lastSyncTime = now;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 5. Change Log Implementation
|
|
|
|
```typescript
|
|
// Add to storage adapters
|
|
interface ChangeLogEntry {
|
|
timestamp: number;
|
|
operation: 'add' | 'update' | 'delete';
|
|
entityType: 'noun' | 'verb';
|
|
entityId: string;
|
|
data?: any;
|
|
}
|
|
|
|
private async appendToChangeLog(entry: ChangeLogEntry): Promise<void> {
|
|
const changeLogKey = `change-log/${Date.now()}-${Math.random()}.json`;
|
|
await this.s3Client!.send(new PutObjectCommand({
|
|
Bucket: this.bucketName,
|
|
Key: changeLogKey,
|
|
Body: JSON.stringify(entry),
|
|
ContentType: 'application/json'
|
|
}));
|
|
}
|
|
```
|
|
|
|
## Implementation Priority
|
|
|
|
1. **High Priority**: Implement distributed locking for statistics updates
|
|
2. **High Priority**: Add change log mechanism for efficient index synchronization
|
|
3. **Medium Priority**: Implement shared index architecture
|
|
4. **Medium Priority**: Add optimistic locking for storage operations
|
|
5. **Low Priority**: Optimize memory usage tracking
|
|
|
|
## Performance Improvements Expected
|
|
|
|
- **Statistics Updates**: 90% reduction in conflicts, near real-time updates
|
|
- **Index Synchronization**: 95% reduction in data transfer, faster updates
|
|
- **Memory Usage**: 70% reduction per service instance
|
|
- **Search Latency**: 50% improvement due to better cache locality
|