v1.0.19
This commit is contained in:
1 parent
4b430340db
commit
9c451aedf9
120 files changed
+7777
-6046
No files matched your search
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* In-Memory Metrics Ring Buffer
|
||||
*
|
||||
* Replaces SQLite/PostgreSQL host_metrics storage with a fixed-size
|
||||
* in-memory circular buffer per environment. Uses pre-allocated arrays
|
||||
* with head/count indices to avoid splice()-based eviction which causes
|
||||
* V8 to repeatedly reallocate backing arrays.
|
||||
*
|
||||
* Memory: 16 envs × 360 slots × ~100 bytes ≈ 576 KB
|
||||
*/
|
||||
|
||||
export interface MetricPoint {
|
||||
id: number;
|
||||
cpuPercent: number;
|
||||
memoryPercent: number;
|
||||
memoryUsed: number;
|
||||
memoryTotal: number;
|
||||
environmentId: number | null;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
const MAX_POINTS_PER_ENV = 360; // 1 hour at 10s interval, 3 hours at 30s
|
||||
|
||||
interface RingBuffer {
|
||||
data: (MetricPoint | null)[];
|
||||
head: number; // next write position
|
||||
count: number; // number of valid entries (≤ MAX_POINTS_PER_ENV)
|
||||
}
|
||||
|
||||
// envId → RingBuffer
|
||||
const store = new Map<number, RingBuffer>();
|
||||
|
||||
let nextId = 1;
|
||||
|
||||
/**
|
||||
* Push a new metric data point for an environment.
|
||||
* Overwrites oldest entry when buffer is full (no array reallocation).
|
||||
*/
|
||||
export function pushMetric(
|
||||
envId: number,
|
||||
cpuPercent: number,
|
||||
memoryPercent: number,
|
||||
memoryUsed: number,
|
||||
memoryTotal: number
|
||||
): void {
|
||||
let ring = store.get(envId);
|
||||
if (!ring) {
|
||||
ring = { data: new Array(MAX_POINTS_PER_ENV).fill(null), head: 0, count: 0 };
|
||||
store.set(envId, ring);
|
||||
}
|
||||
|
||||
ring.data[ring.head] = {
|
||||
id: nextId++,
|
||||
cpuPercent,
|
||||
memoryPercent,
|
||||
memoryUsed,
|
||||
memoryTotal,
|
||||
environmentId: envId,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
ring.head = (ring.head + 1) % MAX_POINTS_PER_ENV;
|
||||
if (ring.count < MAX_POINTS_PER_ENV) ring.count++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read entries from a ring buffer in oldest-first order.
|
||||
*/
|
||||
function readRing(ring: RingBuffer, limit: number): MetricPoint[] {
|
||||
const count = Math.min(ring.count, limit);
|
||||
if (count === 0) return [];
|
||||
|
||||
const result: MetricPoint[] = new Array(count);
|
||||
// Start reading from the oldest entry
|
||||
const start = (ring.head - ring.count + MAX_POINTS_PER_ENV) % MAX_POINTS_PER_ENV;
|
||||
const skip = ring.count - count;
|
||||
const readFrom = (start + skip) % MAX_POINTS_PER_ENV;
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
result[i] = ring.data[(readFrom + i) % MAX_POINTS_PER_ENV]!;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recent metric for an environment.
|
||||
*/
|
||||
export function getLatestMetric(envId: number): MetricPoint | null {
|
||||
const ring = store.get(envId);
|
||||
if (!ring || ring.count === 0) return null;
|
||||
// head points to next write position, so latest is head - 1
|
||||
const idx = (ring.head - 1 + MAX_POINTS_PER_ENV) % MAX_POINTS_PER_ENV;
|
||||
return ring.data[idx];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metrics history for an environment, oldest first.
|
||||
*/
|
||||
export function getMetricsHistory(envId: number, limit = 60): MetricPoint[] {
|
||||
const ring = store.get(envId);
|
||||
if (!ring || ring.count === 0) return [];
|
||||
return readRing(ring, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all metrics (across all environments), newest first, with optional limit.
|
||||
* Used by the global getHostMetrics() fallback when no envId is specified.
|
||||
*/
|
||||
export function getAllMetrics(limit = 60): MetricPoint[] {
|
||||
const all: MetricPoint[] = [];
|
||||
for (const ring of store.values()) {
|
||||
const points = readRing(ring, ring.count);
|
||||
all.push(...points);
|
||||
}
|
||||
// Sort newest first (matching old DB query behavior)
|
||||
all.sort((a, b) => b.id - a.id);
|
||||
return all.slice(0, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all metrics for an environment (e.g., when environment is deleted).
|
||||
*/
|
||||
export function clearEnvironmentMetrics(envId: number): void {
|
||||
store.delete(envId);
|
||||
}
|
||||
Reference in new issue
Block a user