muaddib-scanner 2.10.79 → 2.10.80

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muaddib-scanner",
3
- "version": "2.10.79",
3
+ "version": "2.10.80",
4
4
  "description": "Supply-chain threat detection & response for npm & PyPI/Python",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Adjusts target concurrency every ADJUST_INTERVAL_MS based on three signals:
7
7
  * 1. Queue depth — scale up when backlog grows, down when idle
8
- * 2. Memory pressure — always reduce under heap pressure
8
+ * 2. Memory pressure — always reduce under system RAM pressure
9
9
  * 3. Timeout rate — reduce when system is saturated (I/O contention)
10
10
  *
11
11
  * Scale-up is aggressive (+4) because backlog = lost coverage.
@@ -13,6 +13,8 @@
13
13
  * Memory pressure overrides everything (OOM kills lose the in-memory queue).
14
14
  */
15
15
 
16
+ const os = require('os');
17
+
16
18
  const MIN_CONCURRENCY = 4;
17
19
  const BASE_CONCURRENCY = Math.max(MIN_CONCURRENCY, parseInt(process.env.MUADDIB_SCAN_CONCURRENCY, 10) || 8);
18
20
  const MAX_CONCURRENCY = Math.max(BASE_CONCURRENCY, parseInt(process.env.MUADDIB_MAX_CONCURRENCY, 10) || 32);
@@ -23,7 +25,11 @@ const QUEUE_BACKLOG_THRESHOLD = 1000;
23
25
  const QUEUE_IDLE_THRESHOLD = 100;
24
26
 
25
27
  // System pressure thresholds
26
- const MEMORY_PRESSURE_THRESHOLD = 0.75;
28
+ // Uses os.freemem()/os.totalmem() (real system RAM), NOT process.memoryUsage()
29
+ // heapUsed/heapTotal. V8 adjusts heapTotal dynamically — the ratio is structurally
30
+ // 75-85% even when the OS has 8+ GB free. On a 12GB VPS with 3MB heap,
31
+ // heapUsed/heapTotal = 76% but freemem/totalmem = 75% (8.3GB available).
32
+ const MEMORY_FREE_THRESHOLD = 0.15; // < 15% system RAM free = pressure
27
33
  const TIMEOUT_RATE_THRESHOLD = 0.15;
28
34
  const TIMEOUT_RATE_MIN_SAMPLES = 20;
29
35
 
@@ -41,15 +47,18 @@ let _prevTimeouts = 0;
41
47
  * @returns {{ target: number, reason: string }}
42
48
  */
43
49
  function computeTarget(current, queueDepth, stats) {
44
- const mem = process.memoryUsage();
45
- const memPressure = mem.heapUsed / mem.heapTotal;
50
+ // Use system RAM, not V8 heap ratio (see MEMORY_FREE_THRESHOLD comment above)
51
+ const freeMem = os.freemem();
52
+ const totalMem = os.totalmem();
53
+ const freeRatio = totalMem > 0 ? freeMem / totalMem : 1;
46
54
 
47
- // Priority 1: Memory pressure — always reduce, overrides everything
48
- if (memPressure > MEMORY_PRESSURE_THRESHOLD) {
55
+ // Priority 1: System memory pressure — always reduce, overrides everything
56
+ if (freeRatio < MEMORY_FREE_THRESHOLD) {
49
57
  const target = clamp(current - 4);
50
58
  _prevScanned = stats.scanned || 0;
51
59
  _prevTimeouts = (stats.errorsByType && stats.errorsByType.static_timeout) || 0;
52
- return { target, reason: `memory_pressure (${(memPressure * 100).toFixed(0)}%)` };
60
+ const freeMB = Math.round(freeMem / 1024 / 1024);
61
+ return { target, reason: `memory_pressure (${freeMB}MB free, ${(freeRatio * 100).toFixed(0)}%)` };
53
62
  }
54
63
 
55
64
  // Compute timeout rate from stats deltas (sliding window between adjustments)