stats/basic.go (90 lines of code) (raw):

// Copyright 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stats import ( "flag" "math" "sync" "github.com/GoogleCloudPlatform/ubbagent/clock" "github.com/golang/glog" ) // The maximum number of pending sends to track before old items are dropped. var maxPendingSends = flag.Int("max_pending_sends", 1000, "maximum number of pending sends that are tracked for agent stats") // Basic is a stats.Recorder and stats.Provider that records and provides stats.Snapshot values. // Storage is in-memory and all stats are reset when the agent is restarted. type Basic struct { clock clock.Clock mutex sync.RWMutex pending map[string]*pendingSend pendingCount int64 current Snapshot } func (s *Basic) Register(id string, handlers []string) { s.mutex.Lock() defer s.mutex.Unlock() s.pendingCount++ s.pending[id] = newPendingSend(handlers, s.pendingCount) // Trim the pending set if necessary if len(s.pending) > *maxPendingSends { oldestKey := "" var oldestOrder int64 = math.MaxInt64 for k, v := range s.pending { if v.order < oldestOrder { oldestKey = k oldestOrder = v.order } } if oldestKey != "" { glog.Warningf("stats.Basic: too many pending sends; deleting send %v", oldestKey) delete(s.pending, oldestKey) } } } func (s *Basic) SendSucceeded(id string, handler string) { s.mutex.Lock() defer s.mutex.Unlock() p, exists := s.pending[id] if !exists { // This might happen if the set of pending sends grows to large and older sends are dropped, or // if part of a send succeeded after the agent was restarted. glog.Warningf("stats.Basic: ignoring SendSucceeded from handler %v of unknown report id %v", handler, id) return } p.handlerSuccess(handler) if p.isSuccessful() { delete(s.pending, id) // Reset the "current" failure count: the number of failures since the last success s.current.CurrentFailureCount = 0 // Set the last success time s.current.LastReportSuccess = s.clock.Now() } } func (s *Basic) SendFailed(id string, handler string) { s.mutex.Lock() defer s.mutex.Unlock() // One or more failures means the full send failed. So we remove the pendingSend and increment // the failure count. if _, exists := s.pending[id]; exists { delete(s.pending, id) s.current.CurrentFailureCount++ s.current.TotalFailureCount++ } else { glog.Warningf("stats.Basic: ignoring SendFailed from handler %v of unknown report id %v", handler, id) } } func (s *Basic) Snapshot() Snapshot { s.mutex.RLock() defer s.mutex.RUnlock() return s.current } func NewBasic() *Basic { return newBasic(clock.NewClock()) } func newBasic(clock clock.Clock) *Basic { return &Basic{pending: make(map[string]*pendingSend), clock: clock} } type pendingSend struct { handlers map[string]bool order int64 } func (ps *pendingSend) handlerSuccess(handler string) { delete(ps.handlers, handler) } func (ps *pendingSend) isSuccessful() bool { return len(ps.handlers) == 0 } func newPendingSend(handlers []string, order int64) *pendingSend { hm := make(map[string]bool) for _, h := range handlers { hm[h] = true } return &pendingSend{hm, order} }