2018-05-08 09:38:12 +00:00
|
|
|
// Package metrics provides common functionality for working with metrics,
|
2020-04-14 21:47:09 +00:00
|
|
|
// particularly useful for monitoring components. It includes types to store,
|
2018-05-08 09:38:12 +00:00
|
|
|
// check and filter metrics.
|
|
|
|
package metrics
|
2018-05-01 13:39:41 +00:00
|
|
|
|
|
|
|
import (
|
2019-03-08 02:10:51 +00:00
|
|
|
"container/ring"
|
2018-05-01 13:39:41 +00:00
|
|
|
"errors"
|
2019-03-08 02:10:51 +00:00
|
|
|
"sync"
|
|
|
|
"time"
|
2018-05-01 13:39:41 +00:00
|
|
|
|
|
|
|
"github.com/ipfs/ipfs-cluster/api"
|
|
|
|
)
|
|
|
|
|
2018-05-08 09:38:12 +00:00
|
|
|
// DefaultWindowCap sets the amount of metrics to store per peer.
|
|
|
|
var DefaultWindowCap = 25
|
|
|
|
|
|
|
|
// ErrNoMetrics is returned when there are no metrics in a Window.
|
2018-05-01 13:39:41 +00:00
|
|
|
var ErrNoMetrics = errors.New("no metrics have been added to this window")
|
|
|
|
|
2018-05-08 09:38:12 +00:00
|
|
|
// Window implements a circular queue to store metrics.
|
|
|
|
type Window struct {
|
2019-03-08 02:10:51 +00:00
|
|
|
wMu sync.RWMutex
|
|
|
|
window *ring.Ring
|
2018-05-01 13:39:41 +00:00
|
|
|
}
|
|
|
|
|
2018-05-08 09:38:12 +00:00
|
|
|
// NewWindow creates an instance with the given
|
2018-05-09 07:04:49 +00:00
|
|
|
// window capacity.
|
|
|
|
func NewWindow(windowCap int) *Window {
|
2018-05-08 09:38:12 +00:00
|
|
|
if windowCap <= 0 {
|
|
|
|
panic("invalid windowCap")
|
|
|
|
}
|
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
w := ring.New(windowCap)
|
2018-05-08 09:38:12 +00:00
|
|
|
return &Window{
|
2018-05-01 13:39:41 +00:00
|
|
|
window: w,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add adds a new metric to the window. If the window capacity
|
|
|
|
// has been reached, the oldest metric (by the time it was added),
|
2019-03-08 02:10:51 +00:00
|
|
|
// will be discarded. Add leaves the cursor on the next spot,
|
|
|
|
// which is either empty or the oldest record.
|
2019-02-27 17:04:35 +00:00
|
|
|
func (mw *Window) Add(m *api.Metric) {
|
2019-03-21 01:51:03 +00:00
|
|
|
m.ReceivedAt = time.Now().UnixNano()
|
2019-03-08 02:10:51 +00:00
|
|
|
|
|
|
|
mw.wMu.Lock()
|
2019-03-21 11:54:12 +00:00
|
|
|
mw.window.Value = m
|
2019-03-26 01:55:07 +00:00
|
|
|
mw.window = mw.window.Next()
|
|
|
|
mw.wMu.Unlock()
|
2018-05-01 13:39:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Latest returns the last metric added. It returns an error
|
|
|
|
// if no metrics were added.
|
2019-02-27 17:04:35 +00:00
|
|
|
func (mw *Window) Latest() (*api.Metric, error) {
|
2019-03-21 11:54:12 +00:00
|
|
|
var last *api.Metric
|
|
|
|
var ok bool
|
2019-03-26 01:55:07 +00:00
|
|
|
|
|
|
|
mw.wMu.RLock()
|
|
|
|
// This just returns the previous ring and
|
|
|
|
// doesn't set the window "cursor" to the previous
|
|
|
|
// ring. Therefore this is just a read operation
|
|
|
|
// as well.
|
|
|
|
prevRing := mw.window.Prev()
|
|
|
|
mw.wMu.RUnlock()
|
|
|
|
|
|
|
|
last, ok = prevRing.Value.(*api.Metric)
|
|
|
|
|
|
|
|
if !ok || last == nil {
|
2019-02-27 17:04:35 +00:00
|
|
|
return nil, ErrNoMetrics
|
2018-05-01 13:39:41 +00:00
|
|
|
}
|
2019-03-21 13:53:09 +00:00
|
|
|
|
2019-03-08 02:10:51 +00:00
|
|
|
return last, nil
|
2018-05-01 13:39:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// All returns all the metrics in the window, in the inverse order
|
|
|
|
// they were Added. That is, result[0] will be the last added
|
|
|
|
// metric.
|
2019-02-27 17:04:35 +00:00
|
|
|
func (mw *Window) All() []*api.Metric {
|
2019-03-08 02:10:51 +00:00
|
|
|
values := make([]*api.Metric, 0, mw.window.Len())
|
2019-03-26 01:55:07 +00:00
|
|
|
|
|
|
|
mw.wMu.RLock()
|
2019-03-08 02:10:51 +00:00
|
|
|
mw.window.Do(func(v interface{}) {
|
2019-03-26 01:55:07 +00:00
|
|
|
i, ok := v.(*api.Metric)
|
|
|
|
if ok {
|
2019-03-08 02:10:51 +00:00
|
|
|
// append younger values to older value
|
|
|
|
values = append([]*api.Metric{i}, values...)
|
|
|
|
}
|
|
|
|
})
|
2019-03-26 01:55:07 +00:00
|
|
|
mw.wMu.RUnlock()
|
2019-03-21 13:53:09 +00:00
|
|
|
|
2019-03-21 13:39:35 +00:00
|
|
|
return values
|
2019-03-08 02:10:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Distribution returns the deltas between all the current
|
|
|
|
// values contained in the current window. This will
|
|
|
|
// only return values if the api.Metric.Type() is "ping",
|
|
|
|
// which are used for accural failure detection.
|
2019-03-21 10:42:56 +00:00
|
|
|
func (mw *Window) Distribution() []float64 {
|
2019-03-08 02:10:51 +00:00
|
|
|
ms := mw.All()
|
2019-03-21 10:42:56 +00:00
|
|
|
dist := make([]float64, 0, len(ms)-1)
|
2019-03-21 01:51:03 +00:00
|
|
|
// the last value can't be used to calculate a delta
|
|
|
|
for i, v := range ms[:len(ms)-1] {
|
2019-03-08 02:10:51 +00:00
|
|
|
// All() provides an order slice, where ms[i] is younger than ms[i+1]
|
2019-03-21 01:51:03 +00:00
|
|
|
delta := v.ReceivedAt - ms[i+1].ReceivedAt
|
2019-03-21 10:42:56 +00:00
|
|
|
dist = append(dist, float64(delta))
|
2018-05-01 13:39:41 +00:00
|
|
|
}
|
2019-03-08 02:10:51 +00:00
|
|
|
|
|
|
|
return dist
|
2018-05-01 13:39:41 +00:00
|
|
|
}
|