mirror of
https://github.com/ClusterCockpit/cc-metric-store.git
synced 2024-11-10 05:07:25 +01:00
test streaming checkpoints
This commit is contained in:
parent
1125678bc0
commit
a8bc250600
@ -5,12 +5,66 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ClusterCockpit/cc-metric-store/internal/types"
|
"github.com/ClusterCockpit/cc-metric-store/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func createTestStore(t *testing.T, withData bool) *MemoryStore {
|
||||||
|
ms := NewMemoryStore(map[string]types.MetricConfig{
|
||||||
|
"flops": {Frequency: 1},
|
||||||
|
"membw": {Frequency: 1},
|
||||||
|
"ipc": {Frequency: 2},
|
||||||
|
})
|
||||||
|
|
||||||
|
if !withData {
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
|
||||||
|
n := 1000
|
||||||
|
sel := []string{"hello", "world"}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if err := ms.Write(sel, int64(i), []types.Metric{
|
||||||
|
{Name: "flops", Value: types.Float(math.Sin(float64(i) * 0.1))},
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// n := 3000
|
||||||
|
// x1, x2, x3 := 0.0, 1.1, 2.2
|
||||||
|
// d1, d2, d3 := 0.05, 0.1, 0.2
|
||||||
|
|
||||||
|
// sel1, sel2, sel3 := []string{"cluster"}, []string{"cluster", "host1"}, []string{"cluster", "host2", "cpu0"}
|
||||||
|
// for i := 0; i < n; i++ {
|
||||||
|
// ms.Write(sel1, int64(i), []types.Metric{
|
||||||
|
// {Name: "flops", Value: types.Float(x1)},
|
||||||
|
// {Name: "membw", Value: types.Float(x2)},
|
||||||
|
// {Name: "ipc", Value: types.Float(x3)},
|
||||||
|
// })
|
||||||
|
|
||||||
|
// ms.Write(sel2, int64(i), []types.Metric{
|
||||||
|
// {Name: "flops", Value: types.Float(x1) + 1.},
|
||||||
|
// {Name: "membw", Value: types.Float(x2) + 2.},
|
||||||
|
// {Name: "ipc", Value: types.Float(x3) + 3.},
|
||||||
|
// })
|
||||||
|
|
||||||
|
// ms.Write(sel3, int64(i)*2, []types.Metric{
|
||||||
|
// {Name: "flops", Value: types.Float(x1) + 1.},
|
||||||
|
// {Name: "membw", Value: types.Float(x2) + 2.},
|
||||||
|
// {Name: "ipc", Value: types.Float(x3) + 3.},
|
||||||
|
// })
|
||||||
|
|
||||||
|
// x1 += d1
|
||||||
|
// x2 += d2
|
||||||
|
// x3 += d3
|
||||||
|
// }
|
||||||
|
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
|
||||||
func TestIntEncoding(t *testing.T) {
|
func TestIntEncoding(t *testing.T) {
|
||||||
buf := make([]byte, 0, 100)
|
buf := make([]byte, 0, 100)
|
||||||
x1 := uint64(0x0102030405060708)
|
x1 := uint64(0x0102030405060708)
|
||||||
@ -107,3 +161,45 @@ func TestIdentity(t *testing.T) {
|
|||||||
t.Fatal("x != deserialize(serialize(x))")
|
t.Fatal("x != deserialize(serialize(x))")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStreamingCheckpointIndentity(t *testing.T) {
|
||||||
|
disk := &bytes.Buffer{}
|
||||||
|
ms1 := createTestStore(t, true)
|
||||||
|
if err := ms1.SaveCheckpoint(0, 7000, disk); err != nil {
|
||||||
|
t.Fatal("saving checkpoint failed: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("disk: %#v\n", disk.Bytes())
|
||||||
|
|
||||||
|
ms2 := createTestStore(t, false)
|
||||||
|
if err := ms2.LoadCheckpoint(disk); err != nil {
|
||||||
|
t.Fatal("loading checkpoint failed: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
arr1, from1, to1, err := ms1.Read(types.Selector{{String: "hello"}, {String: "world"}}, "flops", 0, 7000)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
arr2, from2, to2, err := ms2.Read(types.Selector{{String: "hello"}, {String: "world"}}, "flops", 0, 7000)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(arr1, arr2) || from1 != from2 || to1 != to2 {
|
||||||
|
t.Fatal("x != deserialize(serialize(x))")
|
||||||
|
}
|
||||||
|
|
||||||
|
// if !reflect.DeepEqual(ms1, ms2) {
|
||||||
|
// // fmt.Printf("ms1.root: %#v\n", ms1.root)
|
||||||
|
// // fmt.Printf("ms2.root: %#v\n", ms2.root)
|
||||||
|
// // fmt.Printf("ms1.root.sublevels['hello']: %#v\n", *ms1.root.sublevels["hello"])
|
||||||
|
// // fmt.Printf("ms2.root.sublevels['hello']: %#v\n", *ms2.root.sublevels["hello"])
|
||||||
|
// // fmt.Printf("ms1.root.sublevels['hello'].sublevels['world']: %#v\n", *ms1.root.sublevels["hello"].sublevels["world"])
|
||||||
|
// // fmt.Printf("ms2.root.sublevels['hello'].sublevels['world']: %#v\n", *ms2.root.sublevels["hello"].sublevels["world"])
|
||||||
|
// // fmt.Printf("ms1.root.sublevels['hello'].sublevels['world'].metrics[0]: %#v\n", *ms1.root.sublevels["hello"].sublevels["world"].metrics[0])
|
||||||
|
// // fmt.Printf("ms2.root.sublevels['hello'].sublevels['world'].metrics[0]: %#v\n", *ms2.root.sublevels["hello"].sublevels["world"].metrics[0])
|
||||||
|
|
||||||
|
// t.Fatal("x != deserialize(serialize(x))")
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package memstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ClusterCockpit/cc-metric-store/internal/types"
|
"github.com/ClusterCockpit/cc-metric-store/internal/types"
|
||||||
@ -148,11 +149,22 @@ func (ms *MemoryStore) GetMetricConf(metric string) (types.MetricConfig, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ms *MemoryStore) GetMetricForOffset(offset int) string {
|
func (ms *MemoryStore) GetMetricForOffset(offset int) string {
|
||||||
return "" // TODO!
|
for name, mc := range ms.metrics {
|
||||||
|
if mc.Offset == offset {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *MemoryStore) MinFrequency() int64 {
|
func (ms *MemoryStore) MinFrequency() int64 {
|
||||||
return 10 // TODO
|
min := int64(math.MaxInt64)
|
||||||
|
for _, mc := range ms.metrics {
|
||||||
|
if mc.Frequency < min {
|
||||||
|
min = mc.Frequency
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return min
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MemoryStore) GetLevel(selector []string) *Level {
|
func (m *MemoryStore) GetLevel(selector []string) *Level {
|
||||||
@ -189,6 +201,35 @@ func (m *MemoryStore) WriteToLevel(l *Level, selector []string, ts int64, metric
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MemoryStore) Write(selector []string, ts int64, metrics []types.Metric) error {
|
||||||
|
l := m.root.findLevelOrCreate(selector, len(m.metrics))
|
||||||
|
for _, metric := range metrics {
|
||||||
|
mc, ok := m.GetMetricConf(metric.Name)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c := l.metrics[mc.Offset]
|
||||||
|
if c == nil {
|
||||||
|
// First write to this metric and level
|
||||||
|
c = newChunk(ts, mc.Frequency)
|
||||||
|
l.metrics[mc.Offset] = c
|
||||||
|
}
|
||||||
|
|
||||||
|
nc, err := c.write(ts, metric.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last write started a new chunk...
|
||||||
|
if c != nc {
|
||||||
|
l.metrics[mc.Offset] = nc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MemoryStore) Free(t int64) int {
|
func (m *MemoryStore) Free(t int64) int {
|
||||||
_, n := m.root.free(t)
|
_, n := m.root.free(t)
|
||||||
return n
|
return n
|
||||||
|
@ -31,6 +31,7 @@ func (ms *MemoryStore) SaveCheckpoint(from, to int64, w io.Writer) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writes a checkpoint for the current level to buf, buf to w if enough bytes are reached, and returns buf.
|
||||||
func (l *Level) saveCheckpoint(ms *MemoryStore, from, to int64, w io.Writer, buf []byte, metricsbuf []types.Float) ([]byte, error) {
|
func (l *Level) saveCheckpoint(ms *MemoryStore, from, to int64, w io.Writer, buf []byte, metricsbuf []types.Float) ([]byte, error) {
|
||||||
var err error
|
var err error
|
||||||
l.lock.RLock()
|
l.lock.RLock()
|
||||||
@ -39,26 +40,37 @@ func (l *Level) saveCheckpoint(ms *MemoryStore, from, to int64, w io.Writer, buf
|
|||||||
buf = encodeBytes(buf, nil) // Reserved
|
buf = encodeBytes(buf, nil) // Reserved
|
||||||
|
|
||||||
// Metrics:
|
// Metrics:
|
||||||
buf = encodeUint32(buf, uint32(len(l.metrics)))
|
n := 0
|
||||||
|
for _, c := range l.metrics {
|
||||||
|
if c != nil {
|
||||||
|
n += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = encodeUint32(buf, uint32(n))
|
||||||
for i, c := range l.metrics {
|
for i, c := range l.metrics {
|
||||||
|
if c == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
key := ms.GetMetricForOffset(i)
|
key := ms.GetMetricForOffset(i)
|
||||||
buf = encodeString(buf, key)
|
buf = encodeString(buf, key)
|
||||||
|
|
||||||
// Metric
|
// Metric
|
||||||
buf = encodeBytes(buf, nil) // Reserved
|
buf = encodeBytes(buf, nil) // Reserved
|
||||||
|
|
||||||
metricsbuf = metricsbuf[:(to-from)/c.frequency+1]
|
metrics := metricsbuf[:(to-from)/c.frequency+1]
|
||||||
var cfrom int64
|
var cfrom int64
|
||||||
if metricsbuf, cfrom, _, err = c.read(from, to, metricsbuf); err != nil {
|
if metrics, cfrom, _, err = c.read(from, to, metrics); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
buf = encodeUint64(buf, uint64(c.frequency))
|
buf = encodeUint64(buf, uint64(c.frequency))
|
||||||
buf = encodeUint64(buf, uint64(cfrom))
|
buf = encodeUint64(buf, uint64(cfrom))
|
||||||
buf = encodeUint32(buf, uint32(len(metricsbuf)))
|
buf = encodeUint32(buf, uint32(len(metrics)))
|
||||||
|
|
||||||
var x types.Float
|
var x types.Float
|
||||||
elmsize := unsafe.Sizeof(x)
|
elmsize := unsafe.Sizeof(x)
|
||||||
sh := (*reflect.SliceHeader)(unsafe.Pointer(&metricsbuf))
|
sh := (*reflect.SliceHeader)(unsafe.Pointer(&metrics))
|
||||||
bytes := unsafe.Slice((*byte)(unsafe.Pointer(sh.Data)), sh.Len*int(elmsize))
|
bytes := unsafe.Slice((*byte)(unsafe.Pointer(sh.Data)), sh.Len*int(elmsize))
|
||||||
buf = append(buf, bytes...)
|
buf = append(buf, bytes...)
|
||||||
|
|
||||||
@ -69,6 +81,13 @@ func (l *Level) saveCheckpoint(ms *MemoryStore, from, to int64, w io.Writer, buf
|
|||||||
|
|
||||||
buf = buf[0:]
|
buf = buf[0:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for c != nil {
|
||||||
|
if c.end() <= to {
|
||||||
|
c.checkpointed = true
|
||||||
|
}
|
||||||
|
c = c.prev
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sublevels:
|
// Sublevels:
|
||||||
@ -127,13 +146,13 @@ func (l *Level) loadCheckpoint(ms *MemoryStore, r io.Reader, buf []byte) error {
|
|||||||
if n, err = decodeUint32(buf, r); err != nil {
|
if n, err = decodeUint32(buf, r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if l.metrics == nil {
|
||||||
|
l.metrics = make([]*chunk, len(ms.metrics))
|
||||||
|
}
|
||||||
for i := 0; i < int(n); i++ {
|
for i := 0; i < int(n); i++ {
|
||||||
if key, err = decodeString(buf, r); err != nil {
|
if key, err = decodeString(buf, r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if l.metrics == nil {
|
|
||||||
l.metrics = make([]*chunk, len(ms.metrics))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metric:
|
// Metric:
|
||||||
if _, err = decodeBytes(buf, r); err != nil { // Reserved...
|
if _, err = decodeBytes(buf, r); err != nil { // Reserved...
|
||||||
@ -150,6 +169,9 @@ func (l *Level) loadCheckpoint(ms *MemoryStore, r io.Reader, buf []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if numelements == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
var x types.Float
|
var x types.Float
|
||||||
elmsize := unsafe.Sizeof(x)
|
elmsize := unsafe.Sizeof(x)
|
||||||
|
Loading…
Reference in New Issue
Block a user