49 lines
1.4 KiB
Go
49 lines
1.4 KiB
Go
// cleanup.go — background expiration of temporary whitelisted IPs
|
|
//
|
|
// The cleanup goroutine runs every cfg.CleanupInterval and removes entries
|
|
// from the temporary whitelist whose TTL has elapsed.
|
|
//
|
|
// Why not use a persistent store (Redis, SQLite)?
|
|
//
|
|
// - TTL-based expiry with a map + periodic cleanup is simpler and has
|
|
// no external dependencies.
|
|
// - For most use cases (a handful of IPs), this is perfectly adequate.
|
|
// - If you need persistence across restarts, add a disk-backed store later.
|
|
//
|
|
// Why a goroutine instead of checking expiry on every request?
|
|
//
|
|
// - Checking expiry on every request adds latency (two map lookups per request).
|
|
// - The cleanup goroutine is a one-time cost that keeps the hot path fast.
|
|
// - The cleanup runs in a separate goroutine so it doesn't block requests.
|
|
|
|
package main
|
|
|
|
import (
|
|
"log"
|
|
"time"
|
|
)
|
|
|
|
// cleanupLoop runs a periodic cleanup of the temporary whitelist.
|
|
//
|
|
// It runs every d seconds and removes entries that have expired.
|
|
// The loop stops when the context is cancelled.
|
|
func cleanupLoop(tw *tempWhitelist, d time.Duration) {
|
|
ticker := time.NewTicker(d)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
now := time.Now()
|
|
tw.mu.Lock()
|
|
for ip, entry := range tw.entries {
|
|
if entry.Expires.Before(now) {
|
|
log.Printf("expired temporary whitelist for %s", ip)
|
|
delete(tw.entries, ip)
|
|
}
|
|
}
|
|
tw.mu.Unlock()
|
|
}
|
|
}
|
|
}
|