chore: recovery checkpoint - v3.0 API successfully recovered
CRITICAL CHECKPOINT - DO NOT PUSH TO GITHUB Recovery Status: - Successfully recovered brainy.ts from compiled JavaScript - All core v3.0 API methods functional (add, get, update, delete, relate, find, etc.) - Neural subsystem intact (562KB embedded patterns, NLP working) - Augmentation pipeline operational (20+ augmentations) - HNSW clustering system complete - Triple Intelligence compiled (needs constructor fix) - Test suite validates functionality Changes preserved: - 898 files with changes from last 3 days - 144,475 insertions - All augmentation improvements - All test coverage enhancements - Complete v3.0 feature set This is a LOCAL checkpoint only - contains recovered work after corruption incident. Created backup in .backups/brainy-full-20250910-151314.tar.gz Branch: recovery-checkpoint-20250910-151433 Date: Wed Sep 10 03:18:04 PM PDT 2025
This commit is contained in:
parent
f65455fb22
commit
8ff382ca3b
895 changed files with 143654 additions and 28268 deletions
|
|
@ -0,0 +1,378 @@
|
|||
/**
|
||||
* Adaptive Socket Manager
|
||||
* Automatically manages socket pools and connection settings based on load patterns
|
||||
* Zero-configuration approach that learns and adapts to workload characteristics
|
||||
*/
|
||||
import { Agent as HttpsAgent } from 'https';
|
||||
import { NodeHttpHandler } from '@smithy/node-http-handler';
|
||||
import { createModuleLogger } from './logger.js';
|
||||
/**
|
||||
* Adaptive Socket Manager that automatically scales based on load patterns
|
||||
*/
|
||||
export class AdaptiveSocketManager {
|
||||
constructor() {
|
||||
this.logger = createModuleLogger('AdaptiveSocketManager');
|
||||
// Current configuration
|
||||
this.config = {
|
||||
maxSockets: 100, // Start conservative
|
||||
maxFreeSockets: 20,
|
||||
keepAliveTimeout: 60000,
|
||||
connectionTimeout: 10000,
|
||||
socketTimeout: 60000,
|
||||
batchSize: 10
|
||||
};
|
||||
// Performance tracking
|
||||
this.metrics = {
|
||||
requestsPerSecond: 0,
|
||||
pendingRequests: 0,
|
||||
socketUtilization: 0,
|
||||
errorRate: 0,
|
||||
latencyP50: 0,
|
||||
latencyP95: 0,
|
||||
memoryUsage: 0
|
||||
};
|
||||
// Historical data for learning
|
||||
this.history = [];
|
||||
this.maxHistorySize = 100;
|
||||
// Adaptation state
|
||||
this.lastAdaptationTime = 0;
|
||||
this.adaptationInterval = 5000; // Check every 5 seconds
|
||||
this.consecutiveHighLoad = 0;
|
||||
this.consecutiveLowLoad = 0;
|
||||
// Request tracking
|
||||
this.requestStartTimes = new Map();
|
||||
this.requestLatencies = [];
|
||||
this.errorCount = 0;
|
||||
this.successCount = 0;
|
||||
this.lastMetricReset = Date.now();
|
||||
// Socket pool instances
|
||||
this.currentAgent = null;
|
||||
this.currentHandler = null;
|
||||
}
|
||||
/**
|
||||
* Get or create an optimized HTTP handler
|
||||
*/
|
||||
getHttpHandler() {
|
||||
// Adapt configuration if needed
|
||||
this.adaptIfNeeded();
|
||||
// Create new handler if configuration changed
|
||||
if (!this.currentHandler || this.shouldRecreateHandler()) {
|
||||
this.currentAgent = new HttpsAgent({
|
||||
keepAlive: true,
|
||||
maxSockets: this.config.maxSockets,
|
||||
maxFreeSockets: this.config.maxFreeSockets,
|
||||
timeout: this.config.keepAliveTimeout,
|
||||
scheduling: 'fifo' // Fair scheduling for high-volume scenarios
|
||||
});
|
||||
this.currentHandler = new NodeHttpHandler({
|
||||
httpsAgent: this.currentAgent,
|
||||
connectionTimeout: this.config.connectionTimeout,
|
||||
socketTimeout: this.config.socketTimeout
|
||||
});
|
||||
this.logger.debug('Created new HTTP handler with config:', this.config);
|
||||
}
|
||||
return this.currentHandler;
|
||||
}
|
||||
/**
|
||||
* Get current batch size recommendation
|
||||
*/
|
||||
getBatchSize() {
|
||||
this.adaptIfNeeded();
|
||||
return this.config.batchSize;
|
||||
}
|
||||
/**
|
||||
* Track request start
|
||||
*/
|
||||
trackRequestStart(requestId) {
|
||||
this.requestStartTimes.set(requestId, Date.now());
|
||||
this.metrics.pendingRequests++;
|
||||
}
|
||||
/**
|
||||
* Track request completion
|
||||
*/
|
||||
trackRequestComplete(requestId, success) {
|
||||
const startTime = this.requestStartTimes.get(requestId);
|
||||
if (startTime) {
|
||||
const latency = Date.now() - startTime;
|
||||
this.requestLatencies.push(latency);
|
||||
this.requestStartTimes.delete(requestId);
|
||||
// Keep latency array bounded
|
||||
if (this.requestLatencies.length > 1000) {
|
||||
this.requestLatencies = this.requestLatencies.slice(-500);
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
this.successCount++;
|
||||
}
|
||||
else {
|
||||
this.errorCount++;
|
||||
}
|
||||
this.metrics.pendingRequests = Math.max(0, this.metrics.pendingRequests - 1);
|
||||
}
|
||||
/**
|
||||
* Check if we should adapt configuration
|
||||
*/
|
||||
adaptIfNeeded() {
|
||||
const now = Date.now();
|
||||
if (now - this.lastAdaptationTime < this.adaptationInterval) {
|
||||
return;
|
||||
}
|
||||
this.lastAdaptationTime = now;
|
||||
this.updateMetrics();
|
||||
this.analyzeAndAdapt();
|
||||
}
|
||||
/**
|
||||
* Update current metrics
|
||||
*/
|
||||
updateMetrics() {
|
||||
const now = Date.now();
|
||||
const timeSinceReset = (now - this.lastMetricReset) / 1000;
|
||||
// Calculate requests per second
|
||||
const totalRequests = this.successCount + this.errorCount;
|
||||
this.metrics.requestsPerSecond = timeSinceReset > 0
|
||||
? totalRequests / timeSinceReset
|
||||
: 0;
|
||||
// Calculate error rate
|
||||
this.metrics.errorRate = totalRequests > 0
|
||||
? this.errorCount / totalRequests
|
||||
: 0;
|
||||
// Calculate latency percentiles
|
||||
if (this.requestLatencies.length > 0) {
|
||||
const sorted = [...this.requestLatencies].sort((a, b) => a - b);
|
||||
const p50Index = Math.floor(sorted.length * 0.5);
|
||||
const p95Index = Math.floor(sorted.length * 0.95);
|
||||
this.metrics.latencyP50 = sorted[p50Index] || 0;
|
||||
this.metrics.latencyP95 = sorted[p95Index] || 0;
|
||||
}
|
||||
// Calculate socket utilization
|
||||
this.metrics.socketUtilization = this.metrics.pendingRequests / this.config.maxSockets;
|
||||
// Memory usage
|
||||
if (typeof process !== 'undefined' && process.memoryUsage) {
|
||||
const memUsage = process.memoryUsage();
|
||||
this.metrics.memoryUsage = memUsage.heapUsed / memUsage.heapTotal;
|
||||
}
|
||||
// Add to history
|
||||
this.history.push({ ...this.metrics });
|
||||
if (this.history.length > this.maxHistorySize) {
|
||||
this.history.shift();
|
||||
}
|
||||
// Reset counters periodically
|
||||
if (timeSinceReset > 60) {
|
||||
this.lastMetricReset = now;
|
||||
this.successCount = 0;
|
||||
this.errorCount = 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Analyze metrics and adapt configuration
|
||||
*/
|
||||
analyzeAndAdapt() {
|
||||
const wasConfig = { ...this.config };
|
||||
// Detect high load conditions
|
||||
const isHighLoad = this.detectHighLoad();
|
||||
const isLowLoad = this.detectLowLoad();
|
||||
const hasErrors = this.metrics.errorRate > 0.01; // More than 1% errors
|
||||
if (isHighLoad) {
|
||||
this.consecutiveHighLoad++;
|
||||
this.consecutiveLowLoad = 0;
|
||||
if (this.consecutiveHighLoad >= 2) { // Wait for 2 consecutive high load readings
|
||||
this.scaleUp();
|
||||
}
|
||||
}
|
||||
else if (isLowLoad) {
|
||||
this.consecutiveLowLoad++;
|
||||
this.consecutiveHighLoad = 0;
|
||||
if (this.consecutiveLowLoad >= 6) { // Wait longer before scaling down
|
||||
this.scaleDown();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Reset counters if load is normal
|
||||
this.consecutiveHighLoad = Math.max(0, this.consecutiveHighLoad - 1);
|
||||
this.consecutiveLowLoad = Math.max(0, this.consecutiveLowLoad - 1);
|
||||
}
|
||||
// Handle error conditions
|
||||
if (hasErrors) {
|
||||
this.handleErrors();
|
||||
}
|
||||
// Log significant changes
|
||||
if (JSON.stringify(wasConfig) !== JSON.stringify(this.config)) {
|
||||
this.logger.info('Adapted configuration', {
|
||||
from: wasConfig,
|
||||
to: this.config,
|
||||
metrics: this.metrics
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Detect high load conditions
|
||||
*/
|
||||
detectHighLoad() {
|
||||
return (this.metrics.socketUtilization > 0.7 || // Sockets heavily used
|
||||
this.metrics.pendingRequests > this.config.maxSockets * 0.8 || // Many pending requests
|
||||
this.metrics.latencyP95 > 5000 || // High latency
|
||||
this.metrics.requestsPerSecond > 100 // High request rate
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Detect low load conditions
|
||||
*/
|
||||
detectLowLoad() {
|
||||
return (this.metrics.socketUtilization < 0.2 && // Sockets barely used
|
||||
this.metrics.pendingRequests < 5 && // Few pending requests
|
||||
this.metrics.latencyP95 < 1000 && // Low latency
|
||||
this.metrics.requestsPerSecond < 10 && // Low request rate
|
||||
this.metrics.memoryUsage < 0.5 // Low memory usage
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Scale up resources for high load
|
||||
*/
|
||||
scaleUp() {
|
||||
// Increase socket limits progressively
|
||||
const scaleFactor = this.metrics.errorRate > 0.05 ? 1.5 : 2.0; // Scale more aggressively if no errors
|
||||
this.config.maxSockets = Math.min(2000, // Hard limit to prevent resource exhaustion
|
||||
Math.ceil(this.config.maxSockets * scaleFactor));
|
||||
this.config.maxFreeSockets = Math.min(200, Math.ceil(this.config.maxSockets * 0.1) // Keep 10% as free sockets
|
||||
);
|
||||
// Increase batch size for better throughput
|
||||
this.config.batchSize = Math.min(100, Math.ceil(this.config.batchSize * 1.5));
|
||||
// Adjust timeouts for high load
|
||||
this.config.keepAliveTimeout = 120000; // Keep connections alive longer
|
||||
this.config.connectionTimeout = 15000; // Allow more time for connections
|
||||
this.config.socketTimeout = 90000; // Allow more time for responses
|
||||
this.logger.debug('Scaled up for high load', {
|
||||
sockets: this.config.maxSockets,
|
||||
batchSize: this.config.batchSize
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Scale down resources for low load
|
||||
*/
|
||||
scaleDown() {
|
||||
// Only scale down if memory pressure is low
|
||||
if (this.metrics.memoryUsage > 0.7) {
|
||||
return;
|
||||
}
|
||||
// Decrease socket limits conservatively
|
||||
this.config.maxSockets = Math.max(50, // Minimum sockets
|
||||
Math.floor(this.config.maxSockets * 0.7));
|
||||
this.config.maxFreeSockets = Math.max(10, Math.floor(this.config.maxSockets * 0.2) // Keep 20% as free sockets
|
||||
);
|
||||
// Decrease batch size
|
||||
this.config.batchSize = Math.max(5, Math.floor(this.config.batchSize * 0.7));
|
||||
// Adjust timeouts for low load
|
||||
this.config.keepAliveTimeout = 60000;
|
||||
this.config.connectionTimeout = 10000;
|
||||
this.config.socketTimeout = 60000;
|
||||
this.logger.debug('Scaled down for low load', {
|
||||
sockets: this.config.maxSockets,
|
||||
batchSize: this.config.batchSize
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Handle error conditions by adjusting configuration
|
||||
*/
|
||||
handleErrors() {
|
||||
const errorRate = this.metrics.errorRate;
|
||||
if (errorRate > 0.1) { // More than 10% errors
|
||||
// Severe errors - back off aggressively
|
||||
this.config.maxSockets = Math.max(50, Math.floor(this.config.maxSockets * 0.5));
|
||||
this.config.batchSize = Math.max(1, Math.floor(this.config.batchSize * 0.3));
|
||||
this.config.connectionTimeout = Math.min(30000, this.config.connectionTimeout * 2);
|
||||
this.config.socketTimeout = Math.min(120000, this.config.socketTimeout * 2);
|
||||
this.logger.warn('High error rate detected, backing off', {
|
||||
errorRate,
|
||||
newConfig: this.config
|
||||
});
|
||||
}
|
||||
else if (errorRate > 0.05) { // More than 5% errors
|
||||
// Moderate errors - reduce load slightly
|
||||
this.config.batchSize = Math.max(1, Math.floor(this.config.batchSize * 0.7));
|
||||
this.config.connectionTimeout = Math.min(20000, this.config.connectionTimeout * 1.2);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Check if we should recreate the handler
|
||||
*/
|
||||
shouldRecreateHandler() {
|
||||
if (!this.currentAgent)
|
||||
return true;
|
||||
// Recreate if socket configuration changed significantly
|
||||
const currentMaxSockets = this.currentAgent.maxSockets;
|
||||
const socketsDiff = Math.abs(currentMaxSockets - this.config.maxSockets);
|
||||
return socketsDiff > currentMaxSockets * 0.5; // 50% change threshold
|
||||
}
|
||||
/**
|
||||
* Get current configuration (for monitoring)
|
||||
*/
|
||||
getConfig() {
|
||||
return { ...this.config };
|
||||
}
|
||||
/**
|
||||
* Get current metrics (for monitoring)
|
||||
*/
|
||||
getMetrics() {
|
||||
return { ...this.metrics };
|
||||
}
|
||||
/**
|
||||
* Predict optimal configuration based on historical data
|
||||
*/
|
||||
predictOptimalConfig() {
|
||||
if (this.history.length < 10) {
|
||||
return this.config; // Not enough data to predict
|
||||
}
|
||||
// Analyze recent history
|
||||
const recentHistory = this.history.slice(-20);
|
||||
const avgRPS = recentHistory.reduce((sum, m) => sum + m.requestsPerSecond, 0) / recentHistory.length;
|
||||
const maxRPS = Math.max(...recentHistory.map(m => m.requestsPerSecond));
|
||||
const avgLatency = recentHistory.reduce((sum, m) => sum + m.latencyP95, 0) / recentHistory.length;
|
||||
// Predict optimal socket count based on request patterns
|
||||
const optimalSockets = Math.min(2000, Math.max(50, Math.ceil(maxRPS * 2)));
|
||||
// Predict optimal batch size based on latency
|
||||
const optimalBatchSize = avgLatency < 1000 ? 50 : avgLatency < 3000 ? 20 : 10;
|
||||
return {
|
||||
maxSockets: optimalSockets,
|
||||
maxFreeSockets: Math.ceil(optimalSockets * 0.15),
|
||||
keepAliveTimeout: avgRPS > 50 ? 120000 : 60000,
|
||||
connectionTimeout: avgLatency > 3000 ? 20000 : 10000,
|
||||
socketTimeout: avgLatency > 3000 ? 90000 : 60000,
|
||||
batchSize: optimalBatchSize
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Reset to default configuration
|
||||
*/
|
||||
reset() {
|
||||
this.config = {
|
||||
maxSockets: 100,
|
||||
maxFreeSockets: 20,
|
||||
keepAliveTimeout: 60000,
|
||||
connectionTimeout: 10000,
|
||||
socketTimeout: 60000,
|
||||
batchSize: 10
|
||||
};
|
||||
this.consecutiveHighLoad = 0;
|
||||
this.consecutiveLowLoad = 0;
|
||||
this.history = [];
|
||||
this.requestLatencies = [];
|
||||
this.errorCount = 0;
|
||||
this.successCount = 0;
|
||||
// Force recreation of handler
|
||||
this.currentAgent = null;
|
||||
this.currentHandler = null;
|
||||
this.logger.info('Reset to default configuration');
|
||||
}
|
||||
}
|
||||
// Global singleton instance
|
||||
let globalSocketManager = null;
|
||||
/**
|
||||
* Get the global socket manager instance
|
||||
*/
|
||||
export function getGlobalSocketManager() {
|
||||
if (!globalSocketManager) {
|
||||
globalSocketManager = new AdaptiveSocketManager();
|
||||
}
|
||||
return globalSocketManager;
|
||||
}
|
||||
//# sourceMappingURL=adaptiveSocketManager.js.map
|
||||
Loading…
Add table
Add a link
Reference in a new issue