package buffer import ( "sync" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/selfstat" ) var ( MetricsWritten = selfstat.Register("agent", "metrics_written", map[string]string{}) MetricsDropped = selfstat.Register("agent", "metrics_dropped", map[string]string{}) ) // Buffer is an object for storing metrics in a circular buffer. type Buffer struct { sync.Mutex buf []telegraf.Metric first int last int size int empty bool } // NewBuffer returns a Buffer // size is the maximum number of metrics that Buffer will cache. If Add is // called when the buffer is full, then the oldest metric(s) will be dropped. func NewBuffer(size int) *Buffer { return &Buffer{ buf: make([]telegraf.Metric, size), first: 0, last: 0, size: size, empty: true, } } // IsEmpty returns true if Buffer is empty. func (b *Buffer) IsEmpty() bool { return b.empty } // Len returns the current length of the buffer. func (b *Buffer) Len() int { if b.empty { return 0 } else if b.first <= b.last { return b.last - b.first + 1 } // Spans the end of array. // size - gap in the middle return b.size - (b.first - b.last - 1) // size - gap } func (b *Buffer) push(m telegraf.Metric) { // Empty if b.empty { b.last = b.first // Reset b.buf[b.last] = m b.empty = false return } b.last++ b.last %= b.size // Full if b.first == b.last { MetricsDropped.Incr(1) b.first = (b.first + 1) % b.size } b.buf[b.last] = m } // Add adds metrics to the buffer. func (b *Buffer) Add(metrics ...telegraf.Metric) { b.Lock() defer b.Unlock() for i := range metrics { MetricsWritten.Incr(1) b.push(metrics[i]) } } // Batch returns a batch of metrics of size batchSize. // the batch will be of maximum length batchSize. It can be less than batchSize, // if the length of Buffer is less than batchSize. func (b *Buffer) Batch(batchSize int) []telegraf.Metric { b.Lock() defer b.Unlock() outLen := min(b.Len(), batchSize) out := make([]telegraf.Metric, outLen) if outLen == 0 { return out } // We copy everything right of first up to last, count or end // b.last >= rightInd || b.last < b.first // therefore wont copy past b.last rightInd := min(b.size, b.first+outLen) - 1 copyCount := copy(out, b.buf[b.first:rightInd+1]) // We've emptied the ring if rightInd == b.last { b.empty = true } b.first = rightInd + 1 b.first %= b.size // We circle back for the rest if copyCount < outLen { right := min(b.last, outLen-copyCount) copy(out[copyCount:], b.buf[b.first:right+1]) // We've emptied the ring if right == b.last { b.empty = true } b.first = right + 1 b.first %= b.size } return out } func min(a, b int) int { if b < a { return b } return a }