package calls import ( "sync" "time" ) // budget is a sliding-window counter: at most limit events per key per // window. It counts attempts, refused or not, because the point is to // keep outsider traffic away from rastrillo's shared limiter (spec // ยง3.2), and an attempt that reaches Begin is what consumes that. The // budgets are process-local and reset on every deploy or restart. type budget struct { mu sync.Mutex limit int window time.Duration hits map[string][]time.Time } func newBudget(limit int, window time.Duration) *budget { return &budget{limit: limit, window: window, hits: map[string][]time.Time{}} } // allow records an attempt and reports whether it was within budget. func (b *budget) allow(key string, now time.Time) bool { b.mu.Lock() defer b.mu.Unlock() cut := now.Add(-b.window) kept := b.hits[key][:0] for _, t := range b.hits[key] { if t.After(cut) { kept = append(kept, t) } } if len(kept) == 0 { delete(b.hits, key) } else { b.hits[key] = kept } if len(kept) >= b.limit { return false } b.hits[key] = append(kept, now) return true }