feat(common.ratelimiter): Implement means to reserve memory for concurrent use (#16867)

This commit is contained in:
Sven Rebhan 2025-04-23 20:14:06 +02:00 committed by GitHub
parent 4f54548577
commit 3feca5650f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 31 additions and 2 deletions

View File

@ -3,6 +3,7 @@ package ratelimiter
import ( import (
"errors" "errors"
"math" "math"
"sync"
"time" "time"
) )
@ -14,10 +15,17 @@ type RateLimiter struct {
limit int64 limit int64
period time.Duration period time.Duration
periodStart time.Time periodStart time.Time
remaining int64 remaining int64
reserved int64
sync.Mutex
} }
func (r *RateLimiter) Remaining(t time.Time) int64 { func (r *RateLimiter) Remaining(t time.Time) int64 {
r.Lock()
defer r.Unlock()
if r.limit == 0 { if r.limit == 0 {
return math.MaxInt64 return math.MaxInt64
} }
@ -33,10 +41,27 @@ func (r *RateLimiter) Remaining(t time.Time) int64 {
return r.limit return r.limit
} }
return r.remaining return r.remaining - r.reserved
}
func (r *RateLimiter) Reserve(used int64) {
r.Lock()
defer r.Unlock()
r.reserved = max(r.reserved+used, used)
}
func (r *RateLimiter) Release() {
r.Lock()
defer r.Unlock()
r.reserved = 0
} }
func (r *RateLimiter) Accept(t time.Time, used int64) { func (r *RateLimiter) Accept(t time.Time, used int64) {
r.Lock()
defer r.Unlock()
if r.limit == 0 || r.periodStart.After(t) { if r.limit == 0 || r.periodStart.After(t) {
return return
} }
@ -52,9 +77,13 @@ func (r *RateLimiter) Accept(t time.Time, used int64) {
// Update the state // Update the state
r.remaining = max(r.remaining-used, 0) r.remaining = max(r.remaining-used, 0)
r.reserved = max(r.reserved-used, 0)
} }
func (r *RateLimiter) Undo(t time.Time, used int64) { func (r *RateLimiter) Undo(t time.Time, used int64) {
r.Lock()
defer r.Unlock()
// Do nothing if we are not in the current period or unlimited because we // Do nothing if we are not in the current period or unlimited because we
// already reset the limit on a new window. // already reset the limit on a new window.
if r.limit == 0 || r.periodStart.IsZero() || r.periodStart.After(t) || t.Sub(r.periodStart) >= r.period { if r.limit == 0 || r.periodStart.IsZero() || r.periodStart.After(t) || t.Sub(r.periodStart) >= r.period {