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.
Das Modul besitzt eine feste interne EventPayloadMap und speichert Zeitstempel für ein 60-Sekunden-Fenster. Bereinigung geschieht nur beim Loggen und getCount ist semantisch uneindeutig; Root-API, konfigurierbare Uhr und belastbare Tests fehlen.
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
log
Erfasst ein Ereignis mit dem aktuellen Timestamp.
getCount
Liest den aktuellen Wert aus dem 60-Sekunden-Fenster.
EventPayloadMap (intern)
Bindet bekannte Eventnamen an Payloadtypen.
05 · Build it yourself
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 du testen solltest
- 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.
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.
Für verteilte Systeme sind monotone Uhr, Aggregationsintervall und die Bedeutung von 'Rate' Teil des öffentlichen Vertrags.
08 · Weiterdenken