brainy/docs/technical/CONCURRENCY_ANALYSIS.md
David Snelling 79df44351c **chore: remove outdated changelog and summary documents**
- 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.
2025-07-30 11:51:39 -07:00

6.8 KiB

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

1. Implement Distributed Locking

// 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

// 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

// 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

// 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

// 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