package accounting import "time" // Counts are one limiter's counts by counter: this minute's and the one // before, as absolute figures, so reporting them twice changes nothing. type Counts struct { Minute time.Time `json:"minute"` Current map[string]int64 `json:"current,omitempty"` Previous map[string]int64 `json:"previous,omitempty"` } // Add sums other into c, aligned to c's minute: counts of a minute that is // c's previous one land there, older ones are dropped. Clocks a second apart // at a minute's turn are why. func (c *Counts) Add(other Counts) { switch { case other.Minute.Equal(c.Minute): c.Current = addInto(c.Current, other.Current) c.Previous = addInto(c.Previous, other.Previous) case other.Minute.Add(windowLen).Equal(c.Minute): c.Previous = addInto(c.Previous, other.Current) } } func addInto(dst, src map[string]int64) map[string]int64 { if len(src) == 0 { return dst } if dst == nil { dst = make(map[string]int64, len(src)) } for k, v := range src { dst[k] += v } return dst } // Counts reads this limiter's own counts, without the others'. func (l *Limiter) Counts() Counts { now := l.now() out := Counts{Minute: now.Truncate(windowLen), Current: map[string]int64{}, Previous: map[string]int64{}} l.mu.Lock() defer l.mu.Unlock() for k, w := range l.keys { w.roll(now) if w.current > 0 { out.Current[k] = w.current } if w.previous > 0 { out.Previous[k] = w.previous } } return out } // SetOthers replaces what the other control planes have counted. A counter // they did not mention has nothing from them. func (l *Limiter) SetOthers(c Counts) { now := l.now() aligned := Counts{Minute: now.Truncate(windowLen)} aligned.Add(c) l.mu.Lock() defer l.mu.Unlock() for k, w := range l.keys { w.roll(now) w.othersCurrent, w.othersPrevious = aligned.Current[k], aligned.Previous[k] delete(aligned.Current, k) delete(aligned.Previous, k) } // Counters only others have used yet: a key spread over the cluster is // limited here from its first request. for k := range aligned.Current { w := l.windowLocked(k, now) w.othersCurrent, w.othersPrevious = aligned.Current[k], aligned.Previous[k] delete(aligned.Previous, k) } for k, v := range aligned.Previous { l.windowLocked(k, now).othersPrevious = v } } // Rates are an accountant's two limiters' counts. type Rates struct { Requests Counts `json:"requests"` Tokens Counts `json:"tokens"` } // Add sums other into r. func (r *Rates) Add(other Rates) { r.Requests.Add(other.Requests) r.Tokens.Add(other.Tokens) } // NewRates is an empty sum for the minute now is in. func NewRates(now time.Time) Rates { m := now.Truncate(windowLen) return Rates{Requests: Counts{Minute: m}, Tokens: Counts{Minute: m}} } // Rates reads this process's own request and token counts. func (a *Accountant) Rates() Rates { return Rates{Requests: a.requests.Counts(), Tokens: a.tokens.Counts()} } // SetOtherRates puts the other control planes' counts in force here. func (a *Accountant) SetOtherRates(r Rates) { a.requests.SetOthers(r.Requests) a.tokens.SetOthers(r.Tokens) }