You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

97 lines
2.0 KiB

  1. // Package ratelimiter implements the Leaky Bucket ratelimiting algorithm with memcached and in-memory backends.
  2. package ratelimiter
  3. import (
  4. "time"
  5. )
  6. type LeakyBucket struct {
  7. Size uint16
  8. Fill float64
  9. LeakInterval time.Duration // time.Duration for 1 unit of size to leak
  10. Lastupdate time.Time
  11. Now func() time.Time
  12. }
  13. func NewLeakyBucket(size uint16, leakInterval time.Duration) *LeakyBucket {
  14. bucket := LeakyBucket{
  15. Size: size,
  16. Fill: 0,
  17. LeakInterval: leakInterval,
  18. Now: time.Now,
  19. Lastupdate: time.Now(),
  20. }
  21. return &bucket
  22. }
  23. func (b *LeakyBucket) updateFill() {
  24. now := b.Now()
  25. if b.Fill > 0 {
  26. elapsed := now.Sub(b.Lastupdate)
  27. b.Fill -= float64(elapsed) / float64(b.LeakInterval)
  28. if b.Fill < 0 {
  29. b.Fill = 0
  30. }
  31. }
  32. b.Lastupdate = now
  33. }
  34. func (b *LeakyBucket) Pour(amount uint16) bool {
  35. b.updateFill()
  36. var newfill float64 = b.Fill + float64(amount)
  37. if newfill > float64(b.Size) {
  38. return false
  39. }
  40. b.Fill = newfill
  41. return true
  42. }
  43. // The time at which this bucket will be completely drained
  44. func (b *LeakyBucket) DrainedAt() time.Time {
  45. return b.Lastupdate.Add(time.Duration(b.Fill * float64(b.LeakInterval)))
  46. }
  47. // The duration until this bucket is completely drained
  48. func (b *LeakyBucket) TimeToDrain() time.Duration {
  49. return b.DrainedAt().Sub(b.Now())
  50. }
  51. func (b *LeakyBucket) TimeSinceLastUpdate() time.Duration {
  52. return b.Now().Sub(b.Lastupdate)
  53. }
  54. type LeakyBucketSer struct {
  55. Size uint16
  56. Fill float64
  57. LeakInterval time.Duration // time.Duration for 1 unit of size to leak
  58. Lastupdate time.Time
  59. }
  60. func (b *LeakyBucket) Serialise() *LeakyBucketSer {
  61. bucket := LeakyBucketSer{
  62. Size: b.Size,
  63. Fill: b.Fill,
  64. LeakInterval: b.LeakInterval,
  65. Lastupdate: b.Lastupdate,
  66. }
  67. return &bucket
  68. }
  69. func (b *LeakyBucketSer) DeSerialise() *LeakyBucket {
  70. bucket := LeakyBucket{
  71. Size: b.Size,
  72. Fill: b.Fill,
  73. LeakInterval: b.LeakInterval,
  74. Lastupdate: b.Lastupdate,
  75. Now: time.Now,
  76. }
  77. return &bucket
  78. }