github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	// TODO(fwereade): 2016-03-17 lp:1558657
   156  	now := time.Now()
   157  	t := m.LastSuccessfulSend().Add(m.GracePeriod())
   158  	return t.Before(now) || t.Equal(now)
   159  }
   160  
   161  // MeterStatus returns the overall state of the MetricsManager as a meter status summary.
   162  func (m *MetricsManager) MeterStatus() MeterStatus {
   163  	if m.ConsecutiveErrors() < metricsManagerConsecutiveErrorThreshold {
   164  		return MeterStatus{MeterGreen, "ok"}
   165  	}
   166  	if m.gracePeriodExceeded() {
   167  		return MeterStatus{MeterRed, "failed to send metrics, exceeded grace period"}
   168  	}
   169  	return MeterStatus{MeterAmber, "failed to send metrics"}
   170  }