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.
 
 
 

63 lines
1.2 KiB

  1. package memory
  2. import (
  3. "sync"
  4. "time"
  5. )
  6. type token struct{}
  7. type bucketStore struct {
  8. sync.Mutex // guards buckets
  9. buckets map[string]chan token
  10. bucketLen int
  11. reset time.Time
  12. }
  13. // New creates new in-memory token bucket store.
  14. func New() *bucketStore {
  15. return &bucketStore{
  16. buckets: map[string]chan token{},
  17. }
  18. }
  19. func (s *bucketStore) InitRate(rate int, window time.Duration) {
  20. s.bucketLen = rate
  21. s.reset = time.Now()
  22. go func() {
  23. interval := time.Duration(int(window) / rate)
  24. tick := time.NewTicker(interval)
  25. for t := range tick.C {
  26. s.Lock()
  27. s.reset = t.Add(interval)
  28. for key, bucket := range s.buckets {
  29. select {
  30. case <-bucket:
  31. default:
  32. delete(s.buckets, key)
  33. }
  34. }
  35. s.Unlock()
  36. }
  37. }()
  38. }
  39. // Take implements TokenBucketStore interface. It takes token from a bucket
  40. // referenced by a given key, if available.
  41. func (s *bucketStore) Take(key string) (bool, int, time.Time, error) {
  42. s.Lock()
  43. bucket, ok := s.buckets[key]
  44. if !ok {
  45. bucket = make(chan token, s.bucketLen)
  46. s.buckets[key] = bucket
  47. }
  48. s.Unlock()
  49. select {
  50. case bucket <- token{}:
  51. return true, cap(bucket) - len(bucket), s.reset, nil
  52. default:
  53. return false, 0, s.reset, nil
  54. }
  55. }