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:
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user