Compare commits

..

3 Commits

Author SHA1 Message Date
Thomas Roehl
c76219651e Fix parsing of metric subtypes (key stype) 2026-05-04 18:10:01 +02:00
Jan Eitzinger
196f659a50 Merge pull request #547 from ClusterCockpit/546_fix_roofline_legends
fix: change to fixed legend coordinates instead of dynamic placement
2026-04-24 09:01:06 +02:00
Christoph Kluge
ac7865d597 fix: change to fixed legend coordinates instead of dynamic placement 2026-04-15 11:12:07 +02:00
4 changed files with 30 additions and 30 deletions

View File

@@ -78,7 +78,7 @@ type APIQueryResponse struct {
// - Type + TypeIds: First level of hierarchy (e.g., "cpu" + ["0", "1", "2"])
// - SubType + SubTypeIds: Second level of hierarchy (e.g., "core" + ["0", "1"])
//
// If Aggregate is true, data from multiple type/subtype IDs will be aggregated according
// If Aggregate is true, data from multiple type/stype IDs will be aggregated according
// to the metric's aggregation strategy. Otherwise, separate results are returned for each combination.
type APIQuery struct {
Type *string `json:"type,omitempty"`
@@ -174,13 +174,13 @@ func (data *APIMetricData) PadDataWithNull(ms *MemoryStore, from, to int64, metr
// This is the primary API for retrieving metric data from the memory store. It supports:
// - Individual queries via req.Queries
// - Batch queries for all nodes via req.ForAllNodes
// - Hierarchical selector construction (cluster → host → type → subtype)
// - Hierarchical selector construction (cluster → host → type → stype)
// - Optional statistics computation (avg, min, max)
// - Optional data scaling
// - Optional data padding with NaN values
//
// The function constructs selectors based on the query parameters and calls MemoryStore.Read()
// for each selector. If a query specifies Aggregate=false with multiple type/subtype IDs,
// for each selector. If a query specifies Aggregate=false with multiple type/stype IDs,
// separate results are returned for each combination.
//
// Parameters:

View File

@@ -6,11 +6,11 @@
// This file implements ingestion of InfluxDB line-protocol metric data received
// over NATS. Each line encodes one metric sample with the following structure:
//
// <measurement>[,cluster=<c>][,hostname=<h>][,type=<t>][,type-id=<id>][,subtype=<s>][,stype-id=<id>] value=<v> [<timestamp>]
// <measurement>[,cluster=<c>][,hostname=<h>][,type=<t>][,type-id=<id>][,stype=<s>][,stype-id=<id>] value=<v> [<timestamp>]
//
// The measurement name identifies the metric (e.g. "cpu_load"). Tags provide
// routing information (cluster, host) and optional sub-device selectors (type,
// subtype). Only one field is expected per line: "value".
// stype). Only one field is expected per line: "value".
//
// After decoding, each sample is:
// 1. Written to the in-memory store via ms.WriteToLevel.
@@ -103,7 +103,7 @@ func ReceiveNats(ms *MemoryStore,
// reorder prepends prefix to buf in-place when buf has enough spare capacity,
// avoiding an allocation. Falls back to a regular append otherwise.
//
// It is used to assemble the "type<type-id>" and "subtype<stype-id>" selector
// It is used to assemble the "type<type-id>" and "stype<stype-id>" selector
// strings when the type tag arrives before the type-id tag in the line, so the
// two byte slices need to be concatenated in tag-declaration order regardless
// of wire order.
@@ -145,7 +145,7 @@ type decodeState struct {
// current line. Reset at the start of each line's tag-decode loop.
typeBuf []byte
// subTypeBuf accumulates the concatenated "subtype"+"stype-id" tag value.
// subTypeBuf accumulates the concatenated "stype"+"stype-id" tag value.
// Reset at the start of each line's tag-decode loop.
subTypeBuf []byte
@@ -186,7 +186,7 @@ var decodeStatePool = sync.Pool{
// - The Level pointer (host-level node in the metric tree) is cached across
// consecutive lines that share the same cluster+host pair to avoid
// repeated lock acquisitions on the root and cluster levels.
// - []byte→string conversions for type/subtype selectors are cached via
// - []byte→string conversions for type/stype selectors are cached via
// prevType*/prevSubType* fields because batches typically repeat the same
// sub-device identifiers.
// - Timestamp parsing tries Second precision first; if that fails it retries
@@ -269,8 +269,8 @@ func DecodeLine(dec *lineprotocol.Decoder,
}
case "type-id":
st.typeBuf = append(st.typeBuf, val...)
case "subtype":
// We cannot be sure that the "subtype" tag comes before the "stype-id" tag:
case "stype":
// We cannot be sure that the "stype" tag comes before the "stype-id" tag:
if len(st.subTypeBuf) == 0 {
st.subTypeBuf = append(st.subTypeBuf, val...)
} else {
@@ -291,7 +291,7 @@ func DecodeLine(dec *lineprotocol.Decoder,
}
// subtypes: cache []byte→string conversions; messages in a batch typically
// share the same type/subtype so the hit rate is very high.
// share the same type/stype so the hit rate is very high.
st.selector = st.selector[:0]
if len(st.typeBuf) > 0 {
if !bytes.Equal(st.typeBuf, st.prevTypeBytes) {

View File

@@ -287,12 +287,12 @@
} else if (nodesData[i]?.schedulerState == "allocated") {
//u.ctx.strokeStyle = "rgb(0, 255, 0)";
u.ctx.fillStyle = "rgba(0, 255, 0, 0.5)";
} else if (nodesData[i]?.schedulerState == "notindb") {
} else if (nodesData[i]?.schedulerState == "mixed") {
//u.ctx.strokeStyle = "rgb(0, 0, 0)";
u.ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
} else { // Fallback: All other DEFINED states
//u.ctx.strokeStyle = "rgb(255, 0, 0)";
u.ctx.fillStyle = "rgba(255, 0, 0, 0.5)";
} else { // Fallback: All other states: Reserved, Down, Notindb
//u.ctx.strokeStyle = "rgb(255, 0, 0)";
u.ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
}
}
} else {
@@ -450,10 +450,10 @@
tooltip.style.borderColor = "rgb(0, 0, 255)";
} else if (nodesData[i]?.schedulerState == "allocated") {
tooltip.style.borderColor = "rgb(0, 255, 0)";
} else if (nodesData[i]?.schedulerState == "notindb") { // Missing from DB table
tooltip.style.borderColor = "rgb(0, 0, 0)";
} else { // Fallback: All other DEFINED states
} else if (nodesData[i]?.schedulerState == "mixed") {
tooltip.style.borderColor = "rgb(255, 0, 0)";
} else { // Fallback: All other DEFINED states
tooltip.style.borderColor = "rgb(0, 0, 0)";
}
}
} else {
@@ -904,7 +904,7 @@
if (jobsData) {
const posX = u.valToPos(0.1, "x", true)
const posXLimit = u.valToPos(100, "x", true)
const posY = u.valToPos(17500.0, "y", true)
const posY = 7 // u.valToPos(17500.0, "y", true)
u.ctx.fillStyle = 'black'
u.ctx.fillText('0 Hours', posX, posY)
const start = posX + 10
@@ -921,16 +921,16 @@
// Nodes: The Colors Of NodeStates
if (nodesData) {
const posY = u.valToPos(17500.0, "y", true)
const posY = 7 // u.valToPos(17500.0, "y", true)
const posAllocDot = u.valToPos(0.03, "x", true)
const posAllocText = posAllocDot + 60
const posIdleDot = u.valToPos(0.3, "x", true)
const posIdleText = posIdleDot + 30
const posOtherDot = u.valToPos(3, "x", true)
const posIdleDot = u.valToPos(1, "x", true)
const posIdleText = posIdleDot + 28
const posMixedDot = u.valToPos(7, "x", true)
const posMixedText = posMixedDot + 40
const posOtherDot = u.valToPos(100, "x", true)
const posOtherText = posOtherDot + 40
const posMissingDot = u.valToPos(30, "x", true)
const posMissingText = posMissingDot + 80
u.ctx.fillStyle = "rgb(0, 255, 0)"
u.ctx.beginPath()
@@ -948,16 +948,16 @@
u.ctx.fillStyle = "rgb(255, 0, 0)"
u.ctx.beginPath()
u.ctx.arc(posOtherDot, posY, 3, 0, Math.PI * 2, false)
u.ctx.arc(posMixedDot, posY, 3, 0, Math.PI * 2, false)
u.ctx.fill()
u.ctx.fillStyle = 'black'
u.ctx.fillText('Other', posOtherText, posY)
u.ctx.fillText('Mixed', posMixedText, posY)
u.ctx.fillStyle = 'black'
u.ctx.beginPath()
u.ctx.arc(posMissingDot, posY, 3, 0, Math.PI * 2, false)
u.ctx.arc(posOtherDot, posY, 3, 0, Math.PI * 2, false)
u.ctx.fill()
u.ctx.fillText('Missing in DB', posMissingText, posY)
u.ctx.fillText('Other', posOtherText, posY)
}
}
},

View File

@@ -338,7 +338,7 @@
// The Color Scale For Time Information
const posX = u.valToPos(0.1, "x", true)
const posXLimit = u.valToPos(100, "x", true)
const posY = u.valToPos(14000.0, "y", true)
const posY = 7 // u.valToPos(((subCluster?.flopRateSimd?.value || 10000) + 5000), "y", true)
u.ctx.fillStyle = 'black'
u.ctx.fillText('Start', posX, posY)
const start = posX + 10