feat(queue): serialisierte Ansage-Queue mit Prioritäten, now-Sprung, Mute-Store und Rate-Limit-Struktur
- 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)
This commit is contained in:
1 parent
716f6b2fc9
commit
e492baddac
5 files changed
+742
-3
No files matched your search
@@ -0,0 +1,57 @@
|
||||
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)
|
||||
}
|
||||
Reference in new issue
Block a user