@soulcraft/brainy 3.35.0 → 3.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,15 @@
1
1
  /**
2
2
  * UnifiedCache - Single cache for both HNSW and MetadataIndex
3
3
  * Prevents resource competition with cost-aware eviction
4
+ *
5
+ * Features (v3.36.0+):
6
+ * - Adaptive sizing: Automatically scales from 2GB to 128GB+ based on available memory
7
+ * - Container-aware: Detects Docker/K8s limits (cgroups v1/v2)
8
+ * - Environment detection: Production vs development allocation strategies
9
+ * - Memory pressure monitoring: Warns when approaching limits
4
10
  */
5
11
  import { prodLog } from './logger.js';
12
+ import { getRecommendedCacheConfig, formatBytes, checkMemoryPressure } from './memoryDetection.js';
6
13
  export class UnifiedCache {
7
14
  constructor(config = {}) {
8
15
  this.cache = new Map();
@@ -11,17 +18,51 @@ export class UnifiedCache {
11
18
  this.typeAccessCounts = { hnsw: 0, metadata: 0, embedding: 0, other: 0 };
12
19
  this.totalAccessCount = 0;
13
20
  this.currentSize = 0;
14
- this.maxSize = config.maxSize || 2 * 1024 * 1024 * 1024; // 2GB default
21
+ this.memoryPressureCheckTimer = null;
22
+ this.lastMemoryWarning = 0;
23
+ // Adaptive cache sizing (v3.36.0+)
24
+ const recommendation = getRecommendedCacheConfig({
25
+ manualSize: config.maxSize,
26
+ minSize: config.minSize,
27
+ developmentMode: config.developmentMode
28
+ });
29
+ this.memoryInfo = recommendation.memoryInfo;
30
+ this.allocationStrategy = recommendation.allocation;
31
+ this.maxSize = recommendation.allocation.cacheSize;
32
+ // Log allocation decision (v3.36.0+: includes model memory)
33
+ prodLog.info(`UnifiedCache initialized: ${formatBytes(this.maxSize)} ` +
34
+ `(${this.allocationStrategy.environment} mode, ` +
35
+ `${(this.allocationStrategy.ratio * 100).toFixed(0)}% of ${formatBytes(this.allocationStrategy.availableForCache)} ` +
36
+ `after ${formatBytes(this.allocationStrategy.modelMemory)} ${this.allocationStrategy.modelPrecision.toUpperCase()} model)`);
37
+ // Log memory detection details
38
+ prodLog.debug(`Memory detection: source=${this.memoryInfo.source}, ` +
39
+ `container=${this.memoryInfo.isContainer}, ` +
40
+ `system=${formatBytes(this.memoryInfo.systemTotal)}, ` +
41
+ `free=${formatBytes(this.memoryInfo.free)}, ` +
42
+ `totalAvailable=${formatBytes(this.memoryInfo.available)}, ` +
43
+ `modelReserved=${formatBytes(this.allocationStrategy.modelMemory)}, ` +
44
+ `availableForCache=${formatBytes(this.allocationStrategy.availableForCache)}`);
45
+ // Log warnings if any
46
+ for (const warning of recommendation.warnings) {
47
+ prodLog.warn(`UnifiedCache: ${warning}`);
48
+ }
49
+ // Finalize configuration
15
50
  this.config = {
16
51
  enableRequestCoalescing: true,
17
52
  enableFairnessCheck: true,
18
53
  fairnessCheckInterval: 60000, // Check fairness every minute
19
54
  persistPatterns: true,
55
+ enableMemoryMonitoring: true,
56
+ memoryCheckInterval: 30000, // Check memory every 30s
20
57
  ...config
21
58
  };
59
+ // Start monitoring
22
60
  if (this.config.enableFairnessCheck) {
23
61
  this.startFairnessMonitor();
24
62
  }
63
+ if (this.config.enableMemoryMonitoring) {
64
+ this.startMemoryPressureMonitor();
65
+ }
25
66
  }
26
67
  /**
27
68
  * Get item from cache with request coalescing
@@ -62,6 +103,25 @@ export class UnifiedCache {
62
103
  }
63
104
  }
64
105
  }
106
+ /**
107
+ * Synchronous cache lookup (v3.36.0+)
108
+ * Returns cached data immediately or undefined if not cached
109
+ * Use for sync fast path optimization - zero async overhead
110
+ */
111
+ getSync(key) {
112
+ // Check if in cache
113
+ const item = this.cache.get(key);
114
+ if (item) {
115
+ // Update access tracking synchronously
116
+ this.access.set(key, (this.access.get(key) || 0) + 1);
117
+ this.totalAccessCount++;
118
+ item.lastAccess = Date.now();
119
+ item.accessCount++;
120
+ this.typeAccessCounts[item.type]++;
121
+ return item.data;
122
+ }
123
+ return undefined;
124
+ }
65
125
  /**
66
126
  * Set item in cache with cost-aware eviction
67
127
  */
@@ -234,7 +294,45 @@ export class UnifiedCache {
234
294
  }
235
295
  }
236
296
  /**
237
- * Get cache statistics
297
+ * Start memory pressure monitoring
298
+ * Periodically checks if we're approaching memory limits
299
+ */
300
+ startMemoryPressureMonitor() {
301
+ const checkInterval = this.config.memoryCheckInterval || 30000;
302
+ this.memoryPressureCheckTimer = setInterval(() => {
303
+ this.checkMemoryPressure();
304
+ }, checkInterval);
305
+ // Unref so it doesn't keep process alive
306
+ if (this.memoryPressureCheckTimer.unref) {
307
+ this.memoryPressureCheckTimer.unref();
308
+ }
309
+ }
310
+ /**
311
+ * Check current memory pressure and warn if needed
312
+ */
313
+ checkMemoryPressure() {
314
+ const pressure = checkMemoryPressure(this.currentSize, this.memoryInfo);
315
+ // Only log warnings every 5 minutes to avoid spam
316
+ const now = Date.now();
317
+ const fiveMinutes = 5 * 60 * 1000;
318
+ if (pressure.warnings.length > 0 && now - this.lastMemoryWarning > fiveMinutes) {
319
+ for (const warning of pressure.warnings) {
320
+ prodLog.warn(`UnifiedCache: ${warning}`);
321
+ }
322
+ this.lastMemoryWarning = now;
323
+ }
324
+ // If critical, force aggressive eviction
325
+ if (pressure.pressure === 'critical') {
326
+ const targetSize = Math.floor(this.maxSize * 0.7); // Evict to 70%
327
+ const bytesToFree = this.currentSize - targetSize;
328
+ if (bytesToFree > 0) {
329
+ prodLog.warn(`UnifiedCache: Critical memory pressure - forcing eviction of ${formatBytes(bytesToFree)}`);
330
+ this.evictForSize(bytesToFree);
331
+ }
332
+ }
333
+ }
334
+ /**
335
+ * Get cache statistics with memory information
238
336
  */
239
337
  getStats() {
240
338
  const typeSizes = { hnsw: 0, metadata: 0, embedding: 0, other: 0 };
@@ -243,7 +341,10 @@ export class UnifiedCache {
243
341
  typeSizes[item.type] += item.size;
244
342
  typeCounts[item.type]++;
245
343
  }
344
+ const hitRate = this.cache.size > 0 ?
345
+ Array.from(this.cache.values()).reduce((sum, item) => sum + item.accessCount, 0) / this.totalAccessCount : 0;
246
346
  return {
347
+ // Cache statistics
247
348
  totalSize: this.currentSize,
248
349
  maxSize: this.maxSize,
249
350
  utilization: this.currentSize / this.maxSize,
@@ -252,8 +353,26 @@ export class UnifiedCache {
252
353
  typeCounts,
253
354
  typeAccessCounts: this.typeAccessCounts,
254
355
  totalAccessCount: this.totalAccessCount,
255
- hitRate: this.cache.size > 0 ?
256
- Array.from(this.cache.values()).reduce((sum, item) => sum + item.accessCount, 0) / this.totalAccessCount : 0
356
+ hitRate,
357
+ // Memory management (v3.36.0+)
358
+ memory: {
359
+ available: this.memoryInfo.available,
360
+ source: this.memoryInfo.source,
361
+ isContainer: this.memoryInfo.isContainer,
362
+ systemTotal: this.memoryInfo.systemTotal,
363
+ allocationRatio: this.allocationStrategy.ratio,
364
+ environment: this.allocationStrategy.environment
365
+ }
366
+ };
367
+ }
368
+ /**
369
+ * Get detailed memory information
370
+ */
371
+ getMemoryInfo() {
372
+ return {
373
+ memoryInfo: { ...this.memoryInfo },
374
+ allocationStrategy: { ...this.allocationStrategy },
375
+ currentPressure: checkMemoryPressure(this.currentSize, this.memoryInfo)
257
376
  };
258
377
  }
259
378
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulcraft/brainy",
3
- "version": "3.35.0",
3
+ "version": "3.36.0",
4
4
  "description": "Universal Knowledge Protocol™ - World's first Triple Intelligence database unifying vector, graph, and document search in one API. 31 nouns × 40 verbs for infinite expressiveness.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",