mirror of
https://github.com/ClusterCockpit/cc-metric-store.git
synced 2024-11-10 05:07:25 +01:00
reduce allocations and locking
This commit is contained in:
parent
76c58d7799
commit
bf7c33513b
@ -117,13 +117,26 @@ func ReceiveNats(address string, handleLine func(dec *lineprotocol.Decoder) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func decodeLine(dec *lineprotocol.Decoder) error {
|
func decodeLine(dec *lineprotocol.Decoder) error {
|
||||||
|
// Reduce allocations in loop:
|
||||||
|
t := time.Now()
|
||||||
|
metrics := make([]Metric, 0, 10)
|
||||||
|
selector := make([]string, 0, 4)
|
||||||
|
typeBuf, subTypeBuf := make([]byte, 0, 20), make([]byte, 0)
|
||||||
|
|
||||||
|
// Optimize for the case where all lines in a "batch" are about the same
|
||||||
|
// cluster and host. By using `WriteToLevel` (level = host), we do not need
|
||||||
|
// to take the root- and cluster-level lock as often.
|
||||||
|
var hostLevel *level = nil
|
||||||
|
var prevCluster, prevHost string = "", ""
|
||||||
|
|
||||||
for dec.Next() {
|
for dec.Next() {
|
||||||
rawmeasurement, err := dec.Measurement()
|
rawmeasurement, err := dec.Measurement()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var cluster, host, typeName, typeId, subType, subTypeId string
|
var cluster, host string
|
||||||
|
var typeName, typeId, subType, subTypeId []byte
|
||||||
for {
|
for {
|
||||||
key, val, err := dec.NextTag()
|
key, val, err := dec.NextTag()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -133,38 +146,61 @@ func decodeLine(dec *lineprotocol.Decoder) error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The go compiler optimizes string([]byte{...}) == "...":
|
||||||
switch string(key) {
|
switch string(key) {
|
||||||
case "cluster":
|
case "cluster":
|
||||||
cluster = string(val)
|
if string(val) == prevCluster {
|
||||||
|
cluster = prevCluster
|
||||||
|
} else {
|
||||||
|
cluster = string(val)
|
||||||
|
}
|
||||||
case "hostname":
|
case "hostname":
|
||||||
host = string(val)
|
if string(val) == prevHost {
|
||||||
|
host = prevHost
|
||||||
|
} else {
|
||||||
|
host = string(val)
|
||||||
|
}
|
||||||
case "type":
|
case "type":
|
||||||
typeName = string(val)
|
typeName = val
|
||||||
case "type-id":
|
case "type-id":
|
||||||
typeId = string(val)
|
typeId = val
|
||||||
case "subtype":
|
case "subtype":
|
||||||
subType = string(val)
|
subType = val
|
||||||
case "stype-id":
|
case "stype-id":
|
||||||
subTypeId = string(val)
|
subTypeId = val
|
||||||
default:
|
default:
|
||||||
// Ignore unkown tags (cc-metric-collector might send us a unit for example that we do not need)
|
// Ignore unkown tags (cc-metric-collector might send us a unit for example that we do not need)
|
||||||
// return fmt.Errorf("unkown tag: '%s' (value: '%s')", string(key), string(val))
|
// return fmt.Errorf("unkown tag: '%s' (value: '%s')", string(key), string(val))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selector := make([]string, 2, 4)
|
if hostLevel == nil || prevCluster != cluster || prevHost != host {
|
||||||
selector[0] = cluster
|
prevCluster = cluster
|
||||||
selector[1] = host
|
prevHost = host
|
||||||
|
selector = selector[:2]
|
||||||
|
selector[0] = cluster
|
||||||
|
selector[1] = host
|
||||||
|
hostLevel = memoryStore.root.findLevelOrCreate(selector, len(memoryStore.metrics))
|
||||||
|
}
|
||||||
|
|
||||||
|
selector = selector[:0]
|
||||||
if len(typeId) > 0 {
|
if len(typeId) > 0 {
|
||||||
selector = append(selector, typeName+typeId)
|
typeBuf = typeBuf[:0]
|
||||||
|
typeBuf = append(typeBuf, typeName...)
|
||||||
|
typeBuf = append(typeBuf, typeId...)
|
||||||
|
selector = append(selector, string(typeBuf)) // <- Allocation :(
|
||||||
if len(subTypeId) > 0 {
|
if len(subTypeId) > 0 {
|
||||||
selector = append(selector, subType+subTypeId)
|
subTypeBuf = subTypeBuf[:0]
|
||||||
|
subTypeBuf = append(subTypeBuf, subType...)
|
||||||
|
subTypeBuf = append(subTypeBuf, subTypeId...)
|
||||||
|
selector = append(selector, string(subTypeBuf))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics := make([]Metric, 0, 10)
|
metrics = metrics[:0]
|
||||||
measurement := string(rawmeasurement)
|
// A more dense lp format if supported if the measurement is 'data'.
|
||||||
if measurement == "data" {
|
// In that case, the field keys are used as metric names.
|
||||||
|
if string(rawmeasurement) == "data" {
|
||||||
for {
|
for {
|
||||||
key, val, err := dec.NextField()
|
key, val, err := dec.NextField()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -185,11 +221,12 @@ func decodeLine(dec *lineprotocol.Decoder) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
metrics = append(metrics, Metric{
|
metrics = append(metrics, Metric{
|
||||||
Name: string(key),
|
Name: string(key), // <- Allocation :(
|
||||||
Value: value,
|
Value: value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
measurement := string(rawmeasurement) // <- Allocation :(
|
||||||
var value Float
|
var value Float
|
||||||
for {
|
for {
|
||||||
key, val, err := dec.NextField()
|
key, val, err := dec.NextField()
|
||||||
@ -220,13 +257,13 @@ func decodeLine(dec *lineprotocol.Decoder) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := dec.Time(lineprotocol.Second, time.Now())
|
t, err = dec.Time(lineprotocol.Second, t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// log.Printf("write: %s (%v) -> %v\n", string(measurement), selector, value)
|
// log.Printf("write: %s (%v) -> %v\n", string(measurement), selector, value)
|
||||||
if err := memoryStore.Write(selector, t.Unix(), metrics); err != nil {
|
if err := memoryStore.WriteToLevel(hostLevel, selector, t.Unix(), metrics); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
memstore.go
27
memstore.go
@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"math"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -39,13 +38,15 @@ type buffer struct {
|
|||||||
prev, next *buffer // `prev` contains older data, `next` newer data.
|
prev, next *buffer // `prev` contains older data, `next` newer data.
|
||||||
archived bool // If true, this buffer is already archived
|
archived bool // If true, this buffer is already archived
|
||||||
|
|
||||||
closed bool
|
closed bool
|
||||||
statisticts struct {
|
/*
|
||||||
samples int
|
statisticts struct {
|
||||||
min Float
|
samples int
|
||||||
max Float
|
min Float
|
||||||
avg Float
|
max Float
|
||||||
}
|
avg Float
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBuffer(ts, freq int64) *buffer {
|
func newBuffer(ts, freq int64) *buffer {
|
||||||
@ -104,6 +105,9 @@ func (b *buffer) end() int64 {
|
|||||||
return b.start + int64(len(b.data))*b.frequency
|
return b.start + int64(len(b.data))*b.frequency
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *buffer) close() {}
|
||||||
|
|
||||||
|
/*
|
||||||
func (b *buffer) close() {
|
func (b *buffer) close() {
|
||||||
if b.closed {
|
if b.closed {
|
||||||
return
|
return
|
||||||
@ -134,6 +138,7 @@ func (b *buffer) close() {
|
|||||||
b.statisticts.max = NaN
|
b.statisticts.max = NaN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// func interpolate(idx int, data []Float) Float {
|
// func interpolate(idx int, data []Float) Float {
|
||||||
// if idx == 0 || idx+1 == len(data) {
|
// if idx == 0 || idx+1 == len(data) {
|
||||||
@ -362,7 +367,11 @@ func NewMemoryStore(metrics map[string]MetricConfig) *MemoryStore {
|
|||||||
// Write all values in `metrics` to the level specified by `selector` for time `ts`.
|
// Write all values in `metrics` to the level specified by `selector` for time `ts`.
|
||||||
// Look at `findLevelOrCreate` for how selectors work.
|
// Look at `findLevelOrCreate` for how selectors work.
|
||||||
func (m *MemoryStore) Write(selector []string, ts int64, metrics []Metric) error {
|
func (m *MemoryStore) Write(selector []string, ts int64, metrics []Metric) error {
|
||||||
l := m.root.findLevelOrCreate(selector, len(m.metrics))
|
return m.WriteToLevel(&m.root, selector, ts, metrics)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MemoryStore) WriteToLevel(l *level, selector []string, ts int64, metrics []Metric) error {
|
||||||
|
l = l.findLevelOrCreate(selector, len(m.metrics))
|
||||||
l.lock.Lock()
|
l.lock.Lock()
|
||||||
defer l.lock.Unlock()
|
defer l.lock.Unlock()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user