mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-27 02:46:47 +01:00
lib/storage: show top labels with the highest number of series in cardinality explorer
This commit is contained in:
parent
af3dc91a51
commit
b6c1ca12b7
@ -268,6 +268,7 @@ See the [example VMUI at VictoriaMetrics playground](https://play.victoriametric
|
|||||||
VictoriaMetrics provides an ability to explore time series cardinality at `cardinality` tab in [vmui](#vmui) in the following ways:
|
VictoriaMetrics provides an ability to explore time series cardinality at `cardinality` tab in [vmui](#vmui) in the following ways:
|
||||||
|
|
||||||
- To identify metric names with the highest number of series.
|
- To identify metric names with the highest number of series.
|
||||||
|
- To idnetify labels with the highest number of series.
|
||||||
- To identify label=name pairs with the highest number of series.
|
- To identify label=name pairs with the highest number of series.
|
||||||
- To identify labels with the highest number of unique values.
|
- To identify labels with the highest number of unique values.
|
||||||
|
|
||||||
@ -275,8 +276,6 @@ By default cardinality explorer analyzes time series for the current date. It pr
|
|||||||
By default all the time series for the selected date are analyzed. It is possible to narrow down the analysis to series
|
By default all the time series for the selected date are analyzed. It is possible to narrow down the analysis to series
|
||||||
matching the specified [series selector](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors).
|
matching the specified [series selector](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors).
|
||||||
|
|
||||||
Cardinality explorer takes into account [deleted time series](#how-to-delete-time-series), because they stay in the inverted index for up to [-retentionPeriod](#retention). This means that the deleted time series take RAM, CPU, disk IO and disk space for the inverted index in the same way as other time series.
|
|
||||||
|
|
||||||
Cardinality explorer is built on top of [/api/v1/status/tsdb](#tsdb-stats).
|
Cardinality explorer is built on top of [/api/v1/status/tsdb](#tsdb-stats).
|
||||||
|
|
||||||
See [cardinality explorer playground](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/graph/#/cardinality).
|
See [cardinality explorer playground](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/graph/#/cardinality).
|
||||||
|
@ -12,6 +12,7 @@ TSDBStatusResponse generates response for /api/v1/status/tsdb .
|
|||||||
"totalSeries": {%dul= status.TotalSeries %},
|
"totalSeries": {%dul= status.TotalSeries %},
|
||||||
"totalLabelValuePairs": {%dul= status.TotalLabelValuePairs %},
|
"totalLabelValuePairs": {%dul= status.TotalLabelValuePairs %},
|
||||||
"seriesCountByMetricName":{%= tsdbStatusEntries(status.SeriesCountByMetricName) %},
|
"seriesCountByMetricName":{%= tsdbStatusEntries(status.SeriesCountByMetricName) %},
|
||||||
|
"seriesCountByLabelName":{%= tsdbStatusEntries(status.SeriesCountByLabelName) %},
|
||||||
"seriesCountByLabelValuePair":{%= tsdbStatusEntries(status.SeriesCountByLabelValuePair) %},
|
"seriesCountByLabelValuePair":{%= tsdbStatusEntries(status.SeriesCountByLabelValuePair) %},
|
||||||
"labelValueCountByLabelName":{%= tsdbStatusEntries(status.LabelValueCountByLabelName) %}
|
"labelValueCountByLabelName":{%= tsdbStatusEntries(status.LabelValueCountByLabelName) %}
|
||||||
}
|
}
|
||||||
|
@ -40,102 +40,106 @@ func StreamTSDBStatusResponse(qw422016 *qt422016.Writer, status *storage.TSDBSta
|
|||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:14
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:14
|
||||||
streamtsdbStatusEntries(qw422016, status.SeriesCountByMetricName)
|
streamtsdbStatusEntries(qw422016, status.SeriesCountByMetricName)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:14
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:14
|
||||||
|
qw422016.N().S(`,"seriesCountByLabelName":`)
|
||||||
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:15
|
||||||
|
streamtsdbStatusEntries(qw422016, status.SeriesCountByLabelName)
|
||||||
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:15
|
||||||
qw422016.N().S(`,"seriesCountByLabelValuePair":`)
|
qw422016.N().S(`,"seriesCountByLabelValuePair":`)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:15
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:16
|
||||||
streamtsdbStatusEntries(qw422016, status.SeriesCountByLabelValuePair)
|
streamtsdbStatusEntries(qw422016, status.SeriesCountByLabelValuePair)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:15
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:16
|
||||||
qw422016.N().S(`,"labelValueCountByLabelName":`)
|
qw422016.N().S(`,"labelValueCountByLabelName":`)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:16
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:17
|
||||||
streamtsdbStatusEntries(qw422016, status.LabelValueCountByLabelName)
|
streamtsdbStatusEntries(qw422016, status.LabelValueCountByLabelName)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:16
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:17
|
||||||
qw422016.N().S(`}`)
|
qw422016.N().S(`}`)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:18
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:19
|
||||||
qt.Done()
|
qt.Done()
|
||||||
|
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:19
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:20
|
||||||
streamdumpQueryTrace(qw422016, qt)
|
streamdumpQueryTrace(qw422016, qt)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:19
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:20
|
||||||
qw422016.N().S(`}`)
|
qw422016.N().S(`}`)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:21
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:22
|
||||||
}
|
}
|
||||||
|
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:21
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:22
|
||||||
func WriteTSDBStatusResponse(qq422016 qtio422016.Writer, status *storage.TSDBStatus, qt *querytracer.Tracer) {
|
func WriteTSDBStatusResponse(qq422016 qtio422016.Writer, status *storage.TSDBStatus, qt *querytracer.Tracer) {
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:21
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:22
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:21
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:22
|
||||||
StreamTSDBStatusResponse(qw422016, status, qt)
|
StreamTSDBStatusResponse(qw422016, status, qt)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:21
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:22
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:21
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:22
|
||||||
}
|
}
|
||||||
|
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:21
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:22
|
||||||
func TSDBStatusResponse(status *storage.TSDBStatus, qt *querytracer.Tracer) string {
|
func TSDBStatusResponse(status *storage.TSDBStatus, qt *querytracer.Tracer) string {
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:21
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:22
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:21
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:22
|
||||||
WriteTSDBStatusResponse(qb422016, status, qt)
|
WriteTSDBStatusResponse(qb422016, status, qt)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:21
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:22
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:21
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:22
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:21
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:22
|
||||||
return qs422016
|
return qs422016
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:21
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:22
|
||||||
}
|
}
|
||||||
|
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:23
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:24
|
||||||
func streamtsdbStatusEntries(qw422016 *qt422016.Writer, a []storage.TopHeapEntry) {
|
func streamtsdbStatusEntries(qw422016 *qt422016.Writer, a []storage.TopHeapEntry) {
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:23
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:24
|
||||||
qw422016.N().S(`[`)
|
qw422016.N().S(`[`)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:25
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:26
|
||||||
for i, e := range a {
|
for i, e := range a {
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:25
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:26
|
||||||
qw422016.N().S(`{"name":`)
|
qw422016.N().S(`{"name":`)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:27
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:28
|
||||||
qw422016.N().Q(e.Name)
|
qw422016.N().Q(e.Name)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:27
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:28
|
||||||
qw422016.N().S(`,"value":`)
|
qw422016.N().S(`,"value":`)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:28
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:29
|
||||||
qw422016.N().D(int(e.Count))
|
qw422016.N().D(int(e.Count))
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:28
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:29
|
||||||
qw422016.N().S(`}`)
|
qw422016.N().S(`}`)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:30
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:31
|
||||||
if i+1 < len(a) {
|
if i+1 < len(a) {
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:30
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:31
|
||||||
qw422016.N().S(`,`)
|
qw422016.N().S(`,`)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:30
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:31
|
||||||
}
|
}
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:31
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:32
|
||||||
}
|
}
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:31
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:32
|
||||||
qw422016.N().S(`]`)
|
qw422016.N().S(`]`)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:33
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:34
|
||||||
}
|
}
|
||||||
|
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:33
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:34
|
||||||
func writetsdbStatusEntries(qq422016 qtio422016.Writer, a []storage.TopHeapEntry) {
|
func writetsdbStatusEntries(qq422016 qtio422016.Writer, a []storage.TopHeapEntry) {
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:33
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:34
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:33
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:34
|
||||||
streamtsdbStatusEntries(qw422016, a)
|
streamtsdbStatusEntries(qw422016, a)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:33
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:34
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:33
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:34
|
||||||
}
|
}
|
||||||
|
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:33
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:34
|
||||||
func tsdbStatusEntries(a []storage.TopHeapEntry) string {
|
func tsdbStatusEntries(a []storage.TopHeapEntry) string {
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:33
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:34
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:33
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:34
|
||||||
writetsdbStatusEntries(qb422016, a)
|
writetsdbStatusEntries(qb422016, a)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:33
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:34
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:33
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:34
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:33
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:34
|
||||||
return qs422016
|
return qs422016
|
||||||
//line app/vmselect/prometheus/tsdb_status_response.qtpl:33
|
//line app/vmselect/prometheus/tsdb_status_response.qtpl:34
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"main.css": "./static/css/main.7e6d0c89.css",
|
"main.css": "./static/css/main.7e6d0c89.css",
|
||||||
"main.js": "./static/js/main.a6bca65f.js",
|
"main.js": "./static/js/main.f7185a13.js",
|
||||||
"static/js/27.939f971b.chunk.js": "./static/js/27.939f971b.chunk.js",
|
"static/js/27.939f971b.chunk.js": "./static/js/27.939f971b.chunk.js",
|
||||||
"index.html": "./index.html"
|
"index.html": "./index.html"
|
||||||
},
|
},
|
||||||
"entrypoints": [
|
"entrypoints": [
|
||||||
"static/css/main.7e6d0c89.css",
|
"static/css/main.7e6d0c89.css",
|
||||||
"static/js/main.a6bca65f.js"
|
"static/js/main.f7185a13.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1 +1 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><script src="./dashboards/index.js" type="module"></script><script defer="defer" src="./static/js/main.a6bca65f.js"></script><link href="./static/css/main.7e6d0c89.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><script src="./dashboards/index.js" type="module"></script><script defer="defer" src="./static/js/main.f7185a13.js"></script><link href="./static/css/main.7e6d0c89.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
File diff suppressed because one or more lines are too long
@ -54,12 +54,12 @@ const CardinalityConfigurator: FC<CardinalityConfiguratorProps> = ({
|
|||||||
<QueryEditor
|
<QueryEditor
|
||||||
query={query} index={0} autocomplete={autocomplete} queryOptions={queryOptions}
|
query={query} index={0} autocomplete={autocomplete} queryOptions={queryOptions}
|
||||||
error={error} setHistoryIndex={onSetHistory} runQuery={onRunQuery} setQuery={onSetQuery}
|
error={error} setHistoryIndex={onSetHistory} runQuery={onRunQuery} setQuery={onSetQuery}
|
||||||
label={"Arbitrary time series selector"}
|
label={"Time series selector"}
|
||||||
/>
|
/>
|
||||||
<Box display="flex" alignItems="center">
|
<Box display="flex" alignItems="center">
|
||||||
<Box ml={2}>
|
<Box ml={2}>
|
||||||
<TextField
|
<TextField
|
||||||
label="Number of top entries"
|
label="Number of entries per table"
|
||||||
type="number"
|
type="number"
|
||||||
size="small"
|
size="small"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@ -82,7 +82,7 @@ const CardinalityConfigurator: FC<CardinalityConfiguratorProps> = ({
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
Analyzed <b>{totalSeries}</b> series and <b>{totalLabelValuePairs}</b> label=value pairs
|
Analyzed <b>{totalSeries}</b> series with <b>{totalLabelValuePairs}</b> label=value pairs
|
||||||
at <b>{date}</b> {match && <span>for series selector <b>{match}</b></span>}. Show top {topN} entries per table.
|
at <b>{date}</b> {match && <span>for series selector <b>{match}</b></span>}. Show top {topN} entries per table.
|
||||||
</Box>
|
</Box>
|
||||||
</Box>;
|
</Box>;
|
||||||
|
@ -3,12 +3,10 @@ import {SyntheticEvent} from "react";
|
|||||||
import {Alert} from "@mui/material";
|
import {Alert} from "@mui/material";
|
||||||
import {useFetchQuery} from "../../hooks/useCardinalityFetch";
|
import {useFetchQuery} from "../../hooks/useCardinalityFetch";
|
||||||
import {
|
import {
|
||||||
LABEL_VALUE_PAIR_CONTENT_TITLE,
|
METRIC_NAMES_HEADERS,
|
||||||
LABEL_VALUE_PAIRS_TABLE_HEADERS,
|
LABEL_NAMES_HEADERS,
|
||||||
LABEL_WITH_UNIQUE_VALUES_TABLE_HEADERS,
|
LABEL_VALUE_PAIRS_HEADERS,
|
||||||
LABELS_CONTENT_TITLE, METRICS_TABLE_HEADERS,
|
LABELS_WITH_UNIQUE_VALUES_HEADERS,
|
||||||
SERIES_CONTENT_TITLE,
|
|
||||||
SPINNER_TITLE,
|
|
||||||
spinnerContainerStyles
|
spinnerContainerStyles
|
||||||
} from "./consts";
|
} from "./consts";
|
||||||
import {defaultProperties, queryUpdater} from "./helpers";
|
import {defaultProperties, queryUpdater} from "./helpers";
|
||||||
@ -76,7 +74,7 @@ const CardinalityPanel: FC = () => {
|
|||||||
height={"800px"}
|
height={"800px"}
|
||||||
containerStyles={spinnerContainerStyles("100%")}
|
containerStyles={spinnerContainerStyles("100%")}
|
||||||
title={<Alert color="error" severity="error" sx={{whiteSpace: "pre-wrap", mt: 2}}>
|
title={<Alert color="error" severity="error" sx={{whiteSpace: "pre-wrap", mt: 2}}>
|
||||||
{SPINNER_TITLE}
|
Please wait while cardinality stats is calculated. This may take some time if the db contains big number of time series
|
||||||
</Alert>}
|
</Alert>}
|
||||||
/>}
|
/>}
|
||||||
<CardinalityConfigurator error={configError} query={query} onRunQuery={onRunQuery} onSetQuery={onSetQuery}
|
<CardinalityConfigurator error={configError} query={query} onRunQuery={onRunQuery} onSetQuery={onSetQuery}
|
||||||
@ -84,6 +82,7 @@ const CardinalityPanel: FC = () => {
|
|||||||
totalSeries={tsdbStatus.totalSeries} totalLabelValuePairs={tsdbStatus.totalLabelValuePairs}/>
|
totalSeries={tsdbStatus.totalSeries} totalLabelValuePairs={tsdbStatus.totalLabelValuePairs}/>
|
||||||
{error && <Alert color="error" severity="error" sx={{whiteSpace: "pre-wrap", mt: 2}}>{error}</Alert>}
|
{error && <Alert color="error" severity="error" sx={{whiteSpace: "pre-wrap", mt: 2}}>{error}</Alert>}
|
||||||
<MetricsContent
|
<MetricsContent
|
||||||
|
sectionTitle={"Metric names with the highest number of series"}
|
||||||
activeTab={stateTabs.seriesCountByMetricName}
|
activeTab={stateTabs.seriesCountByMetricName}
|
||||||
rows={tsdbStatus.seriesCountByMetricName as unknown as Data[]}
|
rows={tsdbStatus.seriesCountByMetricName as unknown as Data[]}
|
||||||
onChange={handleTabChange}
|
onChange={handleTabChange}
|
||||||
@ -92,10 +91,22 @@ const CardinalityPanel: FC = () => {
|
|||||||
chartContainer={defaultProps.containerRefs.seriesCountByMetricName}
|
chartContainer={defaultProps.containerRefs.seriesCountByMetricName}
|
||||||
totalSeries={tsdbStatus.totalSeries}
|
totalSeries={tsdbStatus.totalSeries}
|
||||||
tabId={"seriesCountByMetricName"}
|
tabId={"seriesCountByMetricName"}
|
||||||
sectionTitle={SERIES_CONTENT_TITLE}
|
tableHeaderCells={METRIC_NAMES_HEADERS}
|
||||||
tableHeaderCells={METRICS_TABLE_HEADERS}
|
|
||||||
/>
|
/>
|
||||||
<MetricsContent
|
<MetricsContent
|
||||||
|
sectionTitle={"Labels with the highest number of series"}
|
||||||
|
activeTab={stateTabs.seriesCountByLabelName}
|
||||||
|
rows={tsdbStatus.seriesCountByLabelName as unknown as Data[]}
|
||||||
|
onChange={handleTabChange}
|
||||||
|
onActionClick={handleFilterClick("seriesCountByLabelName")}
|
||||||
|
tabs={defaultProps.tabs.seriesCountByLabelName}
|
||||||
|
chartContainer={defaultProps.containerRefs.seriesCountByLabelName}
|
||||||
|
totalSeries={tsdbStatus.totalSeries}
|
||||||
|
tabId={"seriesCountByLabelName"}
|
||||||
|
tableHeaderCells={LABEL_NAMES_HEADERS}
|
||||||
|
/>
|
||||||
|
<MetricsContent
|
||||||
|
sectionTitle={"Label=value pairs with the highest number of series"}
|
||||||
activeTab={stateTabs.seriesCountByLabelValuePair}
|
activeTab={stateTabs.seriesCountByLabelValuePair}
|
||||||
rows={tsdbStatus.seriesCountByLabelValuePair as unknown as Data[]}
|
rows={tsdbStatus.seriesCountByLabelValuePair as unknown as Data[]}
|
||||||
onChange={handleTabChange}
|
onChange={handleTabChange}
|
||||||
@ -104,10 +115,10 @@ const CardinalityPanel: FC = () => {
|
|||||||
chartContainer={defaultProps.containerRefs.seriesCountByLabelValuePair}
|
chartContainer={defaultProps.containerRefs.seriesCountByLabelValuePair}
|
||||||
totalSeries={tsdbStatus.totalSeries}
|
totalSeries={tsdbStatus.totalSeries}
|
||||||
tabId={"seriesCountByLabelValuePair"}
|
tabId={"seriesCountByLabelValuePair"}
|
||||||
sectionTitle={LABEL_VALUE_PAIR_CONTENT_TITLE}
|
tableHeaderCells={LABEL_VALUE_PAIRS_HEADERS}
|
||||||
tableHeaderCells={LABEL_VALUE_PAIRS_TABLE_HEADERS}
|
|
||||||
/>
|
/>
|
||||||
<MetricsContent
|
<MetricsContent
|
||||||
|
sectionTitle={"Labels with the highest number of unique values"}
|
||||||
activeTab={stateTabs.labelValueCountByLabelName}
|
activeTab={stateTabs.labelValueCountByLabelName}
|
||||||
rows={tsdbStatus.labelValueCountByLabelName as unknown as Data[]}
|
rows={tsdbStatus.labelValueCountByLabelName as unknown as Data[]}
|
||||||
onChange={handleTabChange}
|
onChange={handleTabChange}
|
||||||
@ -116,8 +127,7 @@ const CardinalityPanel: FC = () => {
|
|||||||
chartContainer={defaultProps.containerRefs.labelValueCountByLabelName}
|
chartContainer={defaultProps.containerRefs.labelValueCountByLabelName}
|
||||||
totalSeries={-1}
|
totalSeries={-1}
|
||||||
tabId={"labelValueCountByLabelName"}
|
tabId={"labelValueCountByLabelName"}
|
||||||
sectionTitle={LABELS_CONTENT_TITLE}
|
tableHeaderCells={LABELS_WITH_UNIQUE_VALUES_HEADERS}
|
||||||
tableHeaderCells={LABEL_WITH_UNIQUE_VALUES_TABLE_HEADERS}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,10 +1,64 @@
|
|||||||
import {HeadCell} from "../Table/types";
|
import {HeadCell} from "../Table/types";
|
||||||
|
|
||||||
export const METRICS_TABLE_HEADERS = [
|
export const METRIC_NAMES_HEADERS = [
|
||||||
{
|
{
|
||||||
disablePadding: false,
|
disablePadding: false,
|
||||||
id: "name",
|
id: "name",
|
||||||
label: "Metrics name",
|
label: "Metric name",
|
||||||
|
numeric: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disablePadding: false,
|
||||||
|
id: "value",
|
||||||
|
label: "Number of series",
|
||||||
|
numeric: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disablePadding: false,
|
||||||
|
id: "percentage",
|
||||||
|
label: "Percent of series",
|
||||||
|
numeric: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disablePadding: false,
|
||||||
|
id: "action",
|
||||||
|
label: "Action",
|
||||||
|
numeric: false,
|
||||||
|
}
|
||||||
|
] as HeadCell[];
|
||||||
|
|
||||||
|
export const LABEL_NAMES_HEADERS = [
|
||||||
|
{
|
||||||
|
disablePadding: false,
|
||||||
|
id: "name",
|
||||||
|
label: "Label name",
|
||||||
|
numeric: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disablePadding: false,
|
||||||
|
id: "value",
|
||||||
|
label: "Number of series",
|
||||||
|
numeric: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disablePadding: false,
|
||||||
|
id: "percentage",
|
||||||
|
label: "Percent of series",
|
||||||
|
numeric: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disablePadding: false,
|
||||||
|
id: "action",
|
||||||
|
label: "Action",
|
||||||
|
numeric: false,
|
||||||
|
}
|
||||||
|
] as HeadCell[];
|
||||||
|
|
||||||
|
export const LABEL_VALUE_PAIRS_HEADERS = [
|
||||||
|
{
|
||||||
|
disablePadding: false,
|
||||||
|
id: "name",
|
||||||
|
label: "Label=value pair",
|
||||||
numeric: false,
|
numeric: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -27,34 +81,7 @@ export const METRICS_TABLE_HEADERS = [
|
|||||||
}
|
}
|
||||||
]as HeadCell[];
|
]as HeadCell[];
|
||||||
|
|
||||||
export const LABEL_VALUE_PAIRS_TABLE_HEADERS = [
|
export const LABELS_WITH_UNIQUE_VALUES_HEADERS = [
|
||||||
{
|
|
||||||
disablePadding: false,
|
|
||||||
id: "name",
|
|
||||||
label: "Lable=value pair",
|
|
||||||
numeric: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
disablePadding: false,
|
|
||||||
id: "value",
|
|
||||||
label: "Number of series",
|
|
||||||
numeric: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
disablePadding: false,
|
|
||||||
id: "percentage",
|
|
||||||
label: "Percent of total label value pairs",
|
|
||||||
numeric: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
disablePadding: false,
|
|
||||||
id: "action",
|
|
||||||
label: "Action",
|
|
||||||
numeric: false,
|
|
||||||
}
|
|
||||||
]as HeadCell[];
|
|
||||||
|
|
||||||
export const LABEL_WITH_UNIQUE_VALUES_TABLE_HEADERS = [
|
|
||||||
{
|
{
|
||||||
disablePadding: false,
|
disablePadding: false,
|
||||||
id: "name",
|
id: "name",
|
||||||
@ -86,9 +113,3 @@ export const spinnerContainerStyles = (height: string) => {
|
|||||||
zIndex: 1000,
|
zIndex: 1000,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SPINNER_TITLE = "Please wait while cardinality stats is calculated. " +
|
|
||||||
"This may take some time if the db contains big number of time series";
|
|
||||||
export const SERIES_CONTENT_TITLE = "Metric names with the highest number of series";
|
|
||||||
export const LABEL_VALUE_PAIR_CONTENT_TITLE = "Label=value pairs with the highest number of series";
|
|
||||||
export const LABELS_CONTENT_TITLE = "Labels with the highest number of unique values";
|
|
||||||
|
@ -2,16 +2,17 @@ import {Containers, DefaultState, QueryUpdater, Tabs, TSDBStatus} from "./types"
|
|||||||
import {useRef} from "preact/compat";
|
import {useRef} from "preact/compat";
|
||||||
|
|
||||||
export const queryUpdater: QueryUpdater = {
|
export const queryUpdater: QueryUpdater = {
|
||||||
labelValueCountByLabelName: (query: string): string => `{${query}!=""}`,
|
seriesCountByMetricName: (query: string): string => {
|
||||||
|
return getSeriesSelector("__name__", query);
|
||||||
|
},
|
||||||
|
seriesCountByLabelName: (query: string): string => `{${query}!=""}`,
|
||||||
seriesCountByLabelValuePair: (query: string): string => {
|
seriesCountByLabelValuePair: (query: string): string => {
|
||||||
const a = query.split("=");
|
const a = query.split("=");
|
||||||
const label = a[0];
|
const label = a[0];
|
||||||
const value = a.slice(1).join("=");
|
const value = a.slice(1).join("=");
|
||||||
return getSeriesSelector(label, value);
|
return getSeriesSelector(label, value);
|
||||||
},
|
},
|
||||||
seriesCountByMetricName: (query: string): string => {
|
labelValueCountByLabelName: (query: string): string => `{${query}!=""}`,
|
||||||
return getSeriesSelector("__name__", query);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSeriesSelector = (label: string, value: string): string => {
|
const getSeriesSelector = (label: string, value: string): string => {
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import {MutableRef} from "preact/hooks";
|
import {MutableRef} from "preact/hooks";
|
||||||
|
|
||||||
export interface TSDBStatus {
|
export interface TSDBStatus {
|
||||||
labelValueCountByLabelName: TopHeapEntry[];
|
|
||||||
seriesCountByLabelValuePair: TopHeapEntry[];
|
|
||||||
seriesCountByMetricName: TopHeapEntry[];
|
|
||||||
totalSeries: number;
|
totalSeries: number;
|
||||||
totalLabelValuePairs: number;
|
totalLabelValuePairs: number;
|
||||||
|
seriesCountByMetricName: TopHeapEntry[];
|
||||||
|
seriesCountByLabelName: TopHeapEntry[];
|
||||||
|
seriesCountByLabelValuePair: TopHeapEntry[];
|
||||||
|
labelValueCountByLabelName: TopHeapEntry[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TopHeapEntry {
|
export interface TopHeapEntry {
|
||||||
@ -18,19 +19,22 @@ export type QueryUpdater = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Tabs {
|
export interface Tabs {
|
||||||
labelValueCountByLabelName: string[];
|
|
||||||
seriesCountByLabelValuePair: string[];
|
|
||||||
seriesCountByMetricName: string[];
|
seriesCountByMetricName: string[];
|
||||||
|
seriesCountByLabelName: string[];
|
||||||
|
seriesCountByLabelValuePair: string[];
|
||||||
|
labelValueCountByLabelName: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Containers<T> {
|
export interface Containers<T> {
|
||||||
labelValueCountByLabelName: MutableRef<T>;
|
|
||||||
seriesCountByLabelValuePair: MutableRef<T>;
|
|
||||||
seriesCountByMetricName: MutableRef<T>;
|
seriesCountByMetricName: MutableRef<T>;
|
||||||
|
seriesCountByLabelName: MutableRef<T>;
|
||||||
|
seriesCountByLabelValuePair: MutableRef<T>;
|
||||||
|
labelValueCountByLabelName: MutableRef<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DefaultState {
|
export interface DefaultState {
|
||||||
labelValueCountByLabelName: number;
|
|
||||||
seriesCountByLabelValuePair: number;
|
|
||||||
seriesCountByMetricName: number;
|
seriesCountByMetricName: number;
|
||||||
|
seriesCountByLabelName: number;
|
||||||
|
seriesCountByLabelValuePair: number;
|
||||||
|
labelValueCountByLabelName: number;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ const defaultTSDBStatus = {
|
|||||||
totalSeries: 0,
|
totalSeries: 0,
|
||||||
totalLabelValuePairs: 0,
|
totalLabelValuePairs: 0,
|
||||||
seriesCountByMetricName: [],
|
seriesCountByMetricName: [],
|
||||||
|
seriesCountByLabelName: [],
|
||||||
seriesCountByLabelValuePair: [],
|
seriesCountByLabelValuePair: [],
|
||||||
labelValueCountByLabelName: [],
|
labelValueCountByLabelName: [],
|
||||||
};
|
};
|
||||||
|
@ -268,6 +268,7 @@ See the [example VMUI at VictoriaMetrics playground](https://play.victoriametric
|
|||||||
VictoriaMetrics provides an ability to explore time series cardinality at `cardinality` tab in [vmui](#vmui) in the following ways:
|
VictoriaMetrics provides an ability to explore time series cardinality at `cardinality` tab in [vmui](#vmui) in the following ways:
|
||||||
|
|
||||||
- To identify metric names with the highest number of series.
|
- To identify metric names with the highest number of series.
|
||||||
|
- To idnetify labels with the highest number of series.
|
||||||
- To identify label=name pairs with the highest number of series.
|
- To identify label=name pairs with the highest number of series.
|
||||||
- To identify labels with the highest number of unique values.
|
- To identify labels with the highest number of unique values.
|
||||||
|
|
||||||
@ -275,8 +276,6 @@ By default cardinality explorer analyzes time series for the current date. It pr
|
|||||||
By default all the time series for the selected date are analyzed. It is possible to narrow down the analysis to series
|
By default all the time series for the selected date are analyzed. It is possible to narrow down the analysis to series
|
||||||
matching the specified [series selector](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors).
|
matching the specified [series selector](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors).
|
||||||
|
|
||||||
Cardinality explorer takes into account [deleted time series](#how-to-delete-time-series), because they stay in the inverted index for up to [-retentionPeriod](#retention). This means that the deleted time series take RAM, CPU, disk IO and disk space for the inverted index in the same way as other time series.
|
|
||||||
|
|
||||||
Cardinality explorer is built on top of [/api/v1/status/tsdb](#tsdb-stats).
|
Cardinality explorer is built on top of [/api/v1/status/tsdb](#tsdb-stats).
|
||||||
|
|
||||||
See [cardinality explorer playground](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/graph/#/cardinality).
|
See [cardinality explorer playground](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/graph/#/cardinality).
|
||||||
|
@ -272,6 +272,7 @@ See the [example VMUI at VictoriaMetrics playground](https://play.victoriametric
|
|||||||
VictoriaMetrics provides an ability to explore time series cardinality at `cardinality` tab in [vmui](#vmui) in the following ways:
|
VictoriaMetrics provides an ability to explore time series cardinality at `cardinality` tab in [vmui](#vmui) in the following ways:
|
||||||
|
|
||||||
- To identify metric names with the highest number of series.
|
- To identify metric names with the highest number of series.
|
||||||
|
- To idnetify labels with the highest number of series.
|
||||||
- To identify label=name pairs with the highest number of series.
|
- To identify label=name pairs with the highest number of series.
|
||||||
- To identify labels with the highest number of unique values.
|
- To identify labels with the highest number of unique values.
|
||||||
|
|
||||||
@ -279,8 +280,6 @@ By default cardinality explorer analyzes time series for the current date. It pr
|
|||||||
By default all the time series for the selected date are analyzed. It is possible to narrow down the analysis to series
|
By default all the time series for the selected date are analyzed. It is possible to narrow down the analysis to series
|
||||||
matching the specified [series selector](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors).
|
matching the specified [series selector](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors).
|
||||||
|
|
||||||
Cardinality explorer takes into account [deleted time series](#how-to-delete-time-series), because they stay in the inverted index for up to [-retentionPeriod](#retention). This means that the deleted time series take RAM, CPU, disk IO and disk space for the inverted index in the same way as other time series.
|
|
||||||
|
|
||||||
Cardinality explorer is built on top of [/api/v1/status/tsdb](#tsdb-stats).
|
Cardinality explorer is built on top of [/api/v1/status/tsdb](#tsdb-stats).
|
||||||
|
|
||||||
See [cardinality explorer playground](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/graph/#/cardinality).
|
See [cardinality explorer playground](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/graph/#/cardinality).
|
||||||
|
@ -834,10 +834,7 @@ func (is *indexSearch) searchLabelNamesWithFiltersOnDate(qt *querytracer.Tracer,
|
|||||||
if err := mp.Init(item, nsPrefixExpected); err != nil {
|
if err := mp.Init(item, nsPrefixExpected); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if mp.IsDeletedTag(dmis) {
|
if mp.GetMatchingSeriesCount(filter, dmis) == 0 {
|
||||||
continue
|
|
||||||
}
|
|
||||||
if mp.GetMatchingSeriesCount(filter) == 0 {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
labelName := mp.Tag.Key
|
labelName := mp.Tag.Key
|
||||||
@ -1000,10 +997,7 @@ func (is *indexSearch) searchLabelValuesWithFiltersOnDate(qt *querytracer.Tracer
|
|||||||
if err := mp.Init(item, nsPrefixExpected); err != nil {
|
if err := mp.Init(item, nsPrefixExpected); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if mp.IsDeletedTag(dmis) {
|
if mp.GetMatchingSeriesCount(filter, dmis) == 0 {
|
||||||
continue
|
|
||||||
}
|
|
||||||
if mp.GetMatchingSeriesCount(filter) == 0 {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
labelValue := mp.Tag.Value
|
labelValue := mp.Tag.Value
|
||||||
@ -1150,7 +1144,7 @@ func (is *indexSearch) searchTagValueSuffixesForPrefix(tvss map[string]struct{},
|
|||||||
if err := mp.Init(item, nsPrefix); err != nil {
|
if err := mp.Init(item, nsPrefix); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if mp.IsDeletedTag(dmis) {
|
if mp.GetMatchingSeriesCount(nil, dmis) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tagValue := mp.Tag.Value
|
tagValue := mp.Tag.Value
|
||||||
@ -1284,12 +1278,14 @@ func (is *indexSearch) getTSDBStatusWithFiltersForDate(qt *querytracer.Tracer, t
|
|||||||
ts := &is.ts
|
ts := &is.ts
|
||||||
kb := &is.kb
|
kb := &is.kb
|
||||||
mp := &is.mp
|
mp := &is.mp
|
||||||
thLabelValueCountByLabelName := newTopHeap(topN)
|
dmis := is.db.s.getDeletedMetricIDs()
|
||||||
thSeriesCountByLabelValuePair := newTopHeap(topN)
|
|
||||||
thSeriesCountByMetricName := newTopHeap(topN)
|
thSeriesCountByMetricName := newTopHeap(topN)
|
||||||
var tmp, labelName, labelNameValue []byte
|
thSeriesCountByLabelName := newTopHeap(topN)
|
||||||
|
thSeriesCountByLabelValuePair := newTopHeap(topN)
|
||||||
|
thLabelValueCountByLabelName := newTopHeap(topN)
|
||||||
|
var tmp, prevLabelName, prevLabelValuePair []byte
|
||||||
var labelValueCountByLabelName, seriesCountByLabelValuePair uint64
|
var labelValueCountByLabelName, seriesCountByLabelValuePair uint64
|
||||||
var totalSeries, totalLabelValuePairs uint64
|
var totalSeries, labelSeries, totalLabelValuePairs uint64
|
||||||
nameEqualBytes := []byte("__name__=")
|
nameEqualBytes := []byte("__name__=")
|
||||||
|
|
||||||
loopsPaceLimiter := 0
|
loopsPaceLimiter := 0
|
||||||
@ -1314,69 +1310,80 @@ func (is *indexSearch) getTSDBStatusWithFiltersForDate(qt *querytracer.Tracer, t
|
|||||||
if err := mp.Init(item, nsPrefixExpected); err != nil {
|
if err := mp.Init(item, nsPrefixExpected); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
matchingSeriesCount := mp.GetMatchingSeriesCount(filter)
|
matchingSeriesCount := mp.GetMatchingSeriesCount(filter, dmis)
|
||||||
if matchingSeriesCount == 0 {
|
if matchingSeriesCount == 0 {
|
||||||
// Skip rows without matching metricIDs.
|
// Skip rows without matching metricIDs.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tmp = append(tmp[:0], mp.Tag.Key...)
|
tmp = append(tmp[:0], mp.Tag.Key...)
|
||||||
tagKey := tmp
|
labelName := tmp
|
||||||
if isArtificialTagKey(tagKey) {
|
if isArtificialTagKey(labelName) {
|
||||||
// Skip artificially created tag keys.
|
// Skip artificially created tag keys.
|
||||||
kb.B = append(kb.B[:0], prefix...)
|
kb.B = append(kb.B[:0], prefix...)
|
||||||
if len(tagKey) > 0 && tagKey[0] == compositeTagKeyPrefix {
|
if len(labelName) > 0 && labelName[0] == compositeTagKeyPrefix {
|
||||||
kb.B = append(kb.B, compositeTagKeyPrefix)
|
kb.B = append(kb.B, compositeTagKeyPrefix)
|
||||||
} else {
|
} else {
|
||||||
kb.B = marshalTagValue(kb.B, tagKey)
|
kb.B = marshalTagValue(kb.B, labelName)
|
||||||
}
|
}
|
||||||
kb.B[len(kb.B)-1]++
|
kb.B[len(kb.B)-1]++
|
||||||
ts.Seek(kb.B)
|
ts.Seek(kb.B)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(tagKey) == 0 {
|
if len(labelName) == 0 {
|
||||||
tagKey = append(tagKey, "__name__"...)
|
labelName = append(labelName, "__name__"...)
|
||||||
tmp = tagKey
|
tmp = labelName
|
||||||
|
}
|
||||||
|
if string(labelName) == "__name__" {
|
||||||
|
totalSeries += uint64(matchingSeriesCount)
|
||||||
}
|
}
|
||||||
tmp = append(tmp, '=')
|
tmp = append(tmp, '=')
|
||||||
tmp = append(tmp, mp.Tag.Value...)
|
tmp = append(tmp, mp.Tag.Value...)
|
||||||
tagKeyValue := tmp
|
labelValuePair := tmp
|
||||||
if string(tagKey) == "__name__" {
|
if len(prevLabelName) == 0 {
|
||||||
totalSeries += uint64(matchingSeriesCount)
|
prevLabelName = append(prevLabelName[:0], labelName...)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(tagKey, labelName) {
|
if string(labelName) != string(prevLabelName) {
|
||||||
thLabelValueCountByLabelName.pushIfNonEmpty(labelName, labelValueCountByLabelName)
|
thLabelValueCountByLabelName.push(prevLabelName, labelValueCountByLabelName)
|
||||||
|
thSeriesCountByLabelName.push(prevLabelName, labelSeries)
|
||||||
|
labelSeries = 0
|
||||||
labelValueCountByLabelName = 0
|
labelValueCountByLabelName = 0
|
||||||
labelName = append(labelName[:0], tagKey...)
|
prevLabelName = append(prevLabelName[:0], labelName...)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(tagKeyValue, labelNameValue) {
|
if len(prevLabelValuePair) == 0 {
|
||||||
thSeriesCountByLabelValuePair.pushIfNonEmpty(labelNameValue, seriesCountByLabelValuePair)
|
prevLabelValuePair = append(prevLabelValuePair[:0], labelValuePair...)
|
||||||
if bytes.HasPrefix(labelNameValue, nameEqualBytes) {
|
labelValueCountByLabelName++
|
||||||
thSeriesCountByMetricName.pushIfNonEmpty(labelNameValue[len(nameEqualBytes):], seriesCountByLabelValuePair)
|
}
|
||||||
|
if string(labelValuePair) != string(prevLabelValuePair) {
|
||||||
|
thSeriesCountByLabelValuePair.push(prevLabelValuePair, seriesCountByLabelValuePair)
|
||||||
|
if bytes.HasPrefix(prevLabelValuePair, nameEqualBytes) {
|
||||||
|
thSeriesCountByMetricName.push(prevLabelValuePair[len(nameEqualBytes):], seriesCountByLabelValuePair)
|
||||||
}
|
}
|
||||||
seriesCountByLabelValuePair = 0
|
seriesCountByLabelValuePair = 0
|
||||||
labelValueCountByLabelName++
|
labelValueCountByLabelName++
|
||||||
labelNameValue = append(labelNameValue[:0], tagKeyValue...)
|
prevLabelValuePair = append(prevLabelValuePair[:0], labelValuePair...)
|
||||||
}
|
}
|
||||||
// Take into account deleted timeseries too.
|
|
||||||
// It is OK if series can be counted multiple times in rare cases -
|
// It is OK if series can be counted multiple times in rare cases -
|
||||||
// the returned number is an estimation.
|
// the returned number is an estimation.
|
||||||
|
labelSeries += uint64(matchingSeriesCount)
|
||||||
seriesCountByLabelValuePair += uint64(matchingSeriesCount)
|
seriesCountByLabelValuePair += uint64(matchingSeriesCount)
|
||||||
totalLabelValuePairs += uint64(matchingSeriesCount)
|
totalLabelValuePairs += uint64(matchingSeriesCount)
|
||||||
}
|
}
|
||||||
if err := ts.Error(); err != nil {
|
if err := ts.Error(); err != nil {
|
||||||
return nil, fmt.Errorf("error when counting time series by metric names: %w", err)
|
return nil, fmt.Errorf("error when counting time series by metric names: %w", err)
|
||||||
}
|
}
|
||||||
thLabelValueCountByLabelName.pushIfNonEmpty(labelName, labelValueCountByLabelName)
|
thLabelValueCountByLabelName.push(prevLabelName, labelValueCountByLabelName)
|
||||||
thSeriesCountByLabelValuePair.pushIfNonEmpty(labelNameValue, seriesCountByLabelValuePair)
|
thSeriesCountByLabelName.push(prevLabelName, labelSeries)
|
||||||
if bytes.HasPrefix(labelNameValue, nameEqualBytes) {
|
thSeriesCountByLabelValuePair.push(prevLabelValuePair, seriesCountByLabelValuePair)
|
||||||
thSeriesCountByMetricName.pushIfNonEmpty(labelNameValue[len(nameEqualBytes):], seriesCountByLabelValuePair)
|
if bytes.HasPrefix(prevLabelValuePair, nameEqualBytes) {
|
||||||
|
thSeriesCountByMetricName.push(prevLabelValuePair[len(nameEqualBytes):], seriesCountByLabelValuePair)
|
||||||
}
|
}
|
||||||
status := &TSDBStatus{
|
status := &TSDBStatus{
|
||||||
SeriesCountByMetricName: thSeriesCountByMetricName.getSortedResult(),
|
|
||||||
LabelValueCountByLabelName: thLabelValueCountByLabelName.getSortedResult(),
|
|
||||||
SeriesCountByLabelValuePair: thSeriesCountByLabelValuePair.getSortedResult(),
|
|
||||||
TotalSeries: totalSeries,
|
TotalSeries: totalSeries,
|
||||||
TotalLabelValuePairs: totalLabelValuePairs,
|
TotalLabelValuePairs: totalLabelValuePairs,
|
||||||
|
SeriesCountByMetricName: thSeriesCountByMetricName.getSortedResult(),
|
||||||
|
SeriesCountByLabelName: thSeriesCountByLabelName.getSortedResult(),
|
||||||
|
SeriesCountByLabelValuePair: thSeriesCountByLabelValuePair.getSortedResult(),
|
||||||
|
LabelValueCountByLabelName: thLabelValueCountByLabelName.getSortedResult(),
|
||||||
}
|
}
|
||||||
return status, nil
|
return status, nil
|
||||||
}
|
}
|
||||||
@ -1388,8 +1395,9 @@ type TSDBStatus struct {
|
|||||||
TotalSeries uint64
|
TotalSeries uint64
|
||||||
TotalLabelValuePairs uint64
|
TotalLabelValuePairs uint64
|
||||||
SeriesCountByMetricName []TopHeapEntry
|
SeriesCountByMetricName []TopHeapEntry
|
||||||
LabelValueCountByLabelName []TopHeapEntry
|
SeriesCountByLabelName []TopHeapEntry
|
||||||
SeriesCountByLabelValuePair []TopHeapEntry
|
SeriesCountByLabelValuePair []TopHeapEntry
|
||||||
|
LabelValueCountByLabelName []TopHeapEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
func (status *TSDBStatus) hasEntries() bool {
|
func (status *TSDBStatus) hasEntries() bool {
|
||||||
@ -1415,7 +1423,7 @@ type TopHeapEntry struct {
|
|||||||
Count uint64
|
Count uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *topHeap) pushIfNonEmpty(name []byte, count uint64) {
|
func (th *topHeap) push(name []byte, count uint64) {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -3108,39 +3116,27 @@ func (mp *tagToMetricIDsRowParser) ParseMetricIDs() {
|
|||||||
mp.metricIDsParsed = true
|
mp.metricIDsParsed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMatchingSeriesCount returns the number of series in mp, which match metricIDs from the given filter.
|
// GetMatchingSeriesCount returns the number of series in mp, which match metricIDs from the given filter
|
||||||
|
// and do not match metricIDs from negativeFilter.
|
||||||
//
|
//
|
||||||
// if filter is empty, then all series in mp are taken into account.
|
// if filter is empty, then all series in mp are taken into account.
|
||||||
func (mp *tagToMetricIDsRowParser) GetMatchingSeriesCount(filter *uint64set.Set) int {
|
func (mp *tagToMetricIDsRowParser) GetMatchingSeriesCount(filter, negativeFilter *uint64set.Set) int {
|
||||||
if filter == nil {
|
if filter == nil && negativeFilter.Len() == 0 {
|
||||||
return mp.MetricIDsLen()
|
return mp.MetricIDsLen()
|
||||||
}
|
}
|
||||||
mp.ParseMetricIDs()
|
mp.ParseMetricIDs()
|
||||||
n := 0
|
n := 0
|
||||||
for _, metricID := range mp.MetricIDs {
|
for _, metricID := range mp.MetricIDs {
|
||||||
if filter.Has(metricID) {
|
if filter != nil && !filter.Has(metricID) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !negativeFilter.Has(metricID) {
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDeletedTag verifies whether the tag from mp is deleted according to dmis.
|
|
||||||
//
|
|
||||||
// dmis must contain deleted MetricIDs.
|
|
||||||
func (mp *tagToMetricIDsRowParser) IsDeletedTag(dmis *uint64set.Set) bool {
|
|
||||||
if dmis.Len() == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
mp.ParseMetricIDs()
|
|
||||||
for _, metricID := range mp.MetricIDs {
|
|
||||||
if !dmis.Has(metricID) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeTagToMetricIDsRows(data []byte, items []mergeset.Item) ([]byte, []mergeset.Item) {
|
func mergeTagToMetricIDsRows(data []byte, items []mergeset.Item) ([]byte, []mergeset.Item) {
|
||||||
data, items = mergeTagToMetricIDsRowsInternal(data, items, nsPrefixTagToMetricIDs)
|
data, items = mergeTagToMetricIDsRowsInternal(data, items, nsPrefixTagToMetricIDs)
|
||||||
data, items = mergeTagToMetricIDsRowsInternal(data, items, nsPrefixDateTagToMetricIDs)
|
data, items = mergeTagToMetricIDsRowsInternal(data, items, nsPrefixDateTagToMetricIDs)
|
||||||
|
@ -1824,6 +1824,27 @@ func TestSearchTSIDWithTimeRange(t *testing.T) {
|
|||||||
if !reflect.DeepEqual(status.SeriesCountByMetricName, expectedSeriesCountByMetricName) {
|
if !reflect.DeepEqual(status.SeriesCountByMetricName, expectedSeriesCountByMetricName) {
|
||||||
t.Fatalf("unexpected SeriesCountByMetricName;\ngot\n%v\nwant\n%v", status.SeriesCountByMetricName, expectedSeriesCountByMetricName)
|
t.Fatalf("unexpected SeriesCountByMetricName;\ngot\n%v\nwant\n%v", status.SeriesCountByMetricName, expectedSeriesCountByMetricName)
|
||||||
}
|
}
|
||||||
|
expectedSeriesCountByLabelName := []TopHeapEntry{
|
||||||
|
{
|
||||||
|
Name: "__name__",
|
||||||
|
Count: 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "constant",
|
||||||
|
Count: 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "day",
|
||||||
|
Count: 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "uniqueid",
|
||||||
|
Count: 1000,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(status.SeriesCountByLabelName, expectedSeriesCountByLabelName) {
|
||||||
|
t.Fatalf("unexpected SeriesCountByLabelName;\ngot\n%v\nwant\n%v", status.SeriesCountByLabelName, expectedSeriesCountByLabelName)
|
||||||
|
}
|
||||||
expectedLabelValueCountByLabelName := []TopHeapEntry{
|
expectedLabelValueCountByLabelName := []TopHeapEntry{
|
||||||
{
|
{
|
||||||
Name: "uniqueid",
|
Name: "uniqueid",
|
||||||
|
Loading…
Reference in New Issue
Block a user