mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-27 05:24:34 -07:00
[Part 1/6] feat(telemetry): add rate limiter and high-water mark tracker with tests (#8110)
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* High-water mark tracker for memory metrics
|
||||
* Only triggers when memory usage increases by a significant threshold
|
||||
*/
|
||||
export class HighWaterMarkTracker {
|
||||
private waterMarks: Map<string, number> = new Map();
|
||||
private lastUpdateTimes: Map<string, number> = new Map();
|
||||
private readonly growthThresholdPercent: number;
|
||||
|
||||
constructor(growthThresholdPercent: number = 5) {
|
||||
if (growthThresholdPercent < 0) {
|
||||
throw new Error('growthThresholdPercent must be non-negative.');
|
||||
}
|
||||
this.growthThresholdPercent = growthThresholdPercent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current value represents a new high-water mark that should trigger recording
|
||||
* @param metricType - Type of metric (e.g., 'heap_used', 'rss')
|
||||
* @param currentValue - Current memory value in bytes
|
||||
* @returns true if this value should trigger a recording
|
||||
*/
|
||||
shouldRecordMetric(metricType: string, currentValue: number): boolean {
|
||||
const now = Date.now();
|
||||
// Track last seen time for cleanup regardless of whether we record
|
||||
this.lastUpdateTimes.set(metricType, now);
|
||||
// Get current high-water mark
|
||||
const currentWaterMark = this.waterMarks.get(metricType) || 0;
|
||||
|
||||
// For first measurement, always record
|
||||
if (currentWaterMark === 0) {
|
||||
this.waterMarks.set(metricType, currentValue);
|
||||
this.lastUpdateTimes.set(metricType, now);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if current value exceeds threshold
|
||||
const thresholdValue =
|
||||
currentWaterMark * (1 + this.growthThresholdPercent / 100);
|
||||
|
||||
if (currentValue > thresholdValue) {
|
||||
// Update high-water mark
|
||||
this.waterMarks.set(metricType, currentValue);
|
||||
this.lastUpdateTimes.set(metricType, now);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current high-water mark for a metric type
|
||||
*/
|
||||
getHighWaterMark(metricType: string): number {
|
||||
return this.waterMarks.get(metricType) || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all high-water marks
|
||||
*/
|
||||
getAllHighWaterMarks(): Record<string, number> {
|
||||
return Object.fromEntries(this.waterMarks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset high-water mark for a specific metric type
|
||||
*/
|
||||
resetHighWaterMark(metricType: string): void {
|
||||
this.waterMarks.delete(metricType);
|
||||
this.lastUpdateTimes.delete(metricType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all high-water marks
|
||||
*/
|
||||
resetAllHighWaterMarks(): void {
|
||||
this.waterMarks.clear();
|
||||
this.lastUpdateTimes.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove stale entries to avoid unbounded growth if metric types are variable.
|
||||
* Entries not updated within maxAgeMs will be removed.
|
||||
*/
|
||||
cleanup(maxAgeMs: number = 3600000): void {
|
||||
const cutoffTime = Date.now() - maxAgeMs;
|
||||
for (const [metricType, lastTime] of this.lastUpdateTimes.entries()) {
|
||||
if (lastTime < cutoffTime) {
|
||||
this.lastUpdateTimes.delete(metricType);
|
||||
this.waterMarks.delete(metricType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user