Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
61 lines
1.4 KiB
Go
61 lines
1.4 KiB
Go
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
|
||
}
|