mirror of
https://github.com/ClusterCockpit/cc-metric-store.git
synced 2024-11-14 15:07:26 +01:00
184 lines
3.6 KiB
Go
184 lines
3.6 KiB
Go
package memorystore
|
|
|
|
import (
|
|
"sync"
|
|
"unsafe"
|
|
|
|
"github.com/ClusterCockpit/cc-metric-store/internal/util"
|
|
)
|
|
|
|
// Could also be called "node" as this forms a node in a tree structure.
|
|
// Called Level because "node" might be confusing here.
|
|
// Can be both a leaf or a inner node. In this tree structue, inner nodes can
|
|
// also hold data (in `metrics`).
|
|
type Level struct {
|
|
children map[string]*Level
|
|
metrics []*buffer
|
|
lock sync.RWMutex
|
|
}
|
|
|
|
// Find the correct level for the given selector, creating it if
|
|
// it does not exist. Example selector in the context of the
|
|
// ClusterCockpit could be: []string{ "emmy", "host123", "cpu0" }.
|
|
// This function would probably benefit a lot from `level.children` beeing a `sync.Map`?
|
|
func (l *Level) findLevelOrCreate(selector []string, nMetrics int) *Level {
|
|
if len(selector) == 0 {
|
|
return l
|
|
}
|
|
|
|
// Allow concurrent reads:
|
|
l.lock.RLock()
|
|
var child *Level
|
|
var ok bool
|
|
if l.children == nil {
|
|
// Children map needs to be created...
|
|
l.lock.RUnlock()
|
|
} else {
|
|
child, ok := l.children[selector[0]]
|
|
l.lock.RUnlock()
|
|
if ok {
|
|
return child.findLevelOrCreate(selector[1:], nMetrics)
|
|
}
|
|
}
|
|
|
|
// The level does not exist, take write lock for unqiue access:
|
|
l.lock.Lock()
|
|
// While this thread waited for the write lock, another thread
|
|
// could have created the child node.
|
|
if l.children != nil {
|
|
child, ok = l.children[selector[0]]
|
|
if ok {
|
|
l.lock.Unlock()
|
|
return child.findLevelOrCreate(selector[1:], nMetrics)
|
|
}
|
|
}
|
|
|
|
child = &Level{
|
|
metrics: make([]*buffer, nMetrics),
|
|
children: nil,
|
|
}
|
|
|
|
if l.children != nil {
|
|
l.children[selector[0]] = child
|
|
} else {
|
|
l.children = map[string]*Level{selector[0]: child}
|
|
}
|
|
l.lock.Unlock()
|
|
return child.findLevelOrCreate(selector[1:], nMetrics)
|
|
}
|
|
|
|
func (l *Level) free(t int64) (int, error) {
|
|
l.lock.Lock()
|
|
defer l.lock.Unlock()
|
|
|
|
n := 0
|
|
for i, b := range l.metrics {
|
|
if b != nil {
|
|
delme, m := b.free(t)
|
|
n += m
|
|
if delme {
|
|
if cap(b.data) == BUFFER_CAP {
|
|
bufferPool.Put(b)
|
|
}
|
|
l.metrics[i] = nil
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, l := range l.children {
|
|
m, err := l.free(t)
|
|
n += m
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
func (l *Level) sizeInBytes() int64 {
|
|
l.lock.RLock()
|
|
defer l.lock.RUnlock()
|
|
size := int64(0)
|
|
|
|
for _, b := range l.metrics {
|
|
if b != nil {
|
|
size += b.count() * int64(unsafe.Sizeof(util.Float(0)))
|
|
}
|
|
}
|
|
|
|
return size
|
|
}
|
|
|
|
func (l *Level) findLevel(selector []string) *Level {
|
|
if len(selector) == 0 {
|
|
return l
|
|
}
|
|
|
|
l.lock.RLock()
|
|
defer l.lock.RUnlock()
|
|
|
|
lvl := l.children[selector[0]]
|
|
if lvl == nil {
|
|
return nil
|
|
}
|
|
|
|
return lvl.findLevel(selector[1:])
|
|
}
|
|
|
|
func (l *Level) findBuffers(selector util.Selector, offset int, f func(b *buffer) error) error {
|
|
l.lock.RLock()
|
|
defer l.lock.RUnlock()
|
|
|
|
if len(selector) == 0 {
|
|
b := l.metrics[offset]
|
|
if b != nil {
|
|
return f(b)
|
|
}
|
|
|
|
for _, lvl := range l.children {
|
|
err := lvl.findBuffers(nil, offset, f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
sel := selector[0]
|
|
if len(sel.String) != 0 && l.children != nil {
|
|
lvl, ok := l.children[sel.String]
|
|
if ok {
|
|
err := lvl.findBuffers(selector[1:], offset, f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if sel.Group != nil && l.children != nil {
|
|
for _, key := range sel.Group {
|
|
lvl, ok := l.children[key]
|
|
if ok {
|
|
err := lvl.findBuffers(selector[1:], offset, f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if sel.Any && l.children != nil {
|
|
for _, lvl := range l.children {
|
|
if err := lvl.findBuffers(selector[1:], offset, f); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|