| // 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 |
| } |