Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
package processing
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// StartLimiter lightly rate-limits processing job starts per company.
|
||||
// In-process only — not shared across API replicas (effective RPM ≈ N × replicas).
|
||||
// RATE_LIMIT_REPLICAS does not divide this limiter; multi-replica hard caps need edge/WAF.
|
||||
// Counts StartJob/RetryJob API calls once each — not per auto-split sibling job,
|
||||
// and not per product in a bulk StartJob payload (capped by MaxStartProducts).
|
||||
type StartLimiter struct {
|
||||
mu sync.Mutex
|
||||
window time.Duration
|
||||
max int
|
||||
events map[uuid.UUID][]time.Time
|
||||
}
|
||||
|
||||
// NewStartLimiter allows maxStarts per window (e.g. 20/min).
|
||||
func NewStartLimiter(maxStarts int, window time.Duration) *StartLimiter {
|
||||
if maxStarts <= 0 {
|
||||
maxStarts = 20
|
||||
}
|
||||
if window <= 0 {
|
||||
window = time.Minute
|
||||
}
|
||||
return &StartLimiter{
|
||||
window: window,
|
||||
max: maxStarts,
|
||||
events: make(map[uuid.UUID][]time.Time),
|
||||
}
|
||||
}
|
||||
|
||||
// Allow reports whether a new job start is permitted for companyID.
|
||||
func (l *StartLimiter) Allow(companyID uuid.UUID) bool {
|
||||
if l == nil {
|
||||
return true
|
||||
}
|
||||
now := time.Now()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
cut := now.Add(-l.window)
|
||||
ev := l.events[companyID]
|
||||
kept := ev[:0]
|
||||
for _, t := range ev {
|
||||
if t.After(cut) {
|
||||
kept = append(kept, t)
|
||||
}
|
||||
}
|
||||
if len(kept) >= l.max {
|
||||
l.events[companyID] = kept
|
||||
return false
|
||||
}
|
||||
kept = append(kept, now)
|
||||
l.events[companyID] = kept
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user