98 lines
2.0 KiB
Go
98 lines
2.0 KiB
Go
// Package ratelimiter implements the Leaky Bucket ratelimiting algorithm with memcached and in-memory backends.
|
|
package ratelimiter
|
|
|
|
import (
|
|
"time"
|
|
)
|
|
|
|
type LeakyBucket struct {
|
|
Size uint16
|
|
Fill float64
|
|
LeakInterval time.Duration // time.Duration for 1 unit of size to leak
|
|
Lastupdate time.Time
|
|
Now func() time.Time
|
|
}
|
|
|
|
func NewLeakyBucket(size uint16, leakInterval time.Duration) *LeakyBucket {
|
|
bucket := LeakyBucket{
|
|
Size: size,
|
|
Fill: 0,
|
|
LeakInterval: leakInterval,
|
|
Now: time.Now,
|
|
Lastupdate: time.Now(),
|
|
}
|
|
|
|
return &bucket
|
|
}
|
|
|
|
func (b *LeakyBucket) updateFill() {
|
|
now := b.Now()
|
|
if b.Fill > 0 {
|
|
elapsed := now.Sub(b.Lastupdate)
|
|
|
|
b.Fill -= float64(elapsed) / float64(b.LeakInterval)
|
|
if b.Fill < 0 {
|
|
b.Fill = 0
|
|
}
|
|
}
|
|
b.Lastupdate = now
|
|
}
|
|
|
|
func (b *LeakyBucket) Pour(amount uint16) bool {
|
|
b.updateFill()
|
|
|
|
var newfill float64 = b.Fill + float64(amount)
|
|
|
|
if newfill > float64(b.Size) {
|
|
return false
|
|
}
|
|
|
|
b.Fill = newfill
|
|
|
|
return true
|
|
}
|
|
|
|
// The time at which this bucket will be completely drained
|
|
func (b *LeakyBucket) DrainedAt() time.Time {
|
|
return b.Lastupdate.Add(time.Duration(b.Fill * float64(b.LeakInterval)))
|
|
}
|
|
|
|
// The duration until this bucket is completely drained
|
|
func (b *LeakyBucket) TimeToDrain() time.Duration {
|
|
return b.DrainedAt().Sub(b.Now())
|
|
}
|
|
|
|
func (b *LeakyBucket) TimeSinceLastUpdate() time.Duration {
|
|
return b.Now().Sub(b.Lastupdate)
|
|
}
|
|
|
|
type LeakyBucketSer struct {
|
|
Size uint16
|
|
Fill float64
|
|
LeakInterval time.Duration // time.Duration for 1 unit of size to leak
|
|
Lastupdate time.Time
|
|
}
|
|
|
|
func (b *LeakyBucket) Serialise() *LeakyBucketSer {
|
|
bucket := LeakyBucketSer{
|
|
Size: b.Size,
|
|
Fill: b.Fill,
|
|
LeakInterval: b.LeakInterval,
|
|
Lastupdate: b.Lastupdate,
|
|
}
|
|
|
|
return &bucket
|
|
}
|
|
|
|
func (b *LeakyBucketSer) DeSerialise() *LeakyBucket {
|
|
bucket := LeakyBucket{
|
|
Size: b.Size,
|
|
Fill: b.Fill,
|
|
LeakInterval: b.LeakInterval,
|
|
Lastupdate: b.Lastupdate,
|
|
Now: time.Now,
|
|
}
|
|
|
|
return &bucket
|
|
}
|