From 3c45256736a3cc5ab4f5374e04943ae3d04a26d3 Mon Sep 17 00:00:00 2001 From: Yury Molodov Date: Tue, 25 Apr 2023 11:21:57 +0200 Subject: [PATCH] vmui: add a comparison of data to the `Cardinality Explorer` (#4123) * feat: add button "show today" to date picker * feat: add comparison with the prev day (#3967) * vmui/docs: add comparison of data to cardinality page --- .../TenantsConfiguration/style.scss | 2 +- .../Main/DatePicker/Calendar/Calendar.tsx | 19 +++++++ .../Calendar/CalendarBody/CalendarBody.tsx | 7 ++- .../Main/DatePicker/Calendar/style.scss | 6 ++ .../CardinalityTotals/CardinalityTotals.tsx | 30 ++++++++-- .../CardinalityTotals/style.scss | 56 +++++++++---------- .../MetricsContent/MetricsContent.tsx | 3 + .../pages/CardinalityPanel/Table/Table.tsx | 2 +- .../Table/TableCells/TableCells.tsx | 45 ++++++++++++++- .../CardinalityPanel/Table/TableHead.tsx | 2 +- .../src/pages/CardinalityPanel/Table/types.ts | 2 + .../pages/CardinalityPanel/appConfigurator.ts | 7 ++- .../hooks/useCardinalityFetch.ts | 51 +++++++++++++---- .../vmui/src/pages/CardinalityPanel/index.tsx | 2 + .../src/pages/CardinalityPanel/style.scss | 21 +++++++ .../vmui/src/pages/CardinalityPanel/types.ts | 3 + .../src/styles/components/dynamic-number.scss | 26 +++++++++ app/vmui/packages/vmui/src/styles/style.scss | 1 + docs/CHANGELOG.md | 1 + 19 files changed, 227 insertions(+), 59 deletions(-) create mode 100644 app/vmui/packages/vmui/src/styles/components/dynamic-number.scss diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/TenantsConfiguration/style.scss b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/TenantsConfiguration/style.scss index b394a8707c..30d6cb60ea 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/TenantsConfiguration/style.scss +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/TenantsConfiguration/style.scss @@ -1,4 +1,4 @@ -@use "../../../../styles/variables" as *; +@use "src/styles/variables" as *; .vm-tenant-input { position: relative; diff --git a/app/vmui/packages/vmui/src/components/Main/DatePicker/Calendar/Calendar.tsx b/app/vmui/packages/vmui/src/components/Main/DatePicker/Calendar/Calendar.tsx index ec809a858e..53a38341f8 100644 --- a/app/vmui/packages/vmui/src/components/Main/DatePicker/Calendar/Calendar.tsx +++ b/app/vmui/packages/vmui/src/components/Main/DatePicker/Calendar/Calendar.tsx @@ -8,6 +8,7 @@ import "./style.scss"; import useDeviceDetect from "../../../../hooks/useDeviceDetect"; import classNames from "classnames"; import MonthsList from "./MonthsList/MonthsList"; +import Button from "../../Button/Button"; interface DatePickerProps { date: Date | Dayjs @@ -29,6 +30,9 @@ const Calendar: FC = ({ const [viewType, setViewType] = useState(CalendarTypeView.days); const [viewDate, setViewDate] = useState(dayjs.tz(date)); const [selectDate, setSelectDate] = useState(dayjs.tz(date)); + + const today = dayjs().startOf("day").tz(); + const viewDateIsToday = today.format() === viewDate.format(); const { isMobile } = useDeviceDetect(); const toggleDisplayYears = () => { @@ -44,6 +48,10 @@ const Calendar: FC = ({ setSelectDate(date); }; + const handleToday = () => { + setViewDate(today); + }; + useEffect(() => { if (selectDate.format() === dayjs.tz(date).format()) return; onChange(selectDate.format(format)); @@ -88,6 +96,17 @@ const Calendar: FC = ({ onChangeViewDate={handleChangeViewDate} /> )} + {!viewDateIsToday && (viewType === CalendarTypeView.days) && ( +
+ +
+ )} ); }; diff --git a/app/vmui/packages/vmui/src/components/Main/DatePicker/Calendar/CalendarBody/CalendarBody.tsx b/app/vmui/packages/vmui/src/components/Main/DatePicker/Calendar/CalendarBody/CalendarBody.tsx index 2b24d3fb95..5edd4b2585 100644 --- a/app/vmui/packages/vmui/src/components/Main/DatePicker/Calendar/CalendarBody/CalendarBody.tsx +++ b/app/vmui/packages/vmui/src/components/Main/DatePicker/Calendar/CalendarBody/CalendarBody.tsx @@ -11,6 +11,7 @@ interface CalendarBodyProps { const weekday = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; const CalendarBody: FC = ({ viewDate, selectDate, onChangeSelectDate }) => { + const format = "YYYY-MM-DD"; const today = dayjs().tz().startOf("day"); const days: (Dayjs|null)[] = useMemo(() => { @@ -45,10 +46,10 @@ const CalendarBody: FC = ({ viewDate, selectDate, onChangeSel "vm-calendar-body-cell": true, "vm-calendar-body-cell_day": true, "vm-calendar-body-cell_day_empty": !d, - "vm-calendar-body-cell_day_active": (d && d.toISOString()) === selectDate.startOf("day").toISOString(), - "vm-calendar-body-cell_day_today": (d && d.toISOString()) === today.toISOString() + "vm-calendar-body-cell_day_active": (d && d.format(format)) === selectDate.format(format), + "vm-calendar-body-cell_day_today": (d && d.format(format)) === today.format(format) })} - key={d ? d.toISOString() : i} + key={d ? d.format(format) : i} onClick={createHandlerSelectDate(d)} > {d && d.format("D")} diff --git a/app/vmui/packages/vmui/src/components/Main/DatePicker/Calendar/style.scss b/app/vmui/packages/vmui/src/components/Main/DatePicker/Calendar/style.scss index f3b8f91ce4..3b7d7074ec 100644 --- a/app/vmui/packages/vmui/src/components/Main/DatePicker/Calendar/style.scss +++ b/app/vmui/packages/vmui/src/components/Main/DatePicker/Calendar/style.scss @@ -166,4 +166,10 @@ } } } + + &-footer { + display: flex; + align-items: center; + justify-content: flex-end; + } } diff --git a/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityTotals/CardinalityTotals.tsx b/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityTotals/CardinalityTotals.tsx index 07a828428a..92ae2129a1 100644 --- a/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityTotals/CardinalityTotals.tsx +++ b/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityTotals/CardinalityTotals.tsx @@ -10,12 +10,14 @@ import "./style.scss"; export interface CardinalityTotalsProps { totalSeries: number; totalSeriesAll: number; + totalSeriesPrev: number; totalLabelValuePairs: number; seriesCountByMetricName: TopHeapEntry[]; } const CardinalityTotals: FC = ({ totalSeries, + totalSeriesPrev, totalSeriesAll, seriesCountByMetricName }) => { @@ -27,11 +29,14 @@ const CardinalityTotals: FC = ({ const isMetric = /__name__/.test(match || ""); const progress = seriesCountByMetricName[0]?.value / totalSeriesAll * 100; + const diff = totalSeries - totalSeriesPrev; + const dynamic = Math.abs(diff) / totalSeriesPrev * 100; const totals = [ { title: "Total series", value: totalSeries.toLocaleString("en-US"), + dynamic: !totalSeries || !totalSeriesPrev ? "" : `${dynamic.toFixed(2)}%`, display: !focusLabel, info: `The total number of active time series. A time series is uniquely identified by its name plus a set of its labels. @@ -57,20 +62,33 @@ const CardinalityTotals: FC = ({ "vm-cardinality-totals_mobile": isMobile })} > - {totals.map(({ title, value, info }) => ( + {totals.map(({ title, value, info, dynamic }) => (
-
+

+ {title} {info && ( - {info}

}> -
+ {info}

}> +
)} -

{title}

-

+ {value} + {!!dynamic && ( + + 0, + })} + > + {dynamic} + + + )}
))} diff --git a/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityTotals/style.scss b/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityTotals/style.scss index 459cb2c090..128db7519c 100644 --- a/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityTotals/style.scss +++ b/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityTotals/style.scss @@ -5,56 +5,52 @@ flex-wrap: wrap; align-content: flex-start; justify-content: flex-start; - gap: $padding-global; + gap: $padding-medium; flex-grow: 1; &_mobile { - gap: $padding-small; + gap: $padding-global; justify-content: center; } &-card { - display: flex; + display: grid; + grid-template-columns: auto 1fr; align-items: center; justify-content: center; - gap: 4px; + gap: $padding-small 4px; - &-header { + &__info-icon { + width: 12px; display: flex; align-items: center; justify-content: center; + color: $color-primary; + } + + &__title { + display: flex; + align-items: center; + justify-content: flex-start; gap: 4px; + grid-column: 1/-1; + color: $color-text; + } - &__info-icon { - width: 12px; - display: flex; - align-items: center; - justify-content: center; - color: $color-primary; - } - - &__title { - font-weight: bold; - color: $color-text; - - &:after { - content: ':'; - } - } - - &__tooltip { - max-width: 280px; - white-space: normal; - padding: $padding-small; - line-height: 130%; - font-size: $font-size; - } + &__tooltip { + max-width: 280px; + white-space: normal; + padding: $padding-small; + line-height: $font-size; + font-size: $font-size; } &__value { font-weight: bold; color: $color-primary; - font-size: $font-size-medium; + font-size: $font-size-large; + line-height: $font-size; + text-align: center; } } } diff --git a/app/vmui/packages/vmui/src/pages/CardinalityPanel/MetricsContent/MetricsContent.tsx b/app/vmui/packages/vmui/src/pages/CardinalityPanel/MetricsContent/MetricsContent.tsx index 8210fcfa21..78ac136668 100644 --- a/app/vmui/packages/vmui/src/pages/CardinalityPanel/MetricsContent/MetricsContent.tsx +++ b/app/vmui/packages/vmui/src/pages/CardinalityPanel/MetricsContent/MetricsContent.tsx @@ -18,6 +18,7 @@ interface MetricsProperties { tabs: string[]; chartContainer: MutableRef | undefined; totalSeries: number, + totalSeriesPrev: number, sectionTitle: string; tip?: string; tableHeaderCells: HeadCell[]; @@ -28,6 +29,7 @@ const MetricsContent: FC = ({ tabs: tabsProps = [], chartContainer, totalSeries, + totalSeriesPrev, onActionClick, sectionTitle, tip, @@ -40,6 +42,7 @@ const MetricsContent: FC = ({ ); diff --git a/app/vmui/packages/vmui/src/pages/CardinalityPanel/Table/Table.tsx b/app/vmui/packages/vmui/src/pages/CardinalityPanel/Table/Table.tsx index d23d5f2b90..d23547227c 100644 --- a/app/vmui/packages/vmui/src/pages/CardinalityPanel/Table/Table.tsx +++ b/app/vmui/packages/vmui/src/pages/CardinalityPanel/Table/Table.tsx @@ -26,7 +26,7 @@ const EnhancedTable: FC = ({ const sortedData = stableSort(rows, getComparator(order, orderBy)); return ( - +
void; } -const TableCells: FC = ({ row, totalSeries, onActionClick }) => { +const TableCells: FC = ({ + row, + totalSeries, + totalSeriesPrev, + onActionClick +}) => { const progress = totalSeries > 0 ? row.value / totalSeries * 100 : -1; + const progressPrev = totalSeriesPrev > 0 ? row.valuePrev / totalSeriesPrev * 100 : -1; + const hasProgresses = [progress, progressPrev].some(p => p === -1); + + const diffPercent = progress - progressPrev; + const relationPrevDay = hasProgresses ? "" : `${diffPercent.toFixed(2)}%`; const handleActionClick = () => { onActionClick(row.name); @@ -35,13 +47,42 @@ const TableCells: FC = ({ row, totalSeries, onActionClick key={row.value} > {row.value} + + {!!row.diff && ( + + 0, + })} + > +  {row.diff > 0 ? "+" : ""}{row.diff} + + + )} {progress > 0 && ( )} {headerCells.map((headCell) => (
- +
+ + {relationPrevDay && ( + + 0, + })} + > + {relationPrevDay} + + + )} +
+
fetch(url))); + const [resp, respPrev, respTotals] = await Promise.all(responses.map(resp => resp.json())); + if (responses[0].ok) { + const { data: dataTotal } = respTotals; + const prevResult = { ...respPrev.data } as TSDBStatus; + const result = { ...resp.data } as TSDBStatus; + result.totalSeriesByAll = dataTotal?.totalSeries; + result.totalSeriesPrev = prevResult?.totalSeries; const name = match?.replace(/[{}"]/g, ""); result.seriesCountByLabelValuePair = result.seriesCountByLabelValuePair.filter(s => s.name !== name); + Object.keys(result).forEach(k => { + const key = k as keyof TSDBStatus; + const entries = result[key]; + const prevEntries = prevResult[key]; + + if (Array.isArray(entries) && Array.isArray(prevEntries)) { + entries.forEach((entry) => { + const valuePrev = prevEntries.find(prevEntry => prevEntry.name === entry.name)?.value; + entry.diff = valuePrev ? entry.value - valuePrev : 0; + entry.valuePrev = valuePrev || 0; + }); + } + }); + setTSDBStatus(result); setIsLoading(false); } else { diff --git a/app/vmui/packages/vmui/src/pages/CardinalityPanel/index.tsx b/app/vmui/packages/vmui/src/pages/CardinalityPanel/index.tsx index b005d9f5da..e65959738c 100644 --- a/app/vmui/packages/vmui/src/pages/CardinalityPanel/index.tsx +++ b/app/vmui/packages/vmui/src/pages/CardinalityPanel/index.tsx @@ -55,6 +55,7 @@ const CardinalityPanel: FC = () => { {isLoading && } { onActionClick={handleFilterClick(keyName)} tabs={defaultState.tabs[keyName as keyof Tabs]} chartContainer={defaultState.containerRefs[keyName as keyof Containers]} + totalSeriesPrev={appConfigurator.totalSeries(keyName, true)} totalSeries={appConfigurator.totalSeries(keyName)} tableHeaderCells={tablesHeaders[keyName]} /> diff --git a/app/vmui/packages/vmui/src/pages/CardinalityPanel/style.scss b/app/vmui/packages/vmui/src/pages/CardinalityPanel/style.scss index f85dc95eb8..d503e3c234 100644 --- a/app/vmui/packages/vmui/src/pages/CardinalityPanel/style.scss +++ b/app/vmui/packages/vmui/src/pages/CardinalityPanel/style.scss @@ -18,4 +18,25 @@ flex-grow: 1; width: 100%; } + + &-table { + &__header { + th:first-child { + width: 60%; + } + + th:not(:first-child) { + width: auto; + } + } + + + &__progress { + display: grid; + grid-template-columns: minmax(200px, 1fr) 70px; + align-items: center; + justify-content: flex-start; + gap: $padding-small; + } + } } diff --git a/app/vmui/packages/vmui/src/pages/CardinalityPanel/types.ts b/app/vmui/packages/vmui/src/pages/CardinalityPanel/types.ts index 14bd3316e3..49464ed82c 100644 --- a/app/vmui/packages/vmui/src/pages/CardinalityPanel/types.ts +++ b/app/vmui/packages/vmui/src/pages/CardinalityPanel/types.ts @@ -4,6 +4,7 @@ export interface TSDBStatus { totalSeries: number; totalLabelValuePairs: number; totalSeriesByAll: number, + totalSeriesPrev: number, seriesCountByMetricName: TopHeapEntry[]; seriesCountByLabelName: TopHeapEntry[]; seriesCountByFocusLabelValue: TopHeapEntry[]; @@ -14,6 +15,8 @@ export interface TSDBStatus { export interface TopHeapEntry { name: string; value: number; + diff: number; + valuePrev: number; } interface QueryUpdaterArgs { diff --git a/app/vmui/packages/vmui/src/styles/components/dynamic-number.scss b/app/vmui/packages/vmui/src/styles/components/dynamic-number.scss new file mode 100644 index 0000000000..eb7aa15c0e --- /dev/null +++ b/app/vmui/packages/vmui/src/styles/components/dynamic-number.scss @@ -0,0 +1,26 @@ +@use "src/styles/variables" as *; + +.vm-dynamic-number { + font-size: $font-size-small; + color: $color-text-disabled; + + &_positive { + color: $color-success; + } + + &_negative { + color: $color-error; + } + + &_down { + &:before { + content: "↓"; + } + } + + &_up { + &:before { + content: "↑"; + } + } +} diff --git a/app/vmui/packages/vmui/src/styles/style.scss b/app/vmui/packages/vmui/src/styles/style.scss index 4fba1b0764..52044679ab 100644 --- a/app/vmui/packages/vmui/src/styles/style.scss +++ b/app/vmui/packages/vmui/src/styles/style.scss @@ -10,6 +10,7 @@ @forward "./components/sectionheader"; @forward "./components/table"; @forward "./components/link"; +@forward "./components/dynamic-number"; :root { /* base palette */ diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 231db7e45e..e56e3cd8c1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -25,6 +25,7 @@ The following tip changes can be tested by building VictoriaMetrics components f * FEATURE: introduce `-http.maxConcurrentRequests` command-line flag to protect VM components from resource exhaustion during unexpected spikes of HTTP requests. By default, the new flag's value is set to 0 which means no limits are applied. * FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): add support for the different time formats for `--vm-native-filter-time-start` and `--vm-native-filter-time-end` flags if the native binary protocol is used for migration. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4091). * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): integrate WITH template playground. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3811). +* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add a comparison of data from the previous day with data from the current day to the `Cardinality Explorer`. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3967). * FEATURE: [vmauth](https://docs.victoriametrics.com/vmauth.html): add ability to filter incoming requests by IP. See [these docs](https://docs.victoriametrics.com/vmauth.html#ip-filters) and [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3491). * BUGFIX: reduce the probability of sudden increase in the number of small parts on systems with small number of CPU cores.