Skip to content

Commit

Permalink
fix(ratelimit): ratelimiter's unexpected behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
hugefiver committed Jul 9, 2024
1 parent eb7eb44 commit 2eee3e3
Showing 1 changed file with 44 additions and 1 deletion.
45 changes: 44 additions & 1 deletion rate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"hash/maphash"
"reflect"
"time"

"github.com/cespare/xxhash/v2"
Expand All @@ -10,6 +11,48 @@ import (
"golang.org/x/time/rate"
)

var rateLimiterPrivateReserveNMethodFunc reflect.Value

func init() {
rl := &rate.Limiter{}
t := reflect.TypeOf(rl)
m, ok := t.MethodByName("reserveN")
if !ok {
panic(`cannot get "reserveN" method in "rate.Limiter"`)
}

mt := m.Type
if mt.NumIn() != 4 || mt.NumOut() != 1 {
panic(`"reserveN" method in "rate.Limiter" has wrong signature: ` + mt.String())
}

if in := mt.In(0); in != reflect.TypeFor[*rate.Limiter]() {
panic(`"reserveN" method in "rate.Limiter": argument 0 must be "rate.Limiter", but got ` + in.String())
}
if in := mt.In(1); in != reflect.TypeFor[time.Time]() {
panic(`"reserveN" method in "rate.Limiter": argument 1 must be "time.Time", but got ` + in.String())
}
if in := mt.In(2); in != reflect.TypeFor[int]() {
panic(`"reserveN" method in "rate.Limiter": argument 2 must be "int", but got ` + in.String())
}
if in := mt.In(3); in != reflect.TypeFor[time.Duration]() {
panic(`"reserveN" method in "rate.Limiter": argument 3 must be "time.Duration", but got ` + in.String())
}
if out := mt.Out(0); out != reflect.TypeFor[rate.Reservation]() {
panic(`"reserveN" method in "rate.Limiter": return value must be "rate.Reservation", but got ` + out.String())
}

fn := m.Func
rateLimiterPrivateReserveNMethodFunc = fn
}

func callRateLimiterReserveN(r *rate.Limiter, t time.Time, n int, maxFutureReserve time.Duration) *rate.Reservation {
ret := rateLimiterPrivateReserveNMethodFunc.Call([]reflect.Value{reflect.ValueOf(r), reflect.ValueOf(t), reflect.ValueOf(n), reflect.ValueOf(maxFutureReserve)})[0]

x := ret.Interface().(rate.Reservation)
return &x
}

type RateLimiter struct {
limiters []*rate.Limiter
}
Expand Down Expand Up @@ -62,7 +105,7 @@ func (r *RateLimiter) AllowN(n int) Reservation {
var taken []*rate.Reservation
now := time.Now()
for _, l := range r.limiters {
if rsv := l.ReserveN(now, n); !rsv.OK() {
if rsv := callRateLimiterReserveN(l, now, n, 0); !rsv.OK() {
for _, x := range taken {
x.CancelAt(now)
}
Expand Down

0 comments on commit 2eee3e3

Please sign in to comment.