-
Notifications
You must be signed in to change notification settings - Fork 18
Closed
Description
Only storing/retrieving the values is thread safe. The limiting itself is NOT thread safe.
https://github.com/bsm/ratelimit/blob/main/ratelimit.go#L65-L71
This part is a race condition as different threads might do the second AddUint64 on a value from another thread.
Proof of concept:
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
"github.com/bsm/ratelimit"
)
func main() {
rl := ratelimit.New(10, time.Second)
accepted := atomic.Int64{}
wg := sync.WaitGroup{}
const threads = 200000
wg.Add(threads)
start := time.Now()
for i := 0; i <threads; i++ {
go func() {
if !rl.Limit() {
accepted.Add(1)
}
wg.Done()
}()
}
wg.Wait()
fmt.Println("took:", time.Since(start))
fmt.Println("accepted:", accepted.Load())
}go run -race main.go (-race is only used to get a janky execution and not an ultra fast linear execution)
Will lead to results as
$ go run -race main.go
took: 256.926726ms
accepted: 23
$ go run -race main.go
took: 263.227221ms
accepted: 12
$ go run -race main.go
took: 304.22349ms
accepted: 13
And the values are far below one second and above the chosen rate limit of 10/s.
vintorez
Metadata
Metadata
Assignees
Labels
No labels