fix(cabana): scope the token last_used_at throttle to the guard instance

- The process-global debounce map was keyed by token ID, so a stamp from one test database suppressed a later write in another and turned TestBackendTokenPostgres red depending on order
- Each compositeGuard now owns a stampThrottle; the backend guard and the ResolveBackend guard of one activation share it, so a single-app binary still writes last_used_at at most once a minute per token
- TestTokenLastUsedThrottlePerGuard pins the per-guard isolation
This commit is contained in:
2026-10-11 15:19:55 +02:00
parent 21fdcfcba6
commit b45af8ebbf
5 changed files with 74 additions and 15 deletions

View File

@@ -270,20 +270,36 @@ func (s gormTokenStore) StampLastUsed(ctx context.Context, id uint, now time.Tim
).Error
}
// lastStamp gates last_used_at writes to at most once per minute per token id
// in this process; the SQL UPDATE is additionally conditional.
var lastStamp sync.Map // uint -> time.Time
// stampThrottle gates last_used_at writes to at most once per minute per token
// ID per guard instance; one instance serves a cabana activation (the backend
// guard and the ResolveBackend guard share it). The SQL UPDATE is additionally
// conditional.
type stampThrottle struct {
last sync.Map // uint -> time.Time
}
func stampLastUsed(ctx context.Context, store tokenStore, id uint, now time.Time) {
// due reports whether id may be stamped at now and records the stamp if so.
// A nil throttle never debounces.
func (t *stampThrottle) due(id uint, now time.Time) bool {
if t == nil {
return true
}
if v, ok := t.last.Load(id); ok {
if now.Sub(v.(time.Time)) < time.Minute {
return false
}
}
t.last.Store(id, now)
return true
}
func stampLastUsed(ctx context.Context, stamps *stampThrottle, store tokenStore, id uint, now time.Time) {
if store == nil || id == 0 {
return
}
if v, ok := lastStamp.Load(id); ok {
if now.Sub(v.(time.Time)) < time.Minute {
return
}
if !stamps.due(id, now) {
return
}
lastStamp.Store(id, now)
if err := store.StampLastUsed(ctx, id, now); err != nil {
slog.Default().WarnContext(ctx, "cabana: token last_used_at stamp failed", "token_id", id, "error", err)
}