Merge branch '11590-log-reuse'
[arvados.git] / sdk / go / dispatch / throttle.go
1 package dispatch
2
3 import (
4         "sync"
5         "time"
6 )
7
8 type throttleEnt struct {
9         last time.Time // last attempt that was allowed
10 }
11
12 type throttle struct {
13         hold      time.Duration
14         seen      map[string]*throttleEnt
15         updated   sync.Cond
16         setupOnce sync.Once
17         mtx       sync.Mutex
18 }
19
20 // Check checks whether there have been too many recent attempts with
21 // the given uuid, and returns true if it's OK to attempt [again] now.
22 func (t *throttle) Check(uuid string) bool {
23         if t.hold == 0 {
24                 return true
25         }
26         t.setupOnce.Do(t.setup)
27         t.mtx.Lock()
28         defer t.updated.Broadcast()
29         defer t.mtx.Unlock()
30         ent, ok := t.seen[uuid]
31         if !ok {
32                 t.seen[uuid] = &throttleEnt{last: time.Now()}
33                 return true
34         }
35         if time.Since(ent.last) < t.hold {
36                 return false
37         }
38         ent.last = time.Now()
39         return true
40 }
41
42 func (t *throttle) setup() {
43         t.seen = make(map[string]*throttleEnt)
44         t.updated.L = &t.mtx
45         go func() {
46                 for range time.NewTicker(t.hold).C {
47                         t.mtx.Lock()
48                         for uuid, ent := range t.seen {
49                                 if time.Since(ent.last) >= t.hold {
50                                         delete(t.seen, uuid)
51                                 }
52                         }
53                         // don't bother cleaning again until the next update
54                         t.updated.Wait()
55                         t.mtx.Unlock()
56                 }
57         }()
58 }