diff --git a/web/api/v1/api.go b/web/api/v1/api.go index 0ec8467fa..a7b8a4fb0 100644 --- a/web/api/v1/api.go +++ b/web/api/v1/api.go @@ -1589,9 +1589,15 @@ type HeadStats struct { MaxTime int64 `json:"maxTime"` } +// TSDBStats has information about the TSDB. +type TSDBStats struct { + MinTime int64 `json:"minTime"` +} + // TSDBStatus has information of cardinality statistics from postings. type TSDBStatus struct { HeadStats HeadStats `json:"headStats"` + TSDBStats TSDBStats `json:"tsdbStats"` SeriesCountByMetricName []TSDBStat `json:"seriesCountByMetricName"` LabelValueCountByLabelName []TSDBStat `json:"labelValueCountByLabelName"` MemoryInBytesByLabelName []TSDBStat `json:"memoryInBytesByLabelName"` @@ -1625,12 +1631,18 @@ func (api *API) serveTSDBStatus(r *http.Request) apiFuncResult { return apiFuncResult{nil, &apiError{errorInternal, fmt.Errorf("error gathering runtime status: %w", err)}, nil, nil} } chunkCount := int64(math.NaN()) + lowestTimestamp := int64(math.NaN()) for _, mF := range metrics { - if *mF.Name == "prometheus_tsdb_head_chunks" { + switch *mF.Name { + case "prometheus_tsdb_head_chunks": m := mF.Metric[0] if m.Gauge != nil { chunkCount = int64(m.Gauge.GetValue()) - break + } + case "prometheus_tsdb_lowest_timestamp": + m := mF.Metric[0] + if m.Gauge != nil { + lowestTimestamp = int64(m.Gauge.GetValue()) } } } @@ -1642,6 +1654,7 @@ func (api *API) serveTSDBStatus(r *http.Request) apiFuncResult { MaxTime: s.MaxTime, NumLabelPairs: s.IndexPostingStats.NumLabelPairs, }, + TSDBStats: TSDBStats{MinTime: lowestTimestamp}, SeriesCountByMetricName: TSDBStatsFromIndexStats(s.IndexPostingStats.CardinalityMetricsStats), LabelValueCountByLabelName: TSDBStatsFromIndexStats(s.IndexPostingStats.CardinalityLabelStats), MemoryInBytesByLabelName: TSDBStatsFromIndexStats(s.IndexPostingStats.LabelValueStats), diff --git a/web/ui/react-app/src/pages/tsdbStatus/TSDBStatus.test.tsx b/web/ui/react-app/src/pages/tsdbStatus/TSDBStatus.test.tsx index b7c683115..7cdea61e5 100644 --- a/web/ui/react-app/src/pages/tsdbStatus/TSDBStatus.test.tsx +++ b/web/ui/react-app/src/pages/tsdbStatus/TSDBStatus.test.tsx @@ -20,6 +20,9 @@ const fakeTSDBStatusResponse: { minTime: 1591516800000, maxTime: 1598896800143, }, + tsdbStats: { + minTime: 1591516800000, + }, labelValueCountByLabelName: [ { name: '__name__', @@ -64,6 +67,9 @@ const fakeEmptyTSDBStatusResponse: { minTime: 9223372036854776000, maxTime: -9223372036854776000, }, + tsdbStats: { + minTime: 9223372036854776000, + }, labelValueCountByLabelName: [], seriesCountByMetricName: [], memoryInBytesByLabelName: [], @@ -84,6 +90,9 @@ const fakeInvalidTimestampTSDBStatusResponse: { minTime: 9223372036854776000, maxTime: -9223372036854776000, }, + tsdbStats: { + minTime: 9223372036854776000, + }, labelValueCountByLabelName: [], seriesCountByMetricName: [], memoryInBytesByLabelName: [], diff --git a/web/ui/react-app/src/pages/tsdbStatus/TSDBStatus.tsx b/web/ui/react-app/src/pages/tsdbStatus/TSDBStatus.tsx index bc4dfe99b..3e9069b55 100644 --- a/web/ui/react-app/src/pages/tsdbStatus/TSDBStatus.tsx +++ b/web/ui/react-app/src/pages/tsdbStatus/TSDBStatus.tsx @@ -19,8 +19,13 @@ interface HeadStats { maxTime: number; } +interface TSDBStats { + minTime: number; +} + export interface TSDBMap { headStats: HeadStats; + tsdbStats: TSDBStats; seriesCountByMetricName: Stats[]; labelValueCountByLabelName: Stats[]; memoryInBytesByLabelName: Stats[]; @@ -45,29 +50,49 @@ export const TSDBStatusContent: FC = ({ } }; const { chunkCount, numSeries, numLabelPairs, minTime, maxTime } = headStats; - const stats = [ + const headStatsKV = [ { header: 'Number of Series', value: numSeries }, { header: 'Number of Chunks', value: chunkCount }, { header: 'Number of Label Pairs', value: numLabelPairs }, { header: 'Current Min Time', value: `${unixToTime(minTime)}` }, { header: 'Current Max Time', value: `${unixToTime(maxTime)}` }, ]; + const tsdbStatsKV = [{ header: 'Current Min Time', value: `${unixToTime(minTime)}` }]; return (

TSDB Status

-

Head Stats

+

TSDB Stats

- {stats.map(({ header }) => { + {tsdbStatsKV.map(({ header }) => { return ; })} - {stats.map(({ header, value }) => { + {tsdbStatsKV.map(({ header, value }) => { + return ; + })} + + +
{header}
{value}
+
+

Head Stats

+
+ + + + {headStatsKV.map(({ header }) => { + return ; + })} + + + + + {headStatsKV.map(({ header, value }) => { return ; })}
{header}
{value}