github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/metricsmanager.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "gopkg.in/mgo.v2" 11 "gopkg.in/mgo.v2/bson" 12 "gopkg.in/mgo.v2/txn" 13 ) 14 15 const ( 16 defaultGracePeriod = 7 * 24 * time.Hour // 1 week in hours 17 metricsManagerConsecutiveErrorThreshold = 3 18 metricsManagerKey = "metricsManagerKey" 19 ) 20 21 // MetricsManager stores data about the state of the metrics manager 22 type MetricsManager struct { 23 st *State 24 doc metricsManagerDoc 25 } 26 27 type metricsManagerDoc struct { 28 LastSuccessfulSend time.Time `bson:"lastsuccessfulsend"` 29 ConsecutiveErrors int `bson:"consecutiveerrors"` 30 GracePeriod time.Duration `bson:"graceperiod"` 31 } 32 33 // LastSuccessfulSend returns the time of the last successful send. 34 func (m *MetricsManager) LastSuccessfulSend() time.Time { 35 return m.doc.LastSuccessfulSend 36 } 37 38 // ConsecutiveErrors returns the number of consecutive failures. 39 func (m *MetricsManager) ConsecutiveErrors() int { 40 return m.doc.ConsecutiveErrors 41 } 42 43 // GracePeriod returns the current grace period. 44 func (m *MetricsManager) GracePeriod() time.Duration { 45 return m.doc.GracePeriod 46 } 47 48 // MetricsManager returns an existing metricsmanager, or a new one if non exists. 49 func (st *State) MetricsManager() (*MetricsManager, error) { 50 mm, err := st.getMetricsManager() 51 if errors.IsNotFound(err) { 52 return st.newMetricsManager() 53 } else if err != nil { 54 return nil, errors.Trace(err) 55 } 56 return mm, nil 57 } 58 59 func (st *State) newMetricsManager() (*MetricsManager, error) { 60 mm := &MetricsManager{ 61 st: st, 62 doc: metricsManagerDoc{ 63 LastSuccessfulSend: time.Time{}, 64 ConsecutiveErrors: 0, 65 GracePeriod: defaultGracePeriod, 66 }} 67 ops := []txn.Op{{ 68 C: metricsManagerC, 69 Id: metricsManagerKey, 70 Assert: txn.DocMissing, 71 Insert: mm.doc, 72 }} 73 err := st.runTransaction(ops) 74 if err != nil { 75 return nil, onAbort(err, errors.NotFoundf("metrics manager")) 76 } 77 return mm, nil 78 } 79 80 func (st *State) getMetricsManager() (*MetricsManager, error) { 81 coll, closer := st.getCollection(metricsManagerC) 82 defer closer() 83 var doc metricsManagerDoc 84 err := coll.FindId(metricsManagerKey).One(&doc) 85 if err == mgo.ErrNotFound { 86 return nil, errors.NotFoundf("metrics manager") 87 } else if err != nil { 88 return nil, errors.Trace(err) 89 } 90 return &MetricsManager{st: st, doc: doc}, nil 91 } 92 93 func (m *MetricsManager) updateMetricsManager(update bson.M) error { 94 ops := []txn.Op{{ 95 C: metricsManagerC, 96 Id: metricsManagerKey, 97 Assert: txn.DocExists, 98 Update: update, 99 }} 100 err := m.st.runTransaction(ops) 101 if err == txn.ErrAborted { 102 err = errors.NotFoundf("metrics manager") 103 } 104 if err != nil { 105 return errors.Trace(err) 106 } 107 return nil 108 } 109 110 // SetLastSuccessfulSend sets the last successful send time to the input time. 111 func (m *MetricsManager) SetLastSuccessfulSend(t time.Time) error { 112 err := m.updateMetricsManager( 113 bson.M{"$set": bson.M{ 114 "lastsuccessfulsend": t.UTC(), 115 "consecutiveerrors": 0, 116 }}, 117 ) 118 if err != nil { 119 return errors.Trace(err) 120 } 121 m.doc.LastSuccessfulSend = t.UTC() 122 m.doc.ConsecutiveErrors = 0 123 return nil 124 } 125 126 func (m *MetricsManager) SetGracePeriod(t time.Duration) error { 127 if t < 0 { 128 return errors.New("grace period can't be negative") 129 } 130 err := m.updateMetricsManager( 131 bson.M{"$set": bson.M{ 132 "graceperiod": t, 133 }}, 134 ) 135 if err != nil { 136 return errors.Trace(err) 137 } 138 m.doc.GracePeriod = t 139 return nil 140 } 141 142 // IncrementConsecutiveErrors adds 1 to the consecutive errors count. 143 func (m *MetricsManager) IncrementConsecutiveErrors() error { 144 err := m.updateMetricsManager( 145 bson.M{"$inc": bson.M{"consecutiveerrors": 1}}, 146 ) 147 if err != nil { 148 return errors.Trace(err) 149 } 150 m.doc.ConsecutiveErrors++ 151 return nil 152 } 153 154 func (m *MetricsManager) gracePeriodExceeded() bool { 155 now := m.st.clock.Now() 156 t := m.LastSuccessfulSend().Add(m.GracePeriod()) 157 return t.Before(now) || t.Equal(now) 158 } 159 160 // MeterStatus returns the overall state of the MetricsManager as a meter status summary. 161 func (m *MetricsManager) MeterStatus() MeterStatus { 162 if m.ConsecutiveErrors() < metricsManagerConsecutiveErrorThreshold { 163 return MeterStatus{MeterGreen, "ok"} 164 } 165 if m.gracePeriodExceeded() { 166 return MeterStatus{MeterRed, "failed to send metrics, exceeded grace period"} 167 } 168 return MeterStatus{MeterAmber, "failed to send metrics"} 169 }