- Worker-Loop: eine Ansage gleichzeitig; now > high > normal > low, FIFO je Level - Cancel bricht die laufende Ansage ab (ctx) und leert die Queue (Zahl als Rückgabe) - Close verwirft wartende Items, bricht die laufende Ansage ab und wartet auf den Worker - MuteStore: persistenter Mute-State als Datei mute im Config-Dir (atomar geschrieben) - RateLimiter: Struktur + Semantik für Task 05, bewusst noch nicht angebunden (Task-04-Default unbegrenzt, dokumentierte Seam in rate.go)
58 lines
1.7 KiB
Go
58 lines
1.7 KiB
Go
package queue
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// RateLimiter ist die Struktur für das Daemon-Gesetz gegen Ansage-Spam
|
|
// (Spec: Default ca. 1 Ansage / 30 s, konfigurierbar).
|
|
//
|
|
// SEAM FÜR TASK 05: Struktur und Semantik stehen und sind getestet, sind
|
|
// aber noch NICHT an den Worker-Loop angebunden — Task 04 erzeugt keinen
|
|
// Limiter (unbegrenzt, so verlangt es der Task-04-Brief: „Default unendlich
|
|
// in T04-Tests, dokumentiert"). Task 05 verdrahtet den Limiter (config
|
|
// queue.rate_limit) in den Loop und ergänzt die prioritätsbewusste
|
|
// Entscheidung: low verwerfen, normal parken, high konfigurierbar verstoßen
|
|
// (queue.allow_high_burst) — dafür ist Next() bereits Teil der Struktur.
|
|
type RateLimiter struct {
|
|
// Interval ist der Mindestabstand zwischen zwei Ansage-Starts.
|
|
// 0 oder negativ = unbegrenzt (Task-04-Default).
|
|
Interval time.Duration
|
|
|
|
mu sync.Mutex
|
|
last time.Time // Start der zuletzt per Reserve gebuchten Ansage
|
|
}
|
|
|
|
// Allow meldet, ob sofort eine Ansage starten dürfte.
|
|
func (l *RateLimiter) Allow() bool {
|
|
if l == nil || l.Interval <= 0 {
|
|
return true
|
|
}
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
return time.Since(l.last) >= l.Interval
|
|
}
|
|
|
|
// Reserve bucht einen Ansage-Slot (direkt nach Allow aufrufen).
|
|
func (l *RateLimiter) Reserve() {
|
|
if l == nil || l.Interval <= 0 {
|
|
return
|
|
}
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
l.last = time.Now()
|
|
}
|
|
|
|
// Next liefert, wann der nächste Ansage-Slot frei wird (Nullzeit, wenn
|
|
// Allow sofort true liefern würde) — Basis für das Parken wartender Items
|
|
// in Task 05.
|
|
func (l *RateLimiter) Next() time.Time {
|
|
if l == nil || l.Interval <= 0 {
|
|
return time.Time{}
|
|
}
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
return l.last.Add(l.Interval)
|
|
}
|