mirror of
				https://github.com/ClusterCockpit/cc-backend
				synced 2025-10-31 16:05:06 +01:00 
			
		
		
		
	Move avro into memorystore. Refactor
Does not compile
This commit is contained in:
		| @@ -246,9 +246,9 @@ func main() { | |||||||
| 	var wg sync.WaitGroup | 	var wg sync.WaitGroup | ||||||
|  |  | ||||||
| 	// Metric Store starts after all flags have been processes | 	// Metric Store starts after all flags have been processes | ||||||
| 	if config.InternalCCMSFlag { | 	if memorystore.InternalCCMSFlag { | ||||||
| 		if mscfg := ccconf.GetPackageConfig("metric-store"); mscfg != nil { | 		if mscfg := ccconf.GetPackageConfig("metric-store"); mscfg != nil { | ||||||
| 			config.InitMetricStore(mscfg) | 			memorystore.InitMetricStore(mscfg) | ||||||
| 		} else { | 		} else { | ||||||
| 			cclog.Abort("Metric Store configuration must be present") | 			cclog.Abort("Metric Store configuration must be present") | ||||||
| 		} | 		} | ||||||
| @@ -257,7 +257,6 @@ func main() { | |||||||
| 	} | 	} | ||||||
| 	archiver.Start(repository.GetJobRepository()) | 	archiver.Start(repository.GetJobRepository()) | ||||||
|  |  | ||||||
| 	// // Comment out |  | ||||||
| 	taskManager.Start(ccconf.GetPackageConfig("cron"), | 	taskManager.Start(ccconf.GetPackageConfig("cron"), | ||||||
| 		ccconf.GetPackageConfig("archive")) | 		ccconf.GetPackageConfig("archive")) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -244,7 +244,7 @@ func serverInit() { | |||||||
| 	apiHandle.MountConfigApiRoutes(configapi) | 	apiHandle.MountConfigApiRoutes(configapi) | ||||||
| 	apiHandle.MountFrontendApiRoutes(frontendapi) | 	apiHandle.MountFrontendApiRoutes(frontendapi) | ||||||
|  |  | ||||||
| 	if config.InternalCCMSFlag { | 	if memorystore.InternalCCMSFlag { | ||||||
| 		apiHandle.MountMetricStoreApiRoutes(metricstoreapi) | 		apiHandle.MountMetricStoreApiRoutes(metricstoreapi) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -369,7 +369,7 @@ func serverShutdown() { | |||||||
| 	server.Shutdown(context.Background()) | 	server.Shutdown(context.Background()) | ||||||
|  |  | ||||||
| 	// Archive all the metric store data | 	// Archive all the metric store data | ||||||
| 	if config.InternalCCMSFlag { | 	if memorystore.InternalCCMSFlag { | ||||||
| 		memorystore.Shutdown() | 		memorystore.Shutdown() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| // All rights reserved. | // All rights reserved. | ||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| package memorystore | package memorystore | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -47,7 +48,7 @@ type ErrorResponse struct { | |||||||
| 	Error  string `json:"error"` // Error Message | 	Error  string `json:"error"` // Error Message | ||||||
| } | } | ||||||
|  |  | ||||||
| type ApiMetricData struct { | type APIMetricData struct { | ||||||
| 	Error      *string           `json:"error,omitempty"` | 	Error      *string           `json:"error,omitempty"` | ||||||
| 	Data       schema.FloatArray `json:"data,omitempty"` | 	Data       schema.FloatArray `json:"data,omitempty"` | ||||||
| 	From       int64             `json:"from"` | 	From       int64             `json:"from"` | ||||||
| @@ -69,7 +70,7 @@ func handleError(err error, statusCode int, rw http.ResponseWriter) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // TODO: Optimize this, just like the stats endpoint! | // TODO: Optimize this, just like the stats endpoint! | ||||||
| func (data *ApiMetricData) AddStats() { | func (data *APIMetricData) AddStats() { | ||||||
| 	n := 0 | 	n := 0 | ||||||
| 	sum, min, max := 0.0, math.MaxFloat64, -math.MaxFloat64 | 	sum, min, max := 0.0, math.MaxFloat64, -math.MaxFloat64 | ||||||
| 	for _, x := range data.Data { | 	for _, x := range data.Data { | ||||||
| @@ -93,7 +94,7 @@ func (data *ApiMetricData) AddStats() { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (data *ApiMetricData) ScaleBy(f schema.Float) { | func (data *APIMetricData) ScaleBy(f schema.Float) { | ||||||
| 	if f == 0 || f == 1 { | 	if f == 0 || f == 1 { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -106,7 +107,7 @@ func (data *ApiMetricData) ScaleBy(f schema.Float) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (data *ApiMetricData) PadDataWithNull(ms *MemoryStore, from, to int64, metric string) { | func (data *APIMetricData) PadDataWithNull(ms *MemoryStore, from, to int64, metric string) { | ||||||
| 	minfo, ok := ms.Metrics[metric] | 	minfo, ok := ms.Metrics[metric] | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return | 		return | ||||||
| @@ -115,7 +116,7 @@ func (data *ApiMetricData) PadDataWithNull(ms *MemoryStore, from, to int64, metr | |||||||
| 	if (data.From / minfo.Frequency) > (from / minfo.Frequency) { | 	if (data.From / minfo.Frequency) > (from / minfo.Frequency) { | ||||||
| 		padfront := int((data.From / minfo.Frequency) - (from / minfo.Frequency)) | 		padfront := int((data.From / minfo.Frequency) - (from / minfo.Frequency)) | ||||||
| 		ndata := make([]schema.Float, 0, padfront+len(data.Data)) | 		ndata := make([]schema.Float, 0, padfront+len(data.Data)) | ||||||
| 		for i := 0; i < padfront; i++ { | 		for range padfront { | ||||||
| 			ndata = append(ndata, schema.NaN) | 			ndata = append(ndata, schema.NaN) | ||||||
| 		} | 		} | ||||||
| 		for j := 0; j < len(data.Data); j++ { | 		for j := 0; j < len(data.Data); j++ { | ||||||
| @@ -218,9 +219,9 @@ func HandleWrite(rw http.ResponseWriter, r *http.Request) { | |||||||
| 	rw.WriteHeader(http.StatusOK) | 	rw.WriteHeader(http.StatusOK) | ||||||
| } | } | ||||||
|  |  | ||||||
| type ApiQueryRequest struct { | type APIQueryRequest struct { | ||||||
| 	Cluster     string     `json:"cluster"` | 	Cluster     string     `json:"cluster"` | ||||||
| 	Queries     []ApiQuery `json:"queries"` | 	Queries     []APIQuery `json:"queries"` | ||||||
| 	ForAllNodes []string   `json:"for-all-nodes"` | 	ForAllNodes []string   `json:"for-all-nodes"` | ||||||
| 	From        int64      `json:"from"` | 	From        int64      `json:"from"` | ||||||
| 	To          int64      `json:"to"` | 	To          int64      `json:"to"` | ||||||
| @@ -229,12 +230,12 @@ type ApiQueryRequest struct { | |||||||
| 	WithPadding bool       `json:"with-padding"` | 	WithPadding bool       `json:"with-padding"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type ApiQueryResponse struct { | type APIQueryResponse struct { | ||||||
| 	Queries []ApiQuery        `json:"queries,omitempty"` | 	Queries []APIQuery        `json:"queries,omitempty"` | ||||||
| 	Results [][]ApiMetricData `json:"results"` | 	Results [][]APIMetricData `json:"results"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type ApiQuery struct { | type APIQuery struct { | ||||||
| 	Type        *string      `json:"type,omitempty"` | 	Type        *string      `json:"type,omitempty"` | ||||||
| 	SubType     *string      `json:"subtype,omitempty"` | 	SubType     *string      `json:"subtype,omitempty"` | ||||||
| 	Metric      string       `json:"metric"` | 	Metric      string       `json:"metric"` | ||||||
| @@ -246,22 +247,21 @@ type ApiQuery struct { | |||||||
| 	Aggregate   bool         `json:"aggreg"` | 	Aggregate   bool         `json:"aggreg"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func FetchData(req ApiQueryRequest) (*ApiQueryResponse, error) { | func FetchData(req APIQueryRequest) (*APIQueryResponse, error) { | ||||||
|  |  | ||||||
| 	req.WithData = true | 	req.WithData = true | ||||||
| 	req.WithData = true | 	req.WithData = true | ||||||
| 	req.WithData = true | 	req.WithData = true | ||||||
|  |  | ||||||
| 	ms := GetMemoryStore() | 	ms := GetMemoryStore() | ||||||
|  |  | ||||||
| 	response := ApiQueryResponse{ | 	response := APIQueryResponse{ | ||||||
| 		Results: make([][]ApiMetricData, 0, len(req.Queries)), | 		Results: make([][]APIMetricData, 0, len(req.Queries)), | ||||||
| 	} | 	} | ||||||
| 	if req.ForAllNodes != nil { | 	if req.ForAllNodes != nil { | ||||||
| 		nodes := ms.ListChildren([]string{req.Cluster}) | 		nodes := ms.ListChildren([]string{req.Cluster}) | ||||||
| 		for _, node := range nodes { | 		for _, node := range nodes { | ||||||
| 			for _, metric := range req.ForAllNodes { | 			for _, metric := range req.ForAllNodes { | ||||||
| 				q := ApiQuery{ | 				q := APIQuery{ | ||||||
| 					Metric:   metric, | 					Metric:   metric, | ||||||
| 					Hostname: node, | 					Hostname: node, | ||||||
| 				} | 				} | ||||||
| @@ -300,21 +300,21 @@ func FetchData(req ApiQueryRequest) (*ApiQueryResponse, error) { | |||||||
| 			} | 			} | ||||||
| 			sels = append(sels, sel) | 			sels = append(sels, sel) | ||||||
| 		} else { | 		} else { | ||||||
| 			for _, typeId := range query.TypeIds { | 			for _, typeID := range query.TypeIds { | ||||||
| 				if query.SubType != nil { | 				if query.SubType != nil { | ||||||
| 					for _, subTypeId := range query.SubTypeIds { | 					for _, subTypeID := range query.SubTypeIds { | ||||||
| 						sels = append(sels, util.Selector{ | 						sels = append(sels, util.Selector{ | ||||||
| 							{String: req.Cluster}, | 							{String: req.Cluster}, | ||||||
| 							{String: query.Hostname}, | 							{String: query.Hostname}, | ||||||
| 							{String: *query.Type + typeId}, | 							{String: *query.Type + typeID}, | ||||||
| 							{String: *query.SubType + subTypeId}, | 							{String: *query.SubType + subTypeID}, | ||||||
| 						}) | 						}) | ||||||
| 					} | 					} | ||||||
| 				} else { | 				} else { | ||||||
| 					sels = append(sels, util.Selector{ | 					sels = append(sels, util.Selector{ | ||||||
| 						{String: req.Cluster}, | 						{String: req.Cluster}, | ||||||
| 						{String: query.Hostname}, | 						{String: query.Hostname}, | ||||||
| 						{String: *query.Type + typeId}, | 						{String: *query.Type + typeID}, | ||||||
| 					}) | 					}) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| @@ -323,12 +323,11 @@ func FetchData(req ApiQueryRequest) (*ApiQueryResponse, error) { | |||||||
| 		// log.Printf("query: %#v\n", query) | 		// log.Printf("query: %#v\n", query) | ||||||
| 		// log.Printf("sels: %#v\n", sels) | 		// log.Printf("sels: %#v\n", sels) | ||||||
| 		var err error | 		var err error | ||||||
| 		res := make([]ApiMetricData, 0, len(sels)) | 		res := make([]APIMetricData, 0, len(sels)) | ||||||
| 		for _, sel := range sels { | 		for _, sel := range sels { | ||||||
| 			data := ApiMetricData{} | 			data := APIMetricData{} | ||||||
|  |  | ||||||
| 			data.Data, data.From, data.To, data.Resolution, err = ms.Read(sel, query.Metric, req.From, req.To, query.Resolution) | 			data.Data, data.From, data.To, data.Resolution, err = ms.Read(sel, query.Metric, req.From, req.To, query.Resolution) | ||||||
|  |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				msg := err.Error() | 				msg := err.Error() | ||||||
| 				data.Error = &msg | 				data.Error = &msg | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| // All rights reserved. This file is part of cc-backend. | // All rights reserved. This file is part of cc-backend. | ||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| package memorystore | package memorystore | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -18,14 +19,13 @@ import ( | |||||||
| 	"sync/atomic" | 	"sync/atomic" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/ClusterCockpit/cc-backend/internal/config" |  | ||||||
| 	cclog "github.com/ClusterCockpit/cc-lib/ccLogger" | 	cclog "github.com/ClusterCockpit/cc-lib/ccLogger" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func Archiving(wg *sync.WaitGroup, ctx context.Context) { | func Archiving(wg *sync.WaitGroup, ctx context.Context) { | ||||||
| 	go func() { | 	go func() { | ||||||
| 		defer wg.Done() | 		defer wg.Done() | ||||||
| 		d, err := time.ParseDuration(config.MetricStoreKeys.Archive.Interval) | 		d, err := time.ParseDuration(Keys.Archive.Interval) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatalf("[METRICSTORE]> error parsing archive interval duration: %v\n", err) | 			log.Fatalf("[METRICSTORE]> error parsing archive interval duration: %v\n", err) | ||||||
| 		} | 		} | ||||||
| @@ -46,8 +46,8 @@ func Archiving(wg *sync.WaitGroup, ctx context.Context) { | |||||||
| 			case <-ticks: | 			case <-ticks: | ||||||
| 				t := time.Now().Add(-d) | 				t := time.Now().Add(-d) | ||||||
| 				log.Printf("[METRICSTORE]> start archiving checkpoints (older than %s)...\n", t.Format(time.RFC3339)) | 				log.Printf("[METRICSTORE]> start archiving checkpoints (older than %s)...\n", t.Format(time.RFC3339)) | ||||||
| 				n, err := ArchiveCheckpoints(config.MetricStoreKeys.Checkpoints.RootDir, | 				n, err := ArchiveCheckpoints(Keys.Checkpoints.RootDir, | ||||||
| 					config.MetricStoreKeys.Archive.RootDir, t.Unix(), config.MetricStoreKeys.Archive.DeleteInstead) | 					Keys.Archive.RootDir, t.Unix(), Keys.Archive.DeleteInstead) | ||||||
|  |  | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Printf("[METRICSTORE]> archiving failed: %s\n", err.Error()) | 					log.Printf("[METRICSTORE]> archiving failed: %s\n", err.Error()) | ||||||
| @@ -59,7 +59,7 @@ func Archiving(wg *sync.WaitGroup, ctx context.Context) { | |||||||
| 	}() | 	}() | ||||||
| } | } | ||||||
|  |  | ||||||
| var ErrNoNewData error = errors.New("all data already archived") | var ErrNoNewArchiveData error = errors.New("all data already archived") | ||||||
|  |  | ||||||
| // ZIP all checkpoint files older than `from` together and write them to the `archiveDir`, | // ZIP all checkpoint files older than `from` together and write them to the `archiveDir`, | ||||||
| // deleting them from the `checkpointsDir`. | // deleting them from the `checkpointsDir`. | ||||||
| @@ -129,7 +129,7 @@ func archiveCheckpoints(dir string, archiveDir string, from int64, deleteInstead | |||||||
| 		return 0, err | 		return 0, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	extension := config.MetricStoreKeys.Checkpoints.FileFormat | 	extension := Keys.Checkpoints.FileFormat | ||||||
| 	files, err := findFiles(entries, from, extension, false) | 	files, err := findFiles(entries, from, extension, false) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return 0, err | 		return 0, err | ||||||
|   | |||||||
| @@ -2,7 +2,8 @@ | |||||||
| // All rights reserved. This file is part of cc-backend. | // All rights reserved. This file is part of cc-backend. | ||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| package avro | 
 | ||||||
|  | package memorystore | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bufio" | 	"bufio" | ||||||
| @@ -19,12 +20,11 @@ import ( | |||||||
| 	"sync/atomic" | 	"sync/atomic" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/ClusterCockpit/cc-backend/internal/config" |  | ||||||
| 	"github.com/ClusterCockpit/cc-lib/schema" | 	"github.com/ClusterCockpit/cc-lib/schema" | ||||||
| 	"github.com/linkedin/goavro/v2" | 	"github.com/linkedin/goavro/v2" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var NumWorkers int = 4 | var NumAvroWorkers int = 4 | ||||||
| 
 | 
 | ||||||
| var ErrNoNewData error = errors.New("no data in the pool") | var ErrNoNewData error = errors.New("no data in the pool") | ||||||
| 
 | 
 | ||||||
| @@ -58,9 +58,9 @@ func (as *AvroStore) ToCheckpoint(dir string, dumpAll bool) (int, error) { | |||||||
| 	n, errs := int32(0), int32(0) | 	n, errs := int32(0), int32(0) | ||||||
| 
 | 
 | ||||||
| 	var wg sync.WaitGroup | 	var wg sync.WaitGroup | ||||||
| 	wg.Add(NumWorkers) | 	wg.Add(NumAvroWorkers) | ||||||
| 	work := make(chan workItem, NumWorkers*2) | 	work := make(chan workItem, NumAvroWorkers*2) | ||||||
| 	for range NumWorkers { | 	for range NumAvroWorkers { | ||||||
| 		go func() { | 		go func() { | ||||||
| 			defer wg.Done() | 			defer wg.Done() | ||||||
| 
 | 
 | ||||||
| @@ -68,7 +68,7 @@ func (as *AvroStore) ToCheckpoint(dir string, dumpAll bool) (int, error) { | |||||||
| 				from := getTimestamp(workItem.dir) | 				from := getTimestamp(workItem.dir) | ||||||
| 
 | 
 | ||||||
| 				if err := workItem.level.toCheckpoint(workItem.dir, from, dumpAll); err != nil { | 				if err := workItem.level.toCheckpoint(workItem.dir, from, dumpAll); err != nil { | ||||||
| 					if err == ErrNoNewData { | 					if err == ErrNoNewArchiveData { | ||||||
| 						continue | 						continue | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| @@ -113,7 +113,7 @@ func getTimestamp(dir string) int64 { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return 0 | 		return 0 | ||||||
| 	} | 	} | ||||||
| 	var maxTs int64 = 0 | 	var maxTS int64 = 0 | ||||||
| 
 | 
 | ||||||
| 	if len(files) == 0 { | 	if len(files) == 0 { | ||||||
| 		return 0 | 		return 0 | ||||||
| @@ -135,19 +135,19 @@ func getTimestamp(dir string) int64 { | |||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if ts > maxTs { | 		if ts > maxTS { | ||||||
| 			maxTs = ts | 			maxTS = ts | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	interval, _ := time.ParseDuration(config.MetricStoreKeys.Checkpoints.Interval) | 	interval, _ := time.ParseDuration(Keys.Checkpoints.Interval) | ||||||
| 	updateTime := time.Unix(maxTs, 0).Add(interval).Add(time.Duration(CheckpointBufferMinutes-1) * time.Minute).Unix() | 	updateTime := time.Unix(maxTS, 0).Add(interval).Add(time.Duration(CheckpointBufferMinutes-1) * time.Minute).Unix() | ||||||
| 
 | 
 | ||||||
| 	if updateTime < time.Now().Unix() { | 	if updateTime < time.Now().Unix() { | ||||||
| 		return 0 | 		return 0 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return maxTs | 	return maxTS | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (l *AvroLevel) toCheckpoint(dir string, from int64, dumpAll bool) error { | func (l *AvroLevel) toCheckpoint(dir string, from int64, dumpAll bool) error { | ||||||
| @@ -156,27 +156,27 @@ func (l *AvroLevel) toCheckpoint(dir string, from int64, dumpAll bool) error { | |||||||
| 
 | 
 | ||||||
| 	// fmt.Printf("Checkpointing directory: %s\n", dir) | 	// fmt.Printf("Checkpointing directory: %s\n", dir) | ||||||
| 	// filepath contains the resolution | 	// filepath contains the resolution | ||||||
| 	int_res, _ := strconv.Atoi(path.Base(dir)) | 	intRes, _ := strconv.Atoi(path.Base(dir)) | ||||||
| 
 | 
 | ||||||
| 	// find smallest overall timestamp in l.data map and delete it from l.data | 	// find smallest overall timestamp in l.data map and delete it from l.data | ||||||
| 	minTs := int64(1<<63 - 1) | 	minTS := int64(1<<63 - 1) | ||||||
| 	for ts, dat := range l.data { | 	for ts, dat := range l.data { | ||||||
| 		if ts < minTs && len(dat) != 0 { | 		if ts < minTS && len(dat) != 0 { | ||||||
| 			minTs = ts | 			minTS = ts | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if from == 0 && minTs != int64(1<<63-1) { | 	if from == 0 && minTS != int64(1<<63-1) { | ||||||
| 		from = minTs | 		from = minTS | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if from == 0 { | 	if from == 0 { | ||||||
| 		return ErrNoNewData | 		return ErrNoNewArchiveData | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var schema string | 	var schema string | ||||||
| 	var codec *goavro.Codec | 	var codec *goavro.Codec | ||||||
| 	record_list := make([]map[string]any, 0) | 	recordList := make([]map[string]any, 0) | ||||||
| 
 | 
 | ||||||
| 	var f *os.File | 	var f *os.File | ||||||
| 
 | 
 | ||||||
| @@ -208,19 +208,19 @@ func (l *AvroLevel) toCheckpoint(dir string, from int64, dumpAll bool) error { | |||||||
| 		f.Close() | 		f.Close() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	time_ref := time.Now().Add(time.Duration(-CheckpointBufferMinutes+1) * time.Minute).Unix() | 	timeRef := time.Now().Add(time.Duration(-CheckpointBufferMinutes+1) * time.Minute).Unix() | ||||||
| 
 | 
 | ||||||
| 	if dumpAll { | 	if dumpAll { | ||||||
| 		time_ref = time.Now().Unix() | 		timeRef = time.Now().Unix() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Empty values | 	// Empty values | ||||||
| 	if len(l.data) == 0 { | 	if len(l.data) == 0 { | ||||||
| 		// we checkpoint avro files every 60 seconds | 		// we checkpoint avro files every 60 seconds | ||||||
| 		repeat := 60 / int_res | 		repeat := 60 / intRes | ||||||
| 
 | 
 | ||||||
| 		for range repeat { | 		for range repeat { | ||||||
| 			record_list = append(record_list, make(map[string]any)) | 			recordList = append(recordList, make(map[string]any)) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -228,15 +228,15 @@ func (l *AvroLevel) toCheckpoint(dir string, from int64, dumpAll bool) error { | |||||||
| 
 | 
 | ||||||
| 	for ts := range l.data { | 	for ts := range l.data { | ||||||
| 		flag := false | 		flag := false | ||||||
| 		if ts < time_ref { | 		if ts < timeRef { | ||||||
| 			data := l.data[ts] | 			data := l.data[ts] | ||||||
| 
 | 
 | ||||||
| 			schema_gen, err := generateSchema(data) | 			schemaGen, err := generateSchema(data) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			flag, schema, err = compareSchema(schema, schema_gen) | 			flag, schema, err = compareSchema(schema, schemaGen) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return fmt.Errorf("failed to compare read and generated schema: %v", err) | 				return fmt.Errorf("failed to compare read and generated schema: %v", err) | ||||||
| 			} | 			} | ||||||
| @@ -262,7 +262,7 @@ func (l *AvroLevel) toCheckpoint(dir string, from int64, dumpAll bool) error { | |||||||
| 						return fmt.Errorf("failed to read record: %v", err) | 						return fmt.Errorf("failed to read record: %v", err) | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					record_list = append(record_list, record.(map[string]any)) | 					recordList = append(recordList, record.(map[string]any)) | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				f.Close() | 				f.Close() | ||||||
| @@ -279,13 +279,13 @@ func (l *AvroLevel) toCheckpoint(dir string, from int64, dumpAll bool) error { | |||||||
| 				return fmt.Errorf("failed to create codec after merged schema: %v", err) | 				return fmt.Errorf("failed to create codec after merged schema: %v", err) | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			record_list = append(record_list, generateRecord(data)) | 			recordList = append(recordList, generateRecord(data)) | ||||||
| 			delete(l.data, ts) | 			delete(l.data, ts) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if len(record_list) == 0 { | 	if len(recordList) == 0 { | ||||||
| 		return ErrNoNewData | 		return ErrNoNewArchiveData | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	f, err = os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0o644) | 	f, err = os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0o644) | ||||||
| @@ -305,7 +305,7 @@ func (l *AvroLevel) toCheckpoint(dir string, from int64, dumpAll bool) error { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Append the new record | 	// Append the new record | ||||||
| 	if err := writer.Append(record_list); err != nil { | 	if err := writer.Append(recordList); err != nil { | ||||||
| 		return fmt.Errorf("failed to append record: %v", err) | 		return fmt.Errorf("failed to append record: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -401,12 +401,12 @@ func compareSchema(schemaRead, schemaGen string) (bool, string, error) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Marshal the merged schema back to JSON | 	// Marshal the merged schema back to JSON | ||||||
| 	mergedSchemaJson, err := json.Marshal(mergedSchema) | 	mergedSchemaJSON, err := json.Marshal(mergedSchema) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return false, "", fmt.Errorf("failed to marshal merged schema: %v", err) | 		return false, "", fmt.Errorf("failed to marshal merged schema: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return true, string(mergedSchemaJson), nil | 	return true, string(mergedSchemaJSON), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func generateSchema(data map[string]schema.Float) (string, error) { | func generateSchema(data map[string]schema.Float) (string, error) { | ||||||
| @@ -2,7 +2,8 @@ | |||||||
| // All rights reserved. This file is part of cc-backend. | // All rights reserved. This file is part of cc-backend. | ||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| package avro | 
 | ||||||
|  | package memorystore | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -10,14 +11,12 @@ import ( | |||||||
| 	"slices" | 	"slices" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"sync" | 	"sync" | ||||||
| 
 |  | ||||||
| 	"github.com/ClusterCockpit/cc-backend/internal/config" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func DataStaging(wg *sync.WaitGroup, ctx context.Context) { | func DataStaging(wg *sync.WaitGroup, ctx context.Context) { | ||||||
| 	// AvroPool is a pool of Avro writers. | 	// AvroPool is a pool of Avro writers. | ||||||
| 	go func() { | 	go func() { | ||||||
| 		if config.MetricStoreKeys.Checkpoints.FileFormat == "json" { | 		if Keys.Checkpoints.FileFormat == "json" { | ||||||
| 			wg.Done() // Mark this goroutine as done | 			wg.Done() // Mark this goroutine as done | ||||||
| 			return    // Exit the goroutine | 			return    // Exit the goroutine | ||||||
| 		} | 		} | ||||||
| @@ -33,7 +32,7 @@ func DataStaging(wg *sync.WaitGroup, ctx context.Context) { | |||||||
| 				return | 				return | ||||||
| 			case val := <-LineProtocolMessages: | 			case val := <-LineProtocolMessages: | ||||||
| 				// Fetch the frequency of the metric from the global configuration | 				// Fetch the frequency of the metric from the global configuration | ||||||
| 				freq, err := config.GetMetricFrequency(val.MetricName) | 				freq, err := GetMetricFrequency(val.MetricName) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Printf("Error fetching metric frequency: %s\n", err) | 					log.Printf("Error fetching metric frequency: %s\n", err) | ||||||
| 					continue | 					continue | ||||||
| @@ -41,8 +40,8 @@ func DataStaging(wg *sync.WaitGroup, ctx context.Context) { | |||||||
| 
 | 
 | ||||||
| 				metricName := "" | 				metricName := "" | ||||||
| 
 | 
 | ||||||
| 				for _, selector_name := range val.Selector { | 				for _, selectorName := range val.Selector { | ||||||
| 					metricName += selector_name + Delimiter | 					metricName += selectorName + Delimiter | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				metricName += val.MetricName | 				metricName += val.MetricName | ||||||
| @@ -2,7 +2,8 @@ | |||||||
| // All rights reserved. This file is part of cc-backend. | // All rights reserved. This file is part of cc-backend. | ||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| package avro | 
 | ||||||
|  | package memorystore | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"sync" | 	"sync" | ||||||
| @@ -112,18 +113,18 @@ func (l *AvroLevel) addMetric(metricName string, value schema.Float, timestamp i | |||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			// Get the last timestamp | 			// Get the last timestamp | ||||||
| 			var lastTs int64 | 			var lastTS int64 | ||||||
| 			for ts := range l.data { | 			for ts := range l.data { | ||||||
| 				if ts > lastTs { | 				if ts > lastTS { | ||||||
| 					lastTs = ts | 					lastTS = ts | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			// Create keys for the next KeyCounter timestamps | 			// Create keys for the next KeyCounter timestamps | ||||||
| 			l.data[lastTs+int64(Freq)] = make(map[string]schema.Float, 0) | 			l.data[lastTS+int64(Freq)] = make(map[string]schema.Float, 0) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	closestTs := int64(0) | 	closestTS := int64(0) | ||||||
| 	minDiff := int64(Freq) + 1 // Start with diff just outside the valid range | 	minDiff := int64(Freq) + 1 // Start with diff just outside the valid range | ||||||
| 	found := false | 	found := false | ||||||
| 
 | 
 | ||||||
| @@ -144,13 +145,13 @@ func (l *AvroLevel) addMetric(metricName string, value schema.Float, timestamp i | |||||||
| 		// Check if this is the closest timestamp so far | 		// Check if this is the closest timestamp so far | ||||||
| 		if Abs(diff) < minDiff { | 		if Abs(diff) < minDiff { | ||||||
| 			minDiff = Abs(diff) | 			minDiff = Abs(diff) | ||||||
| 			closestTs = ts | 			closestTS = ts | ||||||
| 			found = true | 			found = true | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if found { | 	if found { | ||||||
| 		l.data[closestTs][metricName] = value | 		l.data[closestTS][metricName] = value | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -1,3 +1,8 @@ | |||||||
|  | // Copyright (C) NHR@FAU, University Erlangen-Nuremberg. | ||||||
|  | // All rights reserved. This file is part of cc-backend. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| package memorystore | package memorystore | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -12,14 +17,14 @@ import ( | |||||||
| // in the buffer chain will be created if needed so that no copying | // in the buffer chain will be created if needed so that no copying | ||||||
| // of data or reallocation needs to happen on writes. | // of data or reallocation needs to happen on writes. | ||||||
| const ( | const ( | ||||||
| 	BUFFER_CAP int = 512 | 	BufferCap int = 512 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // So that we can reuse allocations | // So that we can reuse allocations | ||||||
| var bufferPool sync.Pool = sync.Pool{ | var bufferPool sync.Pool = sync.Pool{ | ||||||
| 	New: func() interface{} { | 	New: func() any { | ||||||
| 		return &buffer{ | 		return &buffer{ | ||||||
| 			data: make([]schema.Float, 0, BUFFER_CAP), | 			data: make([]schema.Float, 0, BufferCap), | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| } | } | ||||||
| @@ -192,7 +197,7 @@ func (b *buffer) free(t int64) (delme bool, n int) { | |||||||
| 		n += m | 		n += m | ||||||
| 		if delme { | 		if delme { | ||||||
| 			b.prev.next = nil | 			b.prev.next = nil | ||||||
| 			if cap(b.prev.data) == BUFFER_CAP { | 			if cap(b.prev.data) == BufferCap { | ||||||
| 				bufferPool.Put(b.prev) | 				bufferPool.Put(b.prev) | ||||||
| 			} | 			} | ||||||
| 			b.prev = nil | 			b.prev = nil | ||||||
|   | |||||||
| @@ -1,3 +1,8 @@ | |||||||
|  | // Copyright (C) NHR@FAU, University Erlangen-Nuremberg. | ||||||
|  | // All rights reserved. This file is part of cc-backend. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| package memorystore | package memorystore | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -19,8 +24,6 @@ import ( | |||||||
| 	"sync/atomic" | 	"sync/atomic" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/ClusterCockpit/cc-backend/internal/avro" |  | ||||||
| 	"github.com/ClusterCockpit/cc-backend/internal/config" |  | ||||||
| 	"github.com/ClusterCockpit/cc-lib/schema" | 	"github.com/ClusterCockpit/cc-lib/schema" | ||||||
| 	"github.com/linkedin/goavro/v2" | 	"github.com/linkedin/goavro/v2" | ||||||
| ) | ) | ||||||
| @@ -44,12 +47,12 @@ var lastCheckpoint time.Time | |||||||
| func Checkpointing(wg *sync.WaitGroup, ctx context.Context) { | func Checkpointing(wg *sync.WaitGroup, ctx context.Context) { | ||||||
| 	lastCheckpoint = time.Now() | 	lastCheckpoint = time.Now() | ||||||
|  |  | ||||||
| 	if config.MetricStoreKeys.Checkpoints.FileFormat == "json" { | 	if Keys.Checkpoints.FileFormat == "json" { | ||||||
| 		ms := GetMemoryStore() | 		ms := GetMemoryStore() | ||||||
|  |  | ||||||
| 		go func() { | 		go func() { | ||||||
| 			defer wg.Done() | 			defer wg.Done() | ||||||
| 			d, err := time.ParseDuration(config.MetricStoreKeys.Checkpoints.Interval) | 			d, err := time.ParseDuration(Keys.Checkpoints.Interval) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Fatal(err) | 				log.Fatal(err) | ||||||
| 			} | 			} | ||||||
| @@ -70,7 +73,7 @@ func Checkpointing(wg *sync.WaitGroup, ctx context.Context) { | |||||||
| 				case <-ticks: | 				case <-ticks: | ||||||
| 					log.Printf("[METRICSTORE]> start checkpointing (starting at %s)...\n", lastCheckpoint.Format(time.RFC3339)) | 					log.Printf("[METRICSTORE]> start checkpointing (starting at %s)...\n", lastCheckpoint.Format(time.RFC3339)) | ||||||
| 					now := time.Now() | 					now := time.Now() | ||||||
| 					n, err := ms.ToCheckpoint(config.MetricStoreKeys.Checkpoints.RootDir, | 					n, err := ms.ToCheckpoint(Keys.Checkpoints.RootDir, | ||||||
| 						lastCheckpoint.Unix(), now.Unix()) | 						lastCheckpoint.Unix(), now.Unix()) | ||||||
| 					if err != nil { | 					if err != nil { | ||||||
| 						log.Printf("[METRICSTORE]> checkpointing failed: %s\n", err.Error()) | 						log.Printf("[METRICSTORE]> checkpointing failed: %s\n", err.Error()) | ||||||
| @@ -89,9 +92,9 @@ func Checkpointing(wg *sync.WaitGroup, ctx context.Context) { | |||||||
| 			select { | 			select { | ||||||
| 			case <-ctx.Done(): | 			case <-ctx.Done(): | ||||||
| 				return | 				return | ||||||
| 			case <-time.After(time.Duration(avro.CheckpointBufferMinutes) * time.Minute): | 			case <-time.After(time.Duration(CheckpointBufferMinutes) * time.Minute): | ||||||
| 				// This is the first tick untill we collect the data for given minutes. | 				// This is the first tick untill we collect the data for given minutes. | ||||||
| 				avro.GetAvroStore().ToCheckpoint(config.MetricStoreKeys.Checkpoints.RootDir, false) | 				GetAvroStore().ToCheckpoint(Keys.Checkpoints.RootDir, false) | ||||||
| 				// log.Printf("Checkpointing %d avro files", count) | 				// log.Printf("Checkpointing %d avro files", count) | ||||||
|  |  | ||||||
| 			} | 			} | ||||||
| @@ -109,7 +112,7 @@ func Checkpointing(wg *sync.WaitGroup, ctx context.Context) { | |||||||
| 					return | 					return | ||||||
| 				case <-ticks: | 				case <-ticks: | ||||||
| 					// Regular ticks of 1 minute to write data. | 					// Regular ticks of 1 minute to write data. | ||||||
| 					avro.GetAvroStore().ToCheckpoint(config.MetricStoreKeys.Checkpoints.RootDir, false) | 					GetAvroStore().ToCheckpoint(Keys.Checkpoints.RootDir, false) | ||||||
| 					// log.Printf("Checkpointing %d avro files", count) | 					// log.Printf("Checkpointing %d avro files", count) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| @@ -176,7 +179,7 @@ func (m *MemoryStore) ToCheckpoint(dir string, from, to int64) (int, error) { | |||||||
|  |  | ||||||
| 			for workItem := range work { | 			for workItem := range work { | ||||||
| 				if err := workItem.level.toCheckpoint(workItem.dir, from, to, m); err != nil { | 				if err := workItem.level.toCheckpoint(workItem.dir, from, to, m); err != nil { | ||||||
| 					if err == ErrNoNewData { | 					if err == ErrNoNewArchiveData { | ||||||
| 						continue | 						continue | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
| @@ -219,7 +222,7 @@ func (l *Level) toCheckpointFile(from, to int64, m *MemoryStore) (*CheckpointFil | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for metric, minfo := range m.Metrics { | 	for metric, minfo := range m.Metrics { | ||||||
| 		b := l.metrics[minfo.Offset] | 		b := l.metrics[minfo.offset] | ||||||
| 		if b == nil { | 		if b == nil { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| @@ -278,7 +281,7 @@ func (l *Level) toCheckpoint(dir string, from, to int64, m *MemoryStore) error { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if cf == nil { | 	if cf == nil { | ||||||
| 		return ErrNoNewData | 		return ErrNoNewArchiveData | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	filepath := path.Join(dir, fmt.Sprintf("%d.json", from)) | 	filepath := path.Join(dir, fmt.Sprintf("%d.json", from)) | ||||||
| @@ -376,7 +379,7 @@ done: | |||||||
| func (m *MemoryStore) FromCheckpointFiles(dir string, from int64) (int, error) { | func (m *MemoryStore) FromCheckpointFiles(dir string, from int64) (int, error) { | ||||||
| 	if _, err := os.Stat(dir); os.IsNotExist(err) { | 	if _, err := os.Stat(dir); os.IsNotExist(err) { | ||||||
| 		// The directory does not exist, so create it using os.MkdirAll() | 		// The directory does not exist, so create it using os.MkdirAll() | ||||||
| 		err := os.MkdirAll(dir, 0755) // 0755 sets the permissions for the directory | 		err := os.MkdirAll(dir, 0o755) // 0755 sets the permissions for the directory | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatalf("[METRICSTORE]> Error creating directory: %#v\n", err) | 			log.Fatalf("[METRICSTORE]> Error creating directory: %#v\n", err) | ||||||
| 		} | 		} | ||||||
| @@ -384,7 +387,7 @@ func (m *MemoryStore) FromCheckpointFiles(dir string, from int64) (int, error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Config read (replace with your actual config read) | 	// Config read (replace with your actual config read) | ||||||
| 	fileFormat := config.MetricStoreKeys.Checkpoints.FileFormat | 	fileFormat := Keys.Checkpoints.FileFormat | ||||||
| 	if fileFormat == "" { | 	if fileFormat == "" { | ||||||
| 		fileFormat = "avro" | 		fileFormat = "avro" | ||||||
| 	} | 	} | ||||||
| @@ -445,10 +448,10 @@ func (l *Level) loadAvroFile(m *MemoryStore, f *os.File, from int64) error { | |||||||
| 		return fmt.Errorf("[METRICSTORE]> error while reading avro file (resolution parsing) : %s", err) | 		return fmt.Errorf("[METRICSTORE]> error while reading avro file (resolution parsing) : %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	from_timestamp, err := strconv.ParseInt(fileName[strings.Index(fileName, "_")+1:len(fileName)-5], 10, 64) | 	fromTimestamp, err := strconv.ParseInt(fileName[strings.Index(fileName, "_")+1:len(fileName)-5], 10, 64) | ||||||
|  |  | ||||||
| 	// Same logic according to lineprotocol | 	// Same logic according to lineprotocol | ||||||
| 	from_timestamp -= (resolution / 2) | 	fromTimestamp -= (resolution / 2) | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("[METRICSTORE]> error converting timestamp from the avro file : %s", err) | 		return fmt.Errorf("[METRICSTORE]> error converting timestamp from the avro file : %s", err) | ||||||
| @@ -472,7 +475,7 @@ func (l *Level) loadAvroFile(m *MemoryStore, f *os.File, from int64) error { | |||||||
| 			return fmt.Errorf("[METRICSTORE]> error while reading avro file : %s", err) | 			return fmt.Errorf("[METRICSTORE]> error while reading avro file : %s", err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		record, ok := datum.(map[string]interface{}) | 		record, ok := datum.(map[string]any) | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			panic("[METRICSTORE]> failed to assert datum as map[string]interface{}") | 			panic("[METRICSTORE]> failed to assert datum as map[string]interface{}") | ||||||
| 		} | 		} | ||||||
| @@ -484,16 +487,16 @@ func (l *Level) loadAvroFile(m *MemoryStore, f *os.File, from int64) error { | |||||||
| 		recordCounter += 1 | 		recordCounter += 1 | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	to := (from_timestamp + (recordCounter / (60 / resolution) * 60)) | 	to := (fromTimestamp + (recordCounter / (60 / resolution) * 60)) | ||||||
| 	if to < from { | 	if to < from { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for key, floatArray := range metricsData { | 	for key, floatArray := range metricsData { | ||||||
| 		metricName := avro.ReplaceKey(key) | 		metricName := ReplaceKey(key) | ||||||
|  |  | ||||||
| 		if strings.Contains(metricName, avro.Delimiter) { | 		if strings.Contains(metricName, Delimiter) { | ||||||
| 			subString := strings.Split(metricName, avro.Delimiter) | 			subString := strings.Split(metricName, Delimiter) | ||||||
|  |  | ||||||
| 			lvl := l | 			lvl := l | ||||||
|  |  | ||||||
| @@ -517,12 +520,12 @@ func (l *Level) loadAvroFile(m *MemoryStore, f *os.File, from int64) error { | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			leafMetricName := subString[len(subString)-1] | 			leafMetricName := subString[len(subString)-1] | ||||||
| 			err = lvl.createBuffer(m, leafMetricName, floatArray, from_timestamp, resolution) | 			err = lvl.createBuffer(m, leafMetricName, floatArray, fromTimestamp, resolution) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return fmt.Errorf("[METRICSTORE]> error while creating buffers from avroReader : %s", err) | 				return fmt.Errorf("[METRICSTORE]> error while creating buffers from avroReader : %s", err) | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			err = l.createBuffer(m, metricName, floatArray, from_timestamp, resolution) | 			err = l.createBuffer(m, metricName, floatArray, fromTimestamp, resolution) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return fmt.Errorf("[METRICSTORE]> error while creating buffers from avroReader : %s", err) | 				return fmt.Errorf("[METRICSTORE]> error while creating buffers from avroReader : %s", err) | ||||||
| 			} | 			} | ||||||
| @@ -551,9 +554,9 @@ func (l *Level) createBuffer(m *MemoryStore, metricName string, floatArray schem | |||||||
| 		// return errors.New("Unkown metric: " + name) | 		// return errors.New("Unkown metric: " + name) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	prev := l.metrics[minfo.Offset] | 	prev := l.metrics[minfo.offset] | ||||||
| 	if prev == nil { | 	if prev == nil { | ||||||
| 		l.metrics[minfo.Offset] = b | 		l.metrics[minfo.offset] = b | ||||||
| 	} else { | 	} else { | ||||||
| 		if prev.start > b.start { | 		if prev.start > b.start { | ||||||
| 			return errors.New("wooops") | 			return errors.New("wooops") | ||||||
| @@ -573,12 +576,12 @@ func (l *Level) createBuffer(m *MemoryStore, metricName string, floatArray schem | |||||||
| 			prev.data = prev.data[0:len(prev.data):len(prev.data)] | 			prev.data = prev.data[0:len(prev.data):len(prev.data)] | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	l.metrics[minfo.Offset] = b | 	l.metrics[minfo.offset] = b | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (l *Level) loadJsonFile(m *MemoryStore, f *os.File, from int64) error { | func (l *Level) loadJSONFile(m *MemoryStore, f *os.File, from int64) error { | ||||||
| 	br := bufio.NewReader(f) | 	br := bufio.NewReader(f) | ||||||
| 	cf := &CheckpointFile{} | 	cf := &CheckpointFile{} | ||||||
| 	if err := json.NewDecoder(br).Decode(cf); err != nil { | 	if err := json.NewDecoder(br).Decode(cf); err != nil { | ||||||
| @@ -615,9 +618,9 @@ func (l *Level) loadFile(cf *CheckpointFile, m *MemoryStore) error { | |||||||
| 			// return errors.New("Unkown metric: " + name) | 			// return errors.New("Unkown metric: " + name) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		prev := l.metrics[minfo.Offset] | 		prev := l.metrics[minfo.offset] | ||||||
| 		if prev == nil { | 		if prev == nil { | ||||||
| 			l.metrics[minfo.Offset] = b | 			l.metrics[minfo.offset] = b | ||||||
| 		} else { | 		} else { | ||||||
| 			if prev.start > b.start { | 			if prev.start > b.start { | ||||||
| 				return errors.New("wooops") | 				return errors.New("wooops") | ||||||
| @@ -626,7 +629,7 @@ func (l *Level) loadFile(cf *CheckpointFile, m *MemoryStore) error { | |||||||
| 			b.prev = prev | 			b.prev = prev | ||||||
| 			prev.next = b | 			prev.next = b | ||||||
| 		} | 		} | ||||||
| 		l.metrics[minfo.Offset] = b | 		l.metrics[minfo.offset] = b | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(cf.Children) > 0 && l.children == nil { | 	if len(cf.Children) > 0 && l.children == nil { | ||||||
| @@ -690,7 +693,7 @@ func (l *Level) fromCheckpoint(m *MemoryStore, dir string, from int64, extension | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	loaders := map[string]func(*MemoryStore, *os.File, int64) error{ | 	loaders := map[string]func(*MemoryStore, *os.File, int64) error{ | ||||||
| 		"json": l.loadJsonFile, | 		"json": l.loadJSONFile, | ||||||
| 		"avro": l.loadAvroFile, | 		"avro": l.loadAvroFile, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -736,7 +739,7 @@ func findFiles(direntries []fs.DirEntry, t int64, extension string, findMoreRece | |||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| 	filenames := make([]string, 0) | 	filenames := make([]string, 0) | ||||||
| 	for i := 0; i < len(direntries); i++ { | 	for i := range direntries { | ||||||
| 		e := direntries[i] | 		e := direntries[i] | ||||||
| 		ts1 := nums[e.Name()] | 		ts1 := nums[e.Name()] | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,10 +1,16 @@ | |||||||
| package config | // Copyright (C) NHR@FAU, University Erlangen-Nuremberg. | ||||||
|  | // All rights reserved. This file is part of cc-backend. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  | 
 | ||||||
|  | package memorystore | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/ClusterCockpit/cc-backend/internal/config" | ||||||
| 	cclog "github.com/ClusterCockpit/cc-lib/ccLogger" | 	cclog "github.com/ClusterCockpit/cc-lib/ccLogger" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @@ -50,7 +56,7 @@ type NatsConfig struct { | |||||||
| 	} `json:"subscriptions"` | 	} `json:"subscriptions"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var MetricStoreKeys MetricStoreConfig | var Keys MetricStoreConfig | ||||||
| 
 | 
 | ||||||
| // AggregationStrategy for aggregation over multiple values at different cpus/sockets/..., not time! | // AggregationStrategy for aggregation over multiple values at different cpus/sockets/..., not time! | ||||||
| type AggregationStrategy int | type AggregationStrategy int | ||||||
| @@ -75,24 +81,26 @@ func AssignAggregationStratergy(str string) (AggregationStrategy, error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type MetricConfig struct { | type MetricConfig struct { | ||||||
| 	// Interval in seconds at which measurements will arive. | 	// Interval in seconds at which measurements are stored | ||||||
| 	Frequency int64 | 	Frequency int64 | ||||||
| 
 | 
 | ||||||
| 	// Can be 'sum', 'avg' or null. Describes how to aggregate metrics from the same timestep over the hierarchy. | 	// Can be 'sum', 'avg' or null. Describes how to aggregate metrics from the same timestep over the hierarchy. | ||||||
| 	Aggregation AggregationStrategy | 	Aggregation AggregationStrategy | ||||||
| 
 | 
 | ||||||
| 	// Private, used internally... | 	// Private, used internally... | ||||||
| 	Offset int | 	offset int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var Metrics map[string]MetricConfig | var Metrics map[string]MetricConfig | ||||||
| 
 | 
 | ||||||
| func InitMetricStore(msConfig json.RawMessage) { | func InitMetricStore(rawConfig json.RawMessage) { | ||||||
| 	// Validate(msConfigSchema, msConfig) | 	if rawConfig != nil { | ||||||
| 	dec := json.NewDecoder(bytes.NewReader(msConfig)) | 		config.Validate(configSchema, rawConfig) | ||||||
| 	// dec.DisallowUnknownFields() | 		dec := json.NewDecoder(bytes.NewReader(rawConfig)) | ||||||
| 	if err := dec.Decode(&MetricStoreKeys); err != nil { | 		// dec.DisallowUnknownFields() | ||||||
| 		cclog.Abortf("[METRICSTORE]> Metric Store Config Init: Could not decode config file '%s'.\nError: %s\n", msConfig, err.Error()) | 		if err := dec.Decode(&Keys); err != nil { | ||||||
|  | 			cclog.Abortf("[METRICSTORE]> Metric Store Config Init: Could not decode config file '%s'.\nError: %s\n", rawConfig, err.Error()) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
							
								
								
									
										190
									
								
								internal/memorystore/configSchema.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								internal/memorystore/configSchema.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,190 @@ | |||||||
|  | // Copyright (C) NHR@FAU, University Erlangen-Nuremberg. | ||||||
|  | // All rights reserved. This file is part of cc-backend. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package memorystore | ||||||
|  |  | ||||||
|  | const configSchema = `{ | ||||||
|  |   "type": "object", | ||||||
|  |   "properties": { | ||||||
|  |     "jobList": { | ||||||
|  |       "description": "Job list defaults. Applies to user- and jobs views.", | ||||||
|  |       "type": "object", | ||||||
|  |       "properties": { | ||||||
|  |         "usePaging": { | ||||||
|  |           "description": "If classic paging is used instead of continuous scrolling by default.", | ||||||
|  |           "type": "boolean" | ||||||
|  |         }, | ||||||
|  |         "showFootprint": { | ||||||
|  |           "description": "If footprint bars are shown as first column by default.", | ||||||
|  |           "type": "boolean" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "nodeList": { | ||||||
|  |       "description": "Node list defaults. Applies to node list view.", | ||||||
|  |       "type": "object", | ||||||
|  |       "properties": { | ||||||
|  |         "usePaging": { | ||||||
|  |           "description": "If classic paging is used instead of continuous scrolling by default.", | ||||||
|  |           "type": "boolean" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "jobView": { | ||||||
|  |       "description": "Job view defaults.", | ||||||
|  |       "type": "object", | ||||||
|  |       "properties": { | ||||||
|  |         "showPolarPlot": { | ||||||
|  |           "description": "If the job metric footprints polar plot is shown by default.", | ||||||
|  |           "type": "boolean" | ||||||
|  |         }, | ||||||
|  |         "showFootprint": { | ||||||
|  |           "description": "If the annotated job metric footprint bars are shown by default.", | ||||||
|  |           "type": "boolean" | ||||||
|  |         }, | ||||||
|  |         "showRoofline": { | ||||||
|  |           "description": "If the job roofline plot is shown by default.", | ||||||
|  |           "type": "boolean" | ||||||
|  |         }, | ||||||
|  |         "showStatTable": { | ||||||
|  |           "description": "If the job metric statistics table is shown by default.", | ||||||
|  |           "type": "boolean" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "metricConfig": { | ||||||
|  |       "description": "Global initial metric selections for primary views of all clusters.", | ||||||
|  |       "type": "object", | ||||||
|  |       "properties": { | ||||||
|  |         "jobListMetrics": { | ||||||
|  |           "description": "Initial metrics shown for new users in job lists (User and jobs view).", | ||||||
|  |           "type": "array", | ||||||
|  |           "items": { | ||||||
|  |             "type": "string", | ||||||
|  |             "minItems": 1 | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "jobViewPlotMetrics": { | ||||||
|  |           "description": "Initial metrics shown for new users as job view metric plots.", | ||||||
|  |           "type": "array", | ||||||
|  |           "items": { | ||||||
|  |             "type": "string", | ||||||
|  |             "minItems": 1 | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "jobViewTableMetrics": { | ||||||
|  |           "description": "Initial metrics shown for new users in job view statistics table.", | ||||||
|  |           "type": "array", | ||||||
|  |           "items": { | ||||||
|  |             "type": "string", | ||||||
|  |             "minItems": 1 | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "clusters": { | ||||||
|  |           "description": "Overrides for global defaults by cluster and subcluster.", | ||||||
|  |           "type": "array", | ||||||
|  |           "items": { | ||||||
|  |             "type": "object", | ||||||
|  |             "properties": { | ||||||
|  |               "name": { | ||||||
|  |                 "description": "The name of the cluster." | ||||||
|  |               }, | ||||||
|  |               "jobListMetrics": { | ||||||
|  |                 "description": "Initial metrics shown for new users in job lists (User and jobs view) for subcluster.", | ||||||
|  |                 "type": "array", | ||||||
|  |                 "items": { | ||||||
|  |                   "type": "string", | ||||||
|  |                   "minItems": 1 | ||||||
|  |                 } | ||||||
|  |               }, | ||||||
|  |               "jobViewPlotMetrics": { | ||||||
|  |                 "description": "Initial metrics shown for new users as job view timeplots for subcluster.", | ||||||
|  |                 "type": "array", | ||||||
|  |                 "items": { | ||||||
|  |                   "type": "string", | ||||||
|  |                   "minItems": 1 | ||||||
|  |                 } | ||||||
|  |               }, | ||||||
|  |               "jobViewTableMetrics": { | ||||||
|  |                 "description": "Initial metrics shown for new users in job view statistics table for subcluster.", | ||||||
|  |                 "type": "array", | ||||||
|  |                 "items": { | ||||||
|  |                   "type": "string", | ||||||
|  |                   "minItems": 1 | ||||||
|  |                 } | ||||||
|  |               }, | ||||||
|  |               "subClusters": { | ||||||
|  |                 "description": "The array of overrides per subcluster.", | ||||||
|  |                 "type": "array", | ||||||
|  |                 "items": { | ||||||
|  |                   "type": "object", | ||||||
|  |                   "properties": { | ||||||
|  |                     "name": { | ||||||
|  |                       "description": "The name of the subcluster.", | ||||||
|  |                       "type": "string" | ||||||
|  |                     }, | ||||||
|  |                     "jobListMetrics": { | ||||||
|  |                       "description": "Initial metrics shown for new users in job lists (User and jobs view) for subcluster.", | ||||||
|  |                       "type": "array", | ||||||
|  |                       "items": { | ||||||
|  |                         "type": "string", | ||||||
|  |                         "minItems": 1 | ||||||
|  |                       } | ||||||
|  |                     }, | ||||||
|  |                     "jobViewPlotMetrics": { | ||||||
|  |                       "description": "Initial metrics shown for new users as job view timeplots for subcluster.", | ||||||
|  |                       "type": "array", | ||||||
|  |                       "items": { | ||||||
|  |                         "type": "string", | ||||||
|  |                         "minItems": 1 | ||||||
|  |                       } | ||||||
|  |                     }, | ||||||
|  |                     "jobViewTableMetrics": { | ||||||
|  |                       "description": "Initial metrics shown for new users in job view statistics table for subcluster.", | ||||||
|  |                       "type": "array", | ||||||
|  |                       "items": { | ||||||
|  |                         "type": "string", | ||||||
|  |                         "minItems": 1 | ||||||
|  |                       } | ||||||
|  |                     } | ||||||
|  |                   }, | ||||||
|  |                   "required": ["name"], | ||||||
|  |                   "minItems": 1 | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |             }, | ||||||
|  |             "required": ["name", "subClusters"], | ||||||
|  |             "minItems": 1 | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "plotConfiguration": { | ||||||
|  |       "description": "Initial settings for plot render options.", | ||||||
|  |       "type": "object", | ||||||
|  |       "properties": { | ||||||
|  |         "colorBackground": { | ||||||
|  |           "description": "If the metric plot backgrounds are initially colored by threshold limits.", | ||||||
|  |           "type": "boolean" | ||||||
|  |         }, | ||||||
|  |         "plotsPerRow": { | ||||||
|  |           "description": "How many plots are initially rendered in per row. Applies to job, single node, and analysis views.", | ||||||
|  |           "type": "integer" | ||||||
|  |         }, | ||||||
|  |         "lineWidth": { | ||||||
|  |           "description": "Initial thickness of rendered plotlines. Applies to metric plot, job compare plot and roofline.", | ||||||
|  |           "type": "integer" | ||||||
|  |         }, | ||||||
|  |         "colorScheme": { | ||||||
|  |           "description": "Initial colorScheme to be used for metric plots.", | ||||||
|  |           "type": "array", | ||||||
|  |           "items": { | ||||||
|  |             "type": "string" | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }` | ||||||
| @@ -1,3 +1,8 @@ | |||||||
|  | // Copyright (C) NHR@FAU, University Erlangen-Nuremberg. | ||||||
|  | // All rights reserved. This file is part of cc-backend. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| package memorystore | package memorystore | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -41,7 +46,7 @@ func (l *Level) debugDump(m *MemoryStore, w *bufio.Writer, lvlname string, buf [ | |||||||
| 	depth += 1 | 	depth += 1 | ||||||
| 	objitems := 0 | 	objitems := 0 | ||||||
| 	for name, mc := range m.Metrics { | 	for name, mc := range m.Metrics { | ||||||
| 		if b := l.metrics[mc.Offset]; b != nil { | 		if b := l.metrics[mc.offset]; b != nil { | ||||||
| 			for i := 0; i < depth; i++ { | 			for i := 0; i < depth; i++ { | ||||||
| 				buf = append(buf, '\t') | 				buf = append(buf, '\t') | ||||||
| 			} | 			} | ||||||
|   | |||||||
| @@ -1,3 +1,8 @@ | |||||||
|  | // Copyright (C) NHR@FAU, University Erlangen-Nuremberg. | ||||||
|  | // All rights reserved. This file is part of cc-backend. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| package memorystore | package memorystore | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -6,29 +11,28 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // This is a threshold that allows a node to be healthy with certain number of data points missing. | // MaxMissingDataPoints is a threshold that allows a node to be healthy with certain number of data points missing. | ||||||
| // Suppose a node does not receive last 5 data points, then healthCheck endpoint will still say a | // Suppose a node does not receive last 5 data points, then healthCheck endpoint will still say a | ||||||
| // node is healthy. Anything more than 5 missing points in metrics of the node will deem the node unhealthy. | // node is healthy. Anything more than 5 missing points in metrics of the node will deem the node unhealthy. | ||||||
| const MaxMissingDataPoints int64 = 5 | const MaxMissingDataPoints int64 = 5 | ||||||
|  |  | ||||||
| // This is a threshold which allows upto certain number of metrics in a node to be unhealthly. | // MaxUnhealthyMetrics is a threshold which allows upto certain number of metrics in a node to be unhealthly. | ||||||
| // Works with MaxMissingDataPoints. Say 5 metrics (including submetrics) do not receive the last | // Works with MaxMissingDataPoints. Say 5 metrics (including submetrics) do not receive the last | ||||||
| // MaxMissingDataPoints data points, then the node will be deemed healthy. Any more metrics that does | // MaxMissingDataPoints data points, then the node will be deemed healthy. Any more metrics that does | ||||||
| // not receive data for MaxMissingDataPoints data points will deem the node unhealthy. | // not receive data for MaxMissingDataPoints data points will deem the node unhealthy. | ||||||
| const MaxUnhealthyMetrics int64 = 5 | const MaxUnhealthyMetrics int64 = 5 | ||||||
|  |  | ||||||
| func (b *buffer) healthCheck() int64 { | func (b *buffer) healthCheck() int64 { | ||||||
|  |  | ||||||
| 	// Check if the buffer is empty | 	// Check if the buffer is empty | ||||||
| 	if b.data == nil { | 	if b.data == nil { | ||||||
| 		return 1 | 		return 1 | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	buffer_end := b.start + b.frequency*int64(len(b.data)) | 	bufferEnd := b.start + b.frequency*int64(len(b.data)) | ||||||
| 	t := time.Now().Unix() | 	t := time.Now().Unix() | ||||||
|  |  | ||||||
| 	// Check if the buffer is too old | 	// Check if the buffer is too old | ||||||
| 	if t-buffer_end > MaxMissingDataPoints*b.frequency { | 	if t-bufferEnd > MaxMissingDataPoints*b.frequency { | ||||||
| 		return 1 | 		return 1 | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -40,7 +44,7 @@ func (l *Level) healthCheck(m *MemoryStore, count int64) (int64, error) { | |||||||
| 	defer l.lock.RUnlock() | 	defer l.lock.RUnlock() | ||||||
|  |  | ||||||
| 	for _, mc := range m.Metrics { | 	for _, mc := range m.Metrics { | ||||||
| 		if b := l.metrics[mc.Offset]; b != nil { | 		if b := l.metrics[mc.offset]; b != nil { | ||||||
| 			count += b.healthCheck() | 			count += b.healthCheck() | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -1,3 +1,8 @@ | |||||||
|  | // Copyright (C) NHR@FAU, University Erlangen-Nuremberg. | ||||||
|  | // All rights reserved. This file is part of cc-backend. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| package memorystore | package memorystore | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -77,7 +82,7 @@ func (l *Level) free(t int64) (int, error) { | |||||||
| 			delme, m := b.free(t) | 			delme, m := b.free(t) | ||||||
| 			n += m | 			n += m | ||||||
| 			if delme { | 			if delme { | ||||||
| 				if cap(b.data) == BUFFER_CAP { | 				if cap(b.data) == BufferCap { | ||||||
| 					bufferPool.Put(b) | 					bufferPool.Put(b) | ||||||
| 				} | 				} | ||||||
| 				l.metrics[i] = nil | 				l.metrics[i] = nil | ||||||
|   | |||||||
| @@ -1,3 +1,8 @@ | |||||||
|  | // Copyright (C) NHR@FAU, University Erlangen-Nuremberg. | ||||||
|  | // All rights reserved. This file is part of cc-backend. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| package memorystore | package memorystore | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -7,8 +12,6 @@ import ( | |||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/ClusterCockpit/cc-backend/internal/avro" |  | ||||||
| 	"github.com/ClusterCockpit/cc-backend/internal/config" |  | ||||||
| 	"github.com/ClusterCockpit/cc-lib/schema" | 	"github.com/ClusterCockpit/cc-lib/schema" | ||||||
| 	"github.com/influxdata/line-protocol/v2/lineprotocol" | 	"github.com/influxdata/line-protocol/v2/lineprotocol" | ||||||
| 	"github.com/nats-io/nats.go" | 	"github.com/nats-io/nats.go" | ||||||
| @@ -77,10 +80,10 @@ import ( | |||||||
| // 	return nil | // 	return nil | ||||||
| // } | // } | ||||||
|  |  | ||||||
| // Connect to a nats server and subscribe to "updates". This is a blocking | // ReceiveNats connects to a nats server and subscribes to "updates". This is a | ||||||
| // function. handleLine will be called for each line recieved via nats. | // blocking function. handleLine will be called for each line recieved via | ||||||
| // Send `true` through the done channel for gracefull termination. | // nats. Send `true` through the done channel for gracefull termination. | ||||||
| func ReceiveNats(conf *(config.NatsConfig), | func ReceiveNats(conf *(NatsConfig), | ||||||
| 	ms *MemoryStore, | 	ms *MemoryStore, | ||||||
| 	workers int, | 	workers int, | ||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| @@ -170,7 +173,7 @@ func reorder(buf, prefix []byte) []byte { | |||||||
| 		for i := m - 1; i >= 0; i-- { | 		for i := m - 1; i >= 0; i-- { | ||||||
| 			buf[i+n] = buf[i] | 			buf[i+n] = buf[i] | ||||||
| 		} | 		} | ||||||
| 		for i := 0; i < n; i++ { | 		for i := range n { | ||||||
| 			buf[i] = prefix[i] | 			buf[i] = prefix[i] | ||||||
| 		} | 		} | ||||||
| 		return buf | 		return buf | ||||||
| @@ -329,14 +332,15 @@ func decodeLine(dec *lineprotocol.Decoder, | |||||||
|  |  | ||||||
| 		time := t.Unix() | 		time := t.Unix() | ||||||
|  |  | ||||||
| 		if config.MetricStoreKeys.Checkpoints.FileFormat != "json" { | 		if Keys.Checkpoints.FileFormat != "json" { | ||||||
| 			avro.LineProtocolMessages <- &avro.AvroStruct{ | 			LineProtocolMessages <- &AvroStruct{ | ||||||
| 				MetricName: string(metricBuf), | 				MetricName: string(metricBuf), | ||||||
| 				Cluster:    cluster, | 				Cluster:    cluster, | ||||||
| 				Node:       host, | 				Node:       host, | ||||||
| 				Selector:   append([]string{}, selector...), | 				Selector:   append([]string{}, selector...), | ||||||
| 				Value:      metric.Value, | 				Value:      metric.Value, | ||||||
| 				Timestamp:  time} | 				Timestamp:  time, | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if err := ms.WriteToLevel(lvl, selector, time, []Metric{metric}); err != nil { | 		if err := ms.WriteToLevel(lvl, selector, time, []Metric{metric}); err != nil { | ||||||
|   | |||||||
| @@ -1,3 +1,8 @@ | |||||||
|  | // Copyright (C) NHR@FAU, University Erlangen-Nuremberg. | ||||||
|  | // All rights reserved. This file is part of cc-backend. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| package memorystore | package memorystore | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -11,8 +16,6 @@ import ( | |||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/ClusterCockpit/cc-backend/internal/avro" |  | ||||||
| 	"github.com/ClusterCockpit/cc-backend/internal/config" |  | ||||||
| 	"github.com/ClusterCockpit/cc-lib/resampler" | 	"github.com/ClusterCockpit/cc-lib/resampler" | ||||||
| 	"github.com/ClusterCockpit/cc-lib/runtimeEnv" | 	"github.com/ClusterCockpit/cc-lib/runtimeEnv" | ||||||
| 	"github.com/ClusterCockpit/cc-lib/schema" | 	"github.com/ClusterCockpit/cc-lib/schema" | ||||||
| @@ -30,39 +33,36 @@ var NumWorkers int = 4 | |||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	maxWorkers := 10 | 	maxWorkers := 10 | ||||||
| 	NumWorkers = runtime.NumCPU()/2 + 1 | 	NumWorkers = min(runtime.NumCPU()/2+1, maxWorkers) | ||||||
| 	if NumWorkers > maxWorkers { |  | ||||||
| 		NumWorkers = maxWorkers |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| type Metric struct { | type Metric struct { | ||||||
| 	Name         string | 	Name         string | ||||||
| 	Value        schema.Float | 	Value        schema.Float | ||||||
| 	MetricConfig config.MetricConfig | 	MetricConfig MetricConfig | ||||||
| } | } | ||||||
|  |  | ||||||
| type MemoryStore struct { | type MemoryStore struct { | ||||||
| 	Metrics map[string]config.MetricConfig | 	Metrics map[string]MetricConfig | ||||||
| 	root    Level | 	root    Level | ||||||
| } | } | ||||||
|  |  | ||||||
| func Init(wg *sync.WaitGroup) { | func Init(wg *sync.WaitGroup) { | ||||||
| 	startupTime := time.Now() | 	startupTime := time.Now() | ||||||
|  |  | ||||||
| 	//Pass the config.MetricStoreKeys | 	// Pass the config.MetricStoreKeys | ||||||
| 	InitMetrics(config.Metrics) | 	InitMetrics(Metrics) | ||||||
|  |  | ||||||
| 	ms := GetMemoryStore() | 	ms := GetMemoryStore() | ||||||
|  |  | ||||||
| 	d, err := time.ParseDuration(config.MetricStoreKeys.Checkpoints.Restore) | 	d, err := time.ParseDuration(Keys.Checkpoints.Restore) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	restoreFrom := startupTime.Add(-d) | 	restoreFrom := startupTime.Add(-d) | ||||||
| 	log.Printf("[METRICSTORE]> Loading checkpoints newer than %s\n", restoreFrom.Format(time.RFC3339)) | 	log.Printf("[METRICSTORE]> Loading checkpoints newer than %s\n", restoreFrom.Format(time.RFC3339)) | ||||||
| 	files, err := ms.FromCheckpointFiles(config.MetricStoreKeys.Checkpoints.RootDir, restoreFrom.Unix()) | 	files, err := ms.FromCheckpointFiles(Keys.Checkpoints.RootDir, restoreFrom.Unix()) | ||||||
| 	loadedData := ms.SizeInBytes() / 1024 / 1024 // In MB | 	loadedData := ms.SizeInBytes() / 1024 / 1024 // In MB | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatalf("[METRICSTORE]> Loading checkpoints failed: %s\n", err.Error()) | 		log.Fatalf("[METRICSTORE]> Loading checkpoints failed: %s\n", err.Error()) | ||||||
| @@ -85,7 +85,7 @@ func Init(wg *sync.WaitGroup) { | |||||||
| 	Retention(wg, ctx) | 	Retention(wg, ctx) | ||||||
| 	Checkpointing(wg, ctx) | 	Checkpointing(wg, ctx) | ||||||
| 	Archiving(wg, ctx) | 	Archiving(wg, ctx) | ||||||
| 	avro.DataStaging(wg, ctx) | 	DataStaging(wg, ctx) | ||||||
|  |  | ||||||
| 	wg.Add(1) | 	wg.Add(1) | ||||||
| 	sigs := make(chan os.Signal, 1) | 	sigs := make(chan os.Signal, 1) | ||||||
| @@ -97,8 +97,8 @@ func Init(wg *sync.WaitGroup) { | |||||||
| 		shutdown() | 		shutdown() | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
| 	if config.MetricStoreKeys.Nats != nil { | 	if Keys.Nats != nil { | ||||||
| 		for _, natsConf := range config.MetricStoreKeys.Nats { | 		for _, natsConf := range Keys.Nats { | ||||||
| 			// TODO: When multiple nats configs share a URL, do a single connect. | 			// TODO: When multiple nats configs share a URL, do a single connect. | ||||||
| 			wg.Add(1) | 			wg.Add(1) | ||||||
| 			nc := natsConf | 			nc := natsConf | ||||||
| @@ -114,9 +114,9 @@ func Init(wg *sync.WaitGroup) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Create a new, initialized instance of a MemoryStore. | // InitMetrics creates a new, initialized instance of a MemoryStore. | ||||||
| // Will panic if values in the metric configurations are invalid. | // Will panic if values in the metric configurations are invalid. | ||||||
| func InitMetrics(metrics map[string]config.MetricConfig) { | func InitMetrics(metrics map[string]MetricConfig) { | ||||||
| 	singleton.Do(func() { | 	singleton.Do(func() { | ||||||
| 		offset := 0 | 		offset := 0 | ||||||
| 		for key, cfg := range metrics { | 		for key, cfg := range metrics { | ||||||
| @@ -124,10 +124,10 @@ func InitMetrics(metrics map[string]config.MetricConfig) { | |||||||
| 				panic("[METRICSTORE]> invalid frequency") | 				panic("[METRICSTORE]> invalid frequency") | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			metrics[key] = config.MetricConfig{ | 			metrics[key] = MetricConfig{ | ||||||
| 				Frequency:   cfg.Frequency, | 				Frequency:   cfg.Frequency, | ||||||
| 				Aggregation: cfg.Aggregation, | 				Aggregation: cfg.Aggregation, | ||||||
| 				Offset:      offset, | 				offset:      offset, | ||||||
| 			} | 			} | ||||||
| 			offset += 1 | 			offset += 1 | ||||||
| 		} | 		} | ||||||
| @@ -151,17 +151,17 @@ func GetMemoryStore() *MemoryStore { | |||||||
| } | } | ||||||
|  |  | ||||||
| func Shutdown() { | func Shutdown() { | ||||||
| 	log.Printf("[METRICSTORE]> Writing to '%s'...\n", config.MetricStoreKeys.Checkpoints.RootDir) | 	log.Printf("[METRICSTORE]> Writing to '%s'...\n", Keys.Checkpoints.RootDir) | ||||||
| 	var files int | 	var files int | ||||||
| 	var err error | 	var err error | ||||||
|  |  | ||||||
| 	ms := GetMemoryStore() | 	ms := GetMemoryStore() | ||||||
|  |  | ||||||
| 	if config.MetricStoreKeys.Checkpoints.FileFormat == "json" { | 	if Keys.Checkpoints.FileFormat == "json" { | ||||||
| 		files, err = ms.ToCheckpoint(config.MetricStoreKeys.Checkpoints.RootDir, lastCheckpoint.Unix(), time.Now().Unix()) | 		files, err = ms.ToCheckpoint(Keys.Checkpoints.RootDir, lastCheckpoint.Unix(), time.Now().Unix()) | ||||||
| 	} else { | 	} else { | ||||||
| 		files, err = avro.GetAvroStore().ToCheckpoint(config.MetricStoreKeys.Checkpoints.RootDir, true) | 		files, err = GetAvroStore().ToCheckpoint(Keys.Checkpoints.RootDir, true) | ||||||
| 		close(avro.LineProtocolMessages) | 		close(LineProtocolMessages) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -234,7 +234,7 @@ func Shutdown() { | |||||||
|  |  | ||||||
| func getName(m *MemoryStore, i int) string { | func getName(m *MemoryStore, i int) string { | ||||||
| 	for key, val := range m.Metrics { | 	for key, val := range m.Metrics { | ||||||
| 		if val.Offset == i { | 		if val.offset == i { | ||||||
| 			return key | 			return key | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -246,7 +246,7 @@ func Retention(wg *sync.WaitGroup, ctx context.Context) { | |||||||
|  |  | ||||||
| 	go func() { | 	go func() { | ||||||
| 		defer wg.Done() | 		defer wg.Done() | ||||||
| 		d, err := time.ParseDuration(config.MetricStoreKeys.RetentionInMemory) | 		d, err := time.ParseDuration(Keys.RetentionInMemory) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal(err) | 			log.Fatal(err) | ||||||
| 		} | 		} | ||||||
| @@ -311,11 +311,11 @@ func (m *MemoryStore) WriteToLevel(l *Level, selector []string, ts int64, metric | |||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		b := l.metrics[metric.MetricConfig.Offset] | 		b := l.metrics[metric.MetricConfig.offset] | ||||||
| 		if b == nil { | 		if b == nil { | ||||||
| 			// First write to this metric and level | 			// First write to this metric and level | ||||||
| 			b = newBuffer(ts, metric.MetricConfig.Frequency) | 			b = newBuffer(ts, metric.MetricConfig.Frequency) | ||||||
| 			l.metrics[metric.MetricConfig.Offset] = b | 			l.metrics[metric.MetricConfig.offset] = b | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		nb, err := b.write(ts, metric.Value) | 		nb, err := b.write(ts, metric.Value) | ||||||
| @@ -325,7 +325,7 @@ func (m *MemoryStore) WriteToLevel(l *Level, selector []string, ts int64, metric | |||||||
|  |  | ||||||
| 		// Last write created a new buffer... | 		// Last write created a new buffer... | ||||||
| 		if b != nb { | 		if b != nb { | ||||||
| 			l.metrics[metric.MetricConfig.Offset] = nb | 			l.metrics[metric.MetricConfig.offset] = nb | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| @@ -337,17 +337,17 @@ func (m *MemoryStore) WriteToLevel(l *Level, selector []string, ts int64, metric | |||||||
| // the range asked for if no data was available. | // the range asked for if no data was available. | ||||||
| func (m *MemoryStore) Read(selector util.Selector, metric string, from, to, resolution int64) ([]schema.Float, int64, int64, int64, error) { | func (m *MemoryStore) Read(selector util.Selector, metric string, from, to, resolution int64) ([]schema.Float, int64, int64, int64, error) { | ||||||
| 	if from > to { | 	if from > to { | ||||||
| 		return nil, 0, 0, 0, errors.New("[METRICSTORE]> invalid time range\n") | 		return nil, 0, 0, 0, errors.New("[METRICSTORE]> invalid time range") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	minfo, ok := m.Metrics[metric] | 	minfo, ok := m.Metrics[metric] | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return nil, 0, 0, 0, errors.New("[METRICSTORE]> unkown metric: \n" + metric) | 		return nil, 0, 0, 0, errors.New("[METRICSTORE]> unkown metric: " + metric) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	n, data := 0, make([]schema.Float, (to-from)/minfo.Frequency+1) | 	n, data := 0, make([]schema.Float, (to-from)/minfo.Frequency+1) | ||||||
|  |  | ||||||
| 	err := m.root.findBuffers(selector, minfo.Offset, func(b *buffer) error { | 	err := m.root.findBuffers(selector, minfo.offset, func(b *buffer) error { | ||||||
| 		cdata, cfrom, cto, err := b.read(from, to, data) | 		cdata, cfrom, cto, err := b.read(from, to, data) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| @@ -381,14 +381,14 @@ func (m *MemoryStore) Read(selector util.Selector, metric string, from, to, reso | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, 0, 0, 0, err | 		return nil, 0, 0, 0, err | ||||||
| 	} else if n == 0 { | 	} else if n == 0 { | ||||||
| 		return nil, 0, 0, 0, errors.New("[METRICSTORE]> metric or host not found\n") | 		return nil, 0, 0, 0, errors.New("[METRICSTORE]> metric or host not found") | ||||||
| 	} else if n > 1 { | 	} else if n > 1 { | ||||||
| 		if minfo.Aggregation == config.AvgAggregation { | 		if minfo.Aggregation == AvgAggregation { | ||||||
| 			normalize := 1. / schema.Float(n) | 			normalize := 1. / schema.Float(n) | ||||||
| 			for i := 0; i < len(data); i++ { | 			for i := 0; i < len(data); i++ { | ||||||
| 				data[i] *= normalize | 				data[i] *= normalize | ||||||
| 			} | 			} | ||||||
| 		} else if minfo.Aggregation != config.SumAggregation { | 		} else if minfo.Aggregation != SumAggregation { | ||||||
| 			return nil, 0, 0, 0, errors.New("[METRICSTORE]> invalid aggregation") | 			return nil, 0, 0, 0, errors.New("[METRICSTORE]> invalid aggregation") | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -1,10 +1,14 @@ | |||||||
|  | // Copyright (C) NHR@FAU, University Erlangen-Nuremberg. | ||||||
|  | // All rights reserved. This file is part of cc-backend. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| package memorystore | package memorystore | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"math" | 	"math" | ||||||
|  |  | ||||||
| 	"github.com/ClusterCockpit/cc-backend/internal/config" |  | ||||||
| 	"github.com/ClusterCockpit/cc-lib/util" | 	"github.com/ClusterCockpit/cc-lib/util" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -78,7 +82,7 @@ func (m *MemoryStore) Stats(selector util.Selector, metric string, from, to int6 | |||||||
|  |  | ||||||
| 	n, samples := 0, 0 | 	n, samples := 0, 0 | ||||||
| 	avg, min, max := util.Float(0), math.MaxFloat32, -math.MaxFloat32 | 	avg, min, max := util.Float(0), math.MaxFloat32, -math.MaxFloat32 | ||||||
| 	err := m.root.findBuffers(selector, minfo.Offset, func(b *buffer) error { | 	err := m.root.findBuffers(selector, minfo.offset, func(b *buffer) error { | ||||||
| 		stats, cfrom, cto, err := b.stats(from, to) | 		stats, cfrom, cto, err := b.stats(from, to) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| @@ -105,9 +109,9 @@ func (m *MemoryStore) Stats(selector util.Selector, metric string, from, to int6 | |||||||
| 		return nil, 0, 0, ErrNoData | 		return nil, 0, 0, ErrNoData | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if minfo.Aggregation == config.AvgAggregation { | 	if minfo.Aggregation == AvgAggregation { | ||||||
| 		avg /= util.Float(n) | 		avg /= util.Float(n) | ||||||
| 	} else if n > 1 && minfo.Aggregation != config.SumAggregation { | 	} else if n > 1 && minfo.Aggregation != SumAggregation { | ||||||
| 		return nil, 0, 0, errors.New("invalid aggregation") | 		return nil, 0, 0, errors.New("invalid aggregation") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -54,7 +54,7 @@ func (ccms *CCMetricStoreInternal) LoadData( | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	req := memorystore.ApiQueryRequest{ | 	req := memorystore.APIQueryRequest{ | ||||||
| 		Cluster:   job.Cluster, | 		Cluster:   job.Cluster, | ||||||
| 		From:      job.StartTime, | 		From:      job.StartTime, | ||||||
| 		To:        job.StartTime + int64(job.Duration), | 		To:        job.StartTime + int64(job.Duration), | ||||||
| @@ -156,8 +156,8 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
| 	metrics []string, | 	metrics []string, | ||||||
| 	scopes []schema.MetricScope, | 	scopes []schema.MetricScope, | ||||||
| 	resolution int64, | 	resolution int64, | ||||||
| ) ([]memorystore.ApiQuery, []schema.MetricScope, error) { | ) ([]memorystore.APIQuery, []schema.MetricScope, error) { | ||||||
| 	queries := make([]memorystore.ApiQuery, 0, len(metrics)*len(scopes)*len(job.Resources)) | 	queries := make([]memorystore.APIQuery, 0, len(metrics)*len(scopes)*len(job.Resources)) | ||||||
| 	assignedScope := []schema.MetricScope{} | 	assignedScope := []schema.MetricScope{} | ||||||
|  |  | ||||||
| 	subcluster, scerr := archive.GetSubCluster(job.Cluster, job.SubCluster) | 	subcluster, scerr := archive.GetSubCluster(job.Cluster, job.SubCluster) | ||||||
| @@ -219,7 +219,7 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
| 						continue | 						continue | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   host.Hostname, | 						Hostname:   host.Hostname, | ||||||
| 						Aggregate:  false, | 						Aggregate:  false, | ||||||
| @@ -237,7 +237,7 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
| 						continue | 						continue | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   host.Hostname, | 						Hostname:   host.Hostname, | ||||||
| 						Aggregate:  true, | 						Aggregate:  true, | ||||||
| @@ -251,7 +251,7 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
|  |  | ||||||
| 				// HWThread -> HWThead | 				// HWThread -> HWThead | ||||||
| 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeHWThread { | 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeHWThread { | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   host.Hostname, | 						Hostname:   host.Hostname, | ||||||
| 						Aggregate:  false, | 						Aggregate:  false, | ||||||
| @@ -267,7 +267,7 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
| 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeCore { | 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeCore { | ||||||
| 					cores, _ := topology.GetCoresFromHWThreads(hwthreads) | 					cores, _ := topology.GetCoresFromHWThreads(hwthreads) | ||||||
| 					for _, core := range cores { | 					for _, core := range cores { | ||||||
| 						queries = append(queries, memorystore.ApiQuery{ | 						queries = append(queries, memorystore.APIQuery{ | ||||||
| 							Metric:     metric, | 							Metric:     metric, | ||||||
| 							Hostname:   host.Hostname, | 							Hostname:   host.Hostname, | ||||||
| 							Aggregate:  true, | 							Aggregate:  true, | ||||||
| @@ -284,7 +284,7 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
| 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeSocket { | 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeSocket { | ||||||
| 					sockets, _ := topology.GetSocketsFromHWThreads(hwthreads) | 					sockets, _ := topology.GetSocketsFromHWThreads(hwthreads) | ||||||
| 					for _, socket := range sockets { | 					for _, socket := range sockets { | ||||||
| 						queries = append(queries, memorystore.ApiQuery{ | 						queries = append(queries, memorystore.APIQuery{ | ||||||
| 							Metric:     metric, | 							Metric:     metric, | ||||||
| 							Hostname:   host.Hostname, | 							Hostname:   host.Hostname, | ||||||
| 							Aggregate:  true, | 							Aggregate:  true, | ||||||
| @@ -299,7 +299,7 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
|  |  | ||||||
| 				// HWThread -> Node | 				// HWThread -> Node | ||||||
| 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeNode { | 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeNode { | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   host.Hostname, | 						Hostname:   host.Hostname, | ||||||
| 						Aggregate:  true, | 						Aggregate:  true, | ||||||
| @@ -314,7 +314,7 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
| 				// Core -> Core | 				// Core -> Core | ||||||
| 				if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeCore { | 				if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeCore { | ||||||
| 					cores, _ := topology.GetCoresFromHWThreads(hwthreads) | 					cores, _ := topology.GetCoresFromHWThreads(hwthreads) | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   host.Hostname, | 						Hostname:   host.Hostname, | ||||||
| 						Aggregate:  false, | 						Aggregate:  false, | ||||||
| @@ -330,7 +330,7 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
| 				if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeSocket { | 				if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeSocket { | ||||||
| 					sockets, _ := topology.GetSocketsFromCores(hwthreads) | 					sockets, _ := topology.GetSocketsFromCores(hwthreads) | ||||||
| 					for _, socket := range sockets { | 					for _, socket := range sockets { | ||||||
| 						queries = append(queries, memorystore.ApiQuery{ | 						queries = append(queries, memorystore.APIQuery{ | ||||||
| 							Metric:     metric, | 							Metric:     metric, | ||||||
| 							Hostname:   host.Hostname, | 							Hostname:   host.Hostname, | ||||||
| 							Aggregate:  true, | 							Aggregate:  true, | ||||||
| @@ -346,7 +346,7 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
| 				// Core -> Node | 				// Core -> Node | ||||||
| 				if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeNode { | 				if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeNode { | ||||||
| 					cores, _ := topology.GetCoresFromHWThreads(hwthreads) | 					cores, _ := topology.GetCoresFromHWThreads(hwthreads) | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   host.Hostname, | 						Hostname:   host.Hostname, | ||||||
| 						Aggregate:  true, | 						Aggregate:  true, | ||||||
| @@ -361,7 +361,7 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
| 				// MemoryDomain -> MemoryDomain | 				// MemoryDomain -> MemoryDomain | ||||||
| 				if nativeScope == schema.MetricScopeMemoryDomain && scope == schema.MetricScopeMemoryDomain { | 				if nativeScope == schema.MetricScopeMemoryDomain && scope == schema.MetricScopeMemoryDomain { | ||||||
| 					sockets, _ := topology.GetMemoryDomainsFromHWThreads(hwthreads) | 					sockets, _ := topology.GetMemoryDomainsFromHWThreads(hwthreads) | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   host.Hostname, | 						Hostname:   host.Hostname, | ||||||
| 						Aggregate:  false, | 						Aggregate:  false, | ||||||
| @@ -376,7 +376,7 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
| 				// MemoryDoman -> Node | 				// MemoryDoman -> Node | ||||||
| 				if nativeScope == schema.MetricScopeMemoryDomain && scope == schema.MetricScopeNode { | 				if nativeScope == schema.MetricScopeMemoryDomain && scope == schema.MetricScopeNode { | ||||||
| 					sockets, _ := topology.GetMemoryDomainsFromHWThreads(hwthreads) | 					sockets, _ := topology.GetMemoryDomainsFromHWThreads(hwthreads) | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   host.Hostname, | 						Hostname:   host.Hostname, | ||||||
| 						Aggregate:  true, | 						Aggregate:  true, | ||||||
| @@ -391,7 +391,7 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
| 				// Socket -> Socket | 				// Socket -> Socket | ||||||
| 				if nativeScope == schema.MetricScopeSocket && scope == schema.MetricScopeSocket { | 				if nativeScope == schema.MetricScopeSocket && scope == schema.MetricScopeSocket { | ||||||
| 					sockets, _ := topology.GetSocketsFromHWThreads(hwthreads) | 					sockets, _ := topology.GetSocketsFromHWThreads(hwthreads) | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   host.Hostname, | 						Hostname:   host.Hostname, | ||||||
| 						Aggregate:  false, | 						Aggregate:  false, | ||||||
| @@ -406,7 +406,7 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
| 				// Socket -> Node | 				// Socket -> Node | ||||||
| 				if nativeScope == schema.MetricScopeSocket && scope == schema.MetricScopeNode { | 				if nativeScope == schema.MetricScopeSocket && scope == schema.MetricScopeNode { | ||||||
| 					sockets, _ := topology.GetSocketsFromHWThreads(hwthreads) | 					sockets, _ := topology.GetSocketsFromHWThreads(hwthreads) | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   host.Hostname, | 						Hostname:   host.Hostname, | ||||||
| 						Aggregate:  true, | 						Aggregate:  true, | ||||||
| @@ -420,7 +420,7 @@ func (ccms *CCMetricStoreInternal) buildQueries( | |||||||
|  |  | ||||||
| 				// Node -> Node | 				// Node -> Node | ||||||
| 				if nativeScope == schema.MetricScopeNode && scope == schema.MetricScopeNode { | 				if nativeScope == schema.MetricScopeNode && scope == schema.MetricScopeNode { | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   host.Hostname, | 						Hostname:   host.Hostname, | ||||||
| 						Resolution: resolution, | 						Resolution: resolution, | ||||||
| @@ -448,7 +448,7 @@ func (ccms *CCMetricStoreInternal) LoadStats( | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	req := memorystore.ApiQueryRequest{ | 	req := memorystore.APIQueryRequest{ | ||||||
| 		Cluster:   job.Cluster, | 		Cluster:   job.Cluster, | ||||||
| 		From:      job.StartTime, | 		From:      job.StartTime, | ||||||
| 		To:        job.StartTime + int64(job.Duration), | 		To:        job.StartTime + int64(job.Duration), | ||||||
| @@ -507,7 +507,7 @@ func (ccms *CCMetricStoreInternal) LoadScopedStats( | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	req := memorystore.ApiQueryRequest{ | 	req := memorystore.APIQueryRequest{ | ||||||
| 		Cluster:   job.Cluster, | 		Cluster:   job.Cluster, | ||||||
| 		From:      job.StartTime, | 		From:      job.StartTime, | ||||||
| 		To:        job.StartTime + int64(job.Duration), | 		To:        job.StartTime + int64(job.Duration), | ||||||
| @@ -593,7 +593,7 @@ func (ccms *CCMetricStoreInternal) LoadNodeData( | |||||||
| 	from, to time.Time, | 	from, to time.Time, | ||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| ) (map[string]map[string][]*schema.JobMetric, error) { | ) (map[string]map[string][]*schema.JobMetric, error) { | ||||||
| 	req := memorystore.ApiQueryRequest{ | 	req := memorystore.APIQueryRequest{ | ||||||
| 		Cluster:   cluster, | 		Cluster:   cluster, | ||||||
| 		From:      from.Unix(), | 		From:      from.Unix(), | ||||||
| 		To:        to.Unix(), | 		To:        to.Unix(), | ||||||
| @@ -606,7 +606,7 @@ func (ccms *CCMetricStoreInternal) LoadNodeData( | |||||||
| 	} else { | 	} else { | ||||||
| 		for _, node := range nodes { | 		for _, node := range nodes { | ||||||
| 			for _, metric := range metrics { | 			for _, metric := range metrics { | ||||||
| 				req.Queries = append(req.Queries, memorystore.ApiQuery{ | 				req.Queries = append(req.Queries, memorystore.APIQuery{ | ||||||
| 					Hostname:   node, | 					Hostname:   node, | ||||||
| 					Metric:     metric, | 					Metric:     metric, | ||||||
| 					Resolution: 0, // Default for Node Queries: Will return metric $Timestep Resolution | 					Resolution: 0, // Default for Node Queries: Will return metric $Timestep Resolution | ||||||
| @@ -624,7 +624,7 @@ func (ccms *CCMetricStoreInternal) LoadNodeData( | |||||||
| 	var errors []string | 	var errors []string | ||||||
| 	data := make(map[string]map[string][]*schema.JobMetric) | 	data := make(map[string]map[string][]*schema.JobMetric) | ||||||
| 	for i, res := range resBody.Results { | 	for i, res := range resBody.Results { | ||||||
| 		var query memorystore.ApiQuery | 		var query memorystore.APIQuery | ||||||
| 		if resBody.Queries != nil { | 		if resBody.Queries != nil { | ||||||
| 			query = resBody.Queries[i] | 			query = resBody.Queries[i] | ||||||
| 		} else { | 		} else { | ||||||
| @@ -737,7 +737,7 @@ func (ccms *CCMetricStoreInternal) LoadNodeListData( | |||||||
| 		return nil, totalNodes, hasNextPage, err | 		return nil, totalNodes, hasNextPage, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	req := memorystore.ApiQueryRequest{ | 	req := memorystore.APIQueryRequest{ | ||||||
| 		Cluster:   cluster, | 		Cluster:   cluster, | ||||||
| 		Queries:   queries, | 		Queries:   queries, | ||||||
| 		From:      from.Unix(), | 		From:      from.Unix(), | ||||||
| @@ -755,7 +755,7 @@ func (ccms *CCMetricStoreInternal) LoadNodeListData( | |||||||
| 	var errors []string | 	var errors []string | ||||||
| 	data := make(map[string]schema.JobData) | 	data := make(map[string]schema.JobData) | ||||||
| 	for i, row := range resBody.Results { | 	for i, row := range resBody.Results { | ||||||
| 		var query memorystore.ApiQuery | 		var query memorystore.APIQuery | ||||||
| 		if resBody.Queries != nil { | 		if resBody.Queries != nil { | ||||||
| 			query = resBody.Queries[i] | 			query = resBody.Queries[i] | ||||||
| 		} else { | 		} else { | ||||||
| @@ -842,8 +842,8 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
| 	metrics []string, | 	metrics []string, | ||||||
| 	scopes []schema.MetricScope, | 	scopes []schema.MetricScope, | ||||||
| 	resolution int64, | 	resolution int64, | ||||||
| ) ([]memorystore.ApiQuery, []schema.MetricScope, error) { | ) ([]memorystore.APIQuery, []schema.MetricScope, error) { | ||||||
| 	queries := make([]memorystore.ApiQuery, 0, len(metrics)*len(scopes)*len(nodes)) | 	queries := make([]memorystore.APIQuery, 0, len(metrics)*len(scopes)*len(nodes)) | ||||||
| 	assignedScope := []schema.MetricScope{} | 	assignedScope := []schema.MetricScope{} | ||||||
|  |  | ||||||
| 	// Get Topol before loop if subCluster given | 	// Get Topol before loop if subCluster given | ||||||
| @@ -926,7 +926,7 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
| 						continue | 						continue | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   hostname, | 						Hostname:   hostname, | ||||||
| 						Aggregate:  false, | 						Aggregate:  false, | ||||||
| @@ -944,7 +944,7 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
| 						continue | 						continue | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   hostname, | 						Hostname:   hostname, | ||||||
| 						Aggregate:  true, | 						Aggregate:  true, | ||||||
| @@ -958,7 +958,7 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
|  |  | ||||||
| 				// HWThread -> HWThead | 				// HWThread -> HWThead | ||||||
| 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeHWThread { | 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeHWThread { | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   hostname, | 						Hostname:   hostname, | ||||||
| 						Aggregate:  false, | 						Aggregate:  false, | ||||||
| @@ -974,7 +974,7 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
| 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeCore { | 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeCore { | ||||||
| 					cores, _ := topology.GetCoresFromHWThreads(topology.Node) | 					cores, _ := topology.GetCoresFromHWThreads(topology.Node) | ||||||
| 					for _, core := range cores { | 					for _, core := range cores { | ||||||
| 						queries = append(queries, memorystore.ApiQuery{ | 						queries = append(queries, memorystore.APIQuery{ | ||||||
| 							Metric:     metric, | 							Metric:     metric, | ||||||
| 							Hostname:   hostname, | 							Hostname:   hostname, | ||||||
| 							Aggregate:  true, | 							Aggregate:  true, | ||||||
| @@ -991,7 +991,7 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
| 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeSocket { | 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeSocket { | ||||||
| 					sockets, _ := topology.GetSocketsFromHWThreads(topology.Node) | 					sockets, _ := topology.GetSocketsFromHWThreads(topology.Node) | ||||||
| 					for _, socket := range sockets { | 					for _, socket := range sockets { | ||||||
| 						queries = append(queries, memorystore.ApiQuery{ | 						queries = append(queries, memorystore.APIQuery{ | ||||||
| 							Metric:     metric, | 							Metric:     metric, | ||||||
| 							Hostname:   hostname, | 							Hostname:   hostname, | ||||||
| 							Aggregate:  true, | 							Aggregate:  true, | ||||||
| @@ -1006,7 +1006,7 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
|  |  | ||||||
| 				// HWThread -> Node | 				// HWThread -> Node | ||||||
| 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeNode { | 				if nativeScope == schema.MetricScopeHWThread && scope == schema.MetricScopeNode { | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   hostname, | 						Hostname:   hostname, | ||||||
| 						Aggregate:  true, | 						Aggregate:  true, | ||||||
| @@ -1021,7 +1021,7 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
| 				// Core -> Core | 				// Core -> Core | ||||||
| 				if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeCore { | 				if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeCore { | ||||||
| 					cores, _ := topology.GetCoresFromHWThreads(topology.Node) | 					cores, _ := topology.GetCoresFromHWThreads(topology.Node) | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   hostname, | 						Hostname:   hostname, | ||||||
| 						Aggregate:  false, | 						Aggregate:  false, | ||||||
| @@ -1037,7 +1037,7 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
| 				if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeSocket { | 				if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeSocket { | ||||||
| 					sockets, _ := topology.GetSocketsFromCores(topology.Node) | 					sockets, _ := topology.GetSocketsFromCores(topology.Node) | ||||||
| 					for _, socket := range sockets { | 					for _, socket := range sockets { | ||||||
| 						queries = append(queries, memorystore.ApiQuery{ | 						queries = append(queries, memorystore.APIQuery{ | ||||||
| 							Metric:     metric, | 							Metric:     metric, | ||||||
| 							Hostname:   hostname, | 							Hostname:   hostname, | ||||||
| 							Aggregate:  true, | 							Aggregate:  true, | ||||||
| @@ -1053,7 +1053,7 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
| 				// Core -> Node | 				// Core -> Node | ||||||
| 				if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeNode { | 				if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeNode { | ||||||
| 					cores, _ := topology.GetCoresFromHWThreads(topology.Node) | 					cores, _ := topology.GetCoresFromHWThreads(topology.Node) | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   hostname, | 						Hostname:   hostname, | ||||||
| 						Aggregate:  true, | 						Aggregate:  true, | ||||||
| @@ -1068,7 +1068,7 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
| 				// MemoryDomain -> MemoryDomain | 				// MemoryDomain -> MemoryDomain | ||||||
| 				if nativeScope == schema.MetricScopeMemoryDomain && scope == schema.MetricScopeMemoryDomain { | 				if nativeScope == schema.MetricScopeMemoryDomain && scope == schema.MetricScopeMemoryDomain { | ||||||
| 					sockets, _ := topology.GetMemoryDomainsFromHWThreads(topology.Node) | 					sockets, _ := topology.GetMemoryDomainsFromHWThreads(topology.Node) | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   hostname, | 						Hostname:   hostname, | ||||||
| 						Aggregate:  false, | 						Aggregate:  false, | ||||||
| @@ -1083,7 +1083,7 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
| 				// MemoryDoman -> Node | 				// MemoryDoman -> Node | ||||||
| 				if nativeScope == schema.MetricScopeMemoryDomain && scope == schema.MetricScopeNode { | 				if nativeScope == schema.MetricScopeMemoryDomain && scope == schema.MetricScopeNode { | ||||||
| 					sockets, _ := topology.GetMemoryDomainsFromHWThreads(topology.Node) | 					sockets, _ := topology.GetMemoryDomainsFromHWThreads(topology.Node) | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   hostname, | 						Hostname:   hostname, | ||||||
| 						Aggregate:  true, | 						Aggregate:  true, | ||||||
| @@ -1098,7 +1098,7 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
| 				// Socket -> Socket | 				// Socket -> Socket | ||||||
| 				if nativeScope == schema.MetricScopeSocket && scope == schema.MetricScopeSocket { | 				if nativeScope == schema.MetricScopeSocket && scope == schema.MetricScopeSocket { | ||||||
| 					sockets, _ := topology.GetSocketsFromHWThreads(topology.Node) | 					sockets, _ := topology.GetSocketsFromHWThreads(topology.Node) | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   hostname, | 						Hostname:   hostname, | ||||||
| 						Aggregate:  false, | 						Aggregate:  false, | ||||||
| @@ -1113,7 +1113,7 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
| 				// Socket -> Node | 				// Socket -> Node | ||||||
| 				if nativeScope == schema.MetricScopeSocket && scope == schema.MetricScopeNode { | 				if nativeScope == schema.MetricScopeSocket && scope == schema.MetricScopeNode { | ||||||
| 					sockets, _ := topology.GetSocketsFromHWThreads(topology.Node) | 					sockets, _ := topology.GetSocketsFromHWThreads(topology.Node) | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   hostname, | 						Hostname:   hostname, | ||||||
| 						Aggregate:  true, | 						Aggregate:  true, | ||||||
| @@ -1127,7 +1127,7 @@ func (ccms *CCMetricStoreInternal) buildNodeQueries( | |||||||
|  |  | ||||||
| 				// Node -> Node | 				// Node -> Node | ||||||
| 				if nativeScope == schema.MetricScopeNode && scope == schema.MetricScopeNode { | 				if nativeScope == schema.MetricScopeNode && scope == schema.MetricScopeNode { | ||||||
| 					queries = append(queries, memorystore.ApiQuery{ | 					queries = append(queries, memorystore.APIQuery{ | ||||||
| 						Metric:     metric, | 						Metric:     metric, | ||||||
| 						Hostname:   hostname, | 						Hostname:   hostname, | ||||||
| 						Resolution: resolution, | 						Resolution: resolution, | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| // All rights reserved. This file is part of cc-backend. | // All rights reserved. This file is part of cc-backend. | ||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| package metricdata | package metricdata | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -12,6 +13,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/ClusterCockpit/cc-backend/internal/config" | 	"github.com/ClusterCockpit/cc-backend/internal/config" | ||||||
| 	"github.com/ClusterCockpit/cc-backend/internal/graph/model" | 	"github.com/ClusterCockpit/cc-backend/internal/graph/model" | ||||||
|  | 	"github.com/ClusterCockpit/cc-backend/internal/memorystore" | ||||||
| 	cclog "github.com/ClusterCockpit/cc-lib/ccLogger" | 	cclog "github.com/ClusterCockpit/cc-lib/ccLogger" | ||||||
| 	"github.com/ClusterCockpit/cc-lib/schema" | 	"github.com/ClusterCockpit/cc-lib/schema" | ||||||
| ) | ) | ||||||
| @@ -56,7 +58,7 @@ func Init() error { | |||||||
| 				mdr = &CCMetricStore{} | 				mdr = &CCMetricStore{} | ||||||
| 			case "cc-metric-store-internal": | 			case "cc-metric-store-internal": | ||||||
| 				mdr = &CCMetricStoreInternal{} | 				mdr = &CCMetricStoreInternal{} | ||||||
| 				config.InternalCCMSFlag = true | 				memorystore.InternalCCMSFlag = true | ||||||
| 			case "prometheus": | 			case "prometheus": | ||||||
| 				mdr = &PrometheusDataRepository{} | 				mdr = &PrometheusDataRepository{} | ||||||
| 			case "test": | 			case "test": | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user