01 · Problem
Wofür braucht man das?
Ein Gesamtzähler sagt nicht, ob Aktivität gerade stattfindet; für Raten und Bursts braucht man Werte in einem definierten Zeitfenster.
02 · Denkmodell
Das mentale Modell
Jedes Ereignis erhält einen Zeitstempel. Beim Lesen werden abgelaufene Einträge entfernt und die verbleibenden Einträge gezählt.
Misst Ereigniszahlen im gleitenden Fenster mit austauschbarer monotoner Uhr. Zähler betreffen das konfigurierte Fenster, nicht die gesamte Historie. Speicherbedarf wächst mit den Ereignissen im Fenster.
03 · Kontrollfluss
Was passiert in welcher Reihenfolge?
- Ereignisname und aktuellen Zeitpunkt erfassen.
- Zeitstempel in den Bucket des Ereignisses einfügen.
- Beim Schreiben und Lesen alle Werte vor der Fenstergrenze entfernen.
- Anzahl oder Rate aus dem verbleibenden Bucket ableiten.
04 · Bauteile
Die entscheidenden Verträge
logEventErfasst ein Ereignis mit der injizierten Uhr.getCount / getRatePerMinuteLiest aktuelle Fensterzähler oder normalisierte Raten.05 · Selbst bauen
Selbst implementieren
Mit injizierter Uhr bleibt ein gleitender Zähler klein und vollständig deterministisch testbar.
- Fensterlänge und now-Funktion beim Erzeugen entgegennehmen.
- Zeitstempel geordnet pro Ereignis speichern.
- Vor jeder Auswertung Einträge links bis zur Fenstergrenze entfernen.
class WindowCounter {
private points: number[] = [];
constructor(private windowMs: number, private now = Date.now) {}
add() { this.points.push(this.now()); }
count() {
const cutoff = this.now() - this.windowMs;
while (this.points[0] < cutoff) this.points.shift();
return this.points.length;
}
}06 · Verifizieren
Was solltest du testen?
- Einträge genau an der Fenstergrenze folgen der dokumentierten inklusiv/exklusiv-Regel.
- Eine vorgerückte Fake-Uhr entfernt abgelaufene Einträge auch ohne neues Ereignis.
- Getrennte Ereignisbuckets beeinflussen einander nicht.
Selbst ausprobieren
Formuliere die erste Prüfaussage als fehlschlagenden Test, implementiere die kleinste Korrektur und ergänze einen Grenzfall.
Einträge genau an der Fenstergrenze folgen der dokumentierten inklusiv/exklusiv-Regel.
Ansatz vergleichen
Dein Test sollte beim gezielten Verletzen der Invariante fehlschlagen und nach der Korrektur wieder bestehen. Halte den Aufbau deterministisch und prüfe ein beobachtbares Ergebnis.
07 · Grenzen
Kompromisse und Stolperfallen
- Timestamp-Listen sind exakt, benötigen bei hohem Volumen jedoch viel Speicher.
- Zeit-Buckets sparen Speicher und liefern dafür nur angenäherte Grenzen.
- Lazy Cleanup vermeidet Timer, verlagert die Arbeit aber auf Lesezugriffe.
Zähler betreffen das konfigurierte Fenster, nicht die gesamte Historie. Speicherbedarf wächst mit den Ereignissen im Fenster.
08 · Weiterdenken
Quellcode und verwandte Ideen
yarn tsx packages/stats-tracker/examples/basic.ts