diff --git a/app/vmui/packages/vmui/src/components/Main/Icons/index.tsx b/app/vmui/packages/vmui/src/components/Main/Icons/index.tsx index 0340a4ffd..5b0deb156 100644 --- a/app/vmui/packages/vmui/src/components/Main/Icons/index.tsx +++ b/app/vmui/packages/vmui/src/components/Main/Icons/index.tsx @@ -553,3 +553,20 @@ export const SearchIcon = () => ( > ); + +export const SpinnerIcon = () => ( + + + + + +); diff --git a/app/vmui/packages/vmui/src/components/Main/LineLoader/LineLoader.tsx b/app/vmui/packages/vmui/src/components/Main/LineLoader/LineLoader.tsx new file mode 100644 index 000000000..97bb5efa8 --- /dev/null +++ b/app/vmui/packages/vmui/src/components/Main/LineLoader/LineLoader.tsx @@ -0,0 +1,13 @@ +import React, { FC } from "preact/compat"; +import "./style.scss"; + +const LineLoader: FC = () => { + return ( +
+
+
+
+ ); +}; + +export default LineLoader; diff --git a/app/vmui/packages/vmui/src/components/Main/LineLoader/style.scss b/app/vmui/packages/vmui/src/components/Main/LineLoader/style.scss new file mode 100644 index 000000000..2c17a351d --- /dev/null +++ b/app/vmui/packages/vmui/src/components/Main/LineLoader/style.scss @@ -0,0 +1,39 @@ +@use "src/styles/variables" as *; + +.vm-line-loader { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + z-index: 2; + overflow: hidden; + + &__background { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + background-color: $color-text; + opacity: 0.1; + } + + &__line { + position: absolute; + width: 10%; + height: 100%; + background-color: $color-primary; + animation: slide 2s infinite linear; + opacity: 0.8; + } +} + +@keyframes slide { + 0% { + left: 0; + } + 100% { + left: 100%; + } +} diff --git a/app/vmui/packages/vmui/src/hooks/useFetchQuery.ts b/app/vmui/packages/vmui/src/hooks/useFetchQuery.ts index 7aba2dc0a..f23d5ba03 100644 --- a/app/vmui/packages/vmui/src/hooks/useFetchQuery.ts +++ b/app/vmui/packages/vmui/src/hooks/useFetchQuery.ts @@ -34,7 +34,8 @@ interface FetchQueryReturn { queryStats: QueryStats[], warning?: string, traces?: Trace[], - isHistogram: boolean + isHistogram: boolean, + abortFetch: () => void } interface FetchDataParams { @@ -160,6 +161,7 @@ export const useFetchQuery = ({ const error = e as Error; if (error.name === "AbortError") { // Aborts are expected, don't show an error for them. + setIsLoading(false); return; } const helperText = "Please check your serverURL settings and confirm server availability."; @@ -197,6 +199,13 @@ export const useFetchQuery = ({ }, [serverUrl, period, displayType, customStep, hideQuery]); + const abortFetch = useCallback(() => { + fetchQueue.map(f => f.abort()); + setFetchQueue([]); + setGraphData([]); + setLiveData([]); + }, [fetchQueue]); + const [prevUrl, setPrevUrl] = useState([]); useEffect(() => { @@ -238,6 +247,7 @@ export const useFetchQuery = ({ queryStats, warning, traces, - isHistogram + isHistogram, + abortFetch, }; }; diff --git a/app/vmui/packages/vmui/src/pages/CustomPanel/QueryConfigurator/QueryConfigurator.tsx b/app/vmui/packages/vmui/src/pages/CustomPanel/QueryConfigurator/QueryConfigurator.tsx index 1a8c8e8ad..c321aff10 100644 --- a/app/vmui/packages/vmui/src/pages/CustomPanel/QueryConfigurator/QueryConfigurator.tsx +++ b/app/vmui/packages/vmui/src/pages/CustomPanel/QueryConfigurator/QueryConfigurator.tsx @@ -10,6 +10,7 @@ import { PlayIcon, PlusIcon, Prettify, + SpinnerIcon, VisibilityIcon, VisibilityOffIcon } from "../../../components/Main/Icons"; @@ -30,8 +31,10 @@ export interface QueryConfiguratorProps { setQueryErrors: Dispatch>; setHideError: Dispatch>; stats: QueryStats[]; + isLoading?: boolean; onHideQuery?: (queries: number[]) => void onRunQuery: () => void; + abortFetch?: () => void; hideButtons?: { addQuery?: boolean; prettify?: boolean; @@ -46,8 +49,10 @@ const QueryConfigurator: FC = ({ setQueryErrors, setHideError, stats, + isLoading, onHideQuery, onRunQuery, + abortFetch, hideButtons }) => { @@ -84,6 +89,10 @@ const QueryConfigurator: FC = ({ }; const handleRunQuery = () => { + if (isLoading) { + abortFetch && abortFetch(); + return; + } updateHistory(); queryDispatch({ type: "SET_QUERY", payload: stateQuery }); timeDispatch({ type: "RUN_QUERY" }); @@ -271,9 +280,9 @@ const QueryConfigurator: FC = ({ diff --git a/app/vmui/packages/vmui/src/pages/CustomPanel/index.tsx b/app/vmui/packages/vmui/src/pages/CustomPanel/index.tsx index cf56a8c40..0f04ab033 100644 --- a/app/vmui/packages/vmui/src/pages/CustomPanel/index.tsx +++ b/app/vmui/packages/vmui/src/pages/CustomPanel/index.tsx @@ -3,7 +3,7 @@ import QueryConfigurator from "./QueryConfigurator/QueryConfigurator"; import { useFetchQuery } from "../../hooks/useFetchQuery"; import { DisplayTypeSwitch } from "./DisplayTypeSwitch"; import { useGraphDispatch, useGraphState } from "../../state/graph/GraphStateContext"; -import Spinner from "../../components/Main/Spinner/Spinner"; +import LineLoader from "../../components/Main/LineLoader/LineLoader"; import { useCustomPanelState } from "../../state/customPanel/CustomPanelStateContext"; import { useQueryState } from "../../state/query/QueryStateContext"; import { useSetQueryParams } from "./hooks/useSetQueryParams"; @@ -45,7 +45,8 @@ const CustomPanel: FC = () => { queryStats, warning, traces, - isHistogram + isHistogram, + abortFetch, } = useFetchQuery({ visible: true, customStep, @@ -80,14 +81,15 @@ const CustomPanel: FC = () => { setQueryErrors={setQueryErrors} setHideError={setHideError} stats={queryStats} + isLoading={isLoading} onHideQuery={handleHideQuery} onRunQuery={handleRunQuery} + abortFetch={abortFetch} /> - {isLoading && } {showError && {error}} {showInstantQueryTip && } {warning && ( @@ -105,6 +107,7 @@ const CustomPanel: FC = () => { "vm-block_mobile": isMobile, })} > + {isLoading && }
{ const [period, setPeriod] = useState(periodState); const [queryError, setQueryError] = useState(""); - const { logs, isLoading, error, fetchLogs } = useFetchLogs(serverUrl, query, limit); + const { logs, isLoading, error, fetchLogs, abortController } = useFetchLogs(serverUrl, query, limit); const { fetchLogHits, ...dataLogHits } = useFetchLogHits(serverUrl, query); const getPeriod = useCallback(() => { @@ -70,10 +69,15 @@ const ExploreLogs: FC = () => { setQuery(prev => `_stream: ${val === "other" ? "{}" : val} AND (${prev})`); }; - const handleUpdateQuery = () => { - setQuery(tmpQuery); - handleRunQuery(); - }; + const handleUpdateQuery = useCallback(() => { + if (isLoading || dataLogHits.isLoading) { + abortController.abort && abortController.abort(); + dataLogHits.abortController.abort && dataLogHits.abortController.abort(); + } else { + setQuery(tmpQuery); + handleRunQuery(); + } + }, [isLoading, dataLogHits.isLoading]); useEffect(() => { if (query) handleRunQuery(); @@ -93,8 +97,8 @@ const ExploreLogs: FC = () => { onChange={setTmpQuery} onChangeLimit={handleChangeLimit} onRun={handleUpdateQuery} + isLoading={isLoading || dataLogHits.isLoading} /> - {isLoading && } {error && {error}} {!error && ( { query={query} period={period} onApplyFilter={handleApplyFilter} - isLoading={isLoading ? false : dataLogHits.isLoading} /> )} - +
); }; diff --git a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBarChart/ExploreLogsBarChart.tsx b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBarChart/ExploreLogsBarChart.tsx index 40b2cae0f..506cf418d 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBarChart/ExploreLogsBarChart.tsx +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBarChart/ExploreLogsBarChart.tsx @@ -9,7 +9,7 @@ import { AlignedData } from "uplot"; import BarHitsChart from "../../../components/Chart/BarHitsChart/BarHitsChart"; import Alert from "../../../components/Main/Alert/Alert"; import { TimeParams } from "../../../types"; -import Spinner from "../../../components/Main/Spinner/Spinner"; +import LineLoader from "../../../components/Main/LineLoader/LineLoader"; interface Props { query: string; @@ -72,10 +72,7 @@ const ExploreLogsBarChart: FC = ({ logHits, period, error, isLoading, onA "vm-block_mobile": isMobile, })} > - {isLoading && } + {isLoading && } {!error && noDataMessage && (
{noDataMessage} diff --git a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBody/ExploreLogsBody.tsx b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBody/ExploreLogsBody.tsx index 4c44dc37d..da5584197 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBody/ExploreLogsBody.tsx +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsBody/ExploreLogsBody.tsx @@ -16,9 +16,11 @@ import TableLogs from "./TableLogs"; import GroupLogs from "../GroupLogs/GroupLogs"; import { DATE_TIME_FORMAT } from "../../../constants/date"; import { marked } from "marked"; +import LineLoader from "../../../components/Main/LineLoader/LineLoader"; export interface ExploreLogBodyProps { data: Logs[]; + isLoading: boolean; } enum DisplayType { @@ -33,7 +35,7 @@ const tabs = [ { label: "JSON", value: DisplayType.json, icon: }, ]; -const ExploreLogsBody: FC = ({ data }) => { +const ExploreLogsBody: FC = ({ data, isLoading }) => { const { isMobile } = useDeviceDetect(); const { timezone } = useTimeState(); const { setSearchParamsFromKeys } = useSearchParamsFromObject(); @@ -75,6 +77,7 @@ const ExploreLogsBody: FC = ({ data }) => { "vm-block_mobile": isMobile, })} > + {isLoading && }
void; onChangeLimit: (val: number) => void; onRun: () => void; @@ -20,6 +21,7 @@ const ExploreLogsHeader: FC = ({ query, limit, error, + isLoading, onChange, onChangeLimit, onRun, @@ -94,13 +96,16 @@ const ExploreLogsHeader: FC = ({ Documentation
-
+
diff --git a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsHeader/style.scss b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsHeader/style.scss index 1bc7ab0b6..f2e0dbc10 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsHeader/style.scss +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/ExploreLogsHeader/style.scss @@ -29,8 +29,18 @@ flex-grow: 1; } - &__execute { + &-execute { + position: relative; display: grid; + + &__text { + position: absolute; + + &_hidden { + position: relative; + visibility: hidden; + } + } } &-helpful { diff --git a/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogHits.ts b/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogHits.ts index b9953f7ad..af784db47 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogHits.ts +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogHits.ts @@ -118,5 +118,6 @@ export const useFetchLogHits = (server: string, query: string) => { isLoading: Object.values(isLoading).some(s => s), error, fetchLogHits, + abortController: abortControllerRef.current }; }; diff --git a/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogs.ts b/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogs.ts index e789f8e6a..36dd4a888 100644 --- a/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogs.ts +++ b/app/vmui/packages/vmui/src/pages/ExploreLogs/hooks/useFetchLogs.ts @@ -81,5 +81,6 @@ export const useFetchLogs = (server: string, query: string, limit: number) => { isLoading: Object.values(isLoading).some(s => s), error, fetchLogs, + abortController: abortControllerRef.current }; }; diff --git a/docs/VictoriaLogs/CHANGELOG.md b/docs/VictoriaLogs/CHANGELOG.md index 7cb34f221..f0a66a5bc 100644 --- a/docs/VictoriaLogs/CHANGELOG.md +++ b/docs/VictoriaLogs/CHANGELOG.md @@ -17,6 +17,7 @@ according to [these docs](https://docs.victoriametrics.com/victorialogs/quicksta * FEATURE: add support for forced merge. See [these docs](https://docs.victoriametrics.com/victorialogs/#forced-merge). * FEATURE: skip empty log fields in query results, since they are treated as non-existing fields in [VictoriaLogs data model](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model). +* FEATURE: [web UI](https://docs.victoriametrics.com/victorialogs/querying/#web-ui): add the ability to cancel running queries. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7097). * BUGFIX: avoid possible panic when logs for a new day are ingested during execution of concurrent queries. * BUGFIX: avoid panic at `lib/logstorage.(*blockResultColumn).forEachDictValue()` when [stats with additional filters](https://docs.victoriametrics.com/victorialogs/logsql/#stats-with-additional-filters). The panic has been introduced in [v0.33.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v0.33.0-victorialogs) in [this commit](https://github.com/VictoriaMetrics/VictoriaMetrics/commit/a350be48b68330ee1a487e1fb09b002d3be45163). diff --git a/docs/changelog/CHANGELOG.md b/docs/changelog/CHANGELOG.md index 4670432dc..74f5d0273 100644 --- a/docs/changelog/CHANGELOG.md +++ b/docs/changelog/CHANGELOG.md @@ -24,6 +24,7 @@ See also [LTS releases](https://docs.victoriametrics.com/lts-releases/). * FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent/) and [Single-node VictoriaMetrics](https://docs.victoriametrics.com/): add support of [exponential histograms](https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponentialhistogram) ingested via [OpenTelemetry protocol for metrics](https://docs.victoriametrics.com/#sending-data-via-opentelemetry). Such histograms will be automatically converted to [VictoriaMetrics histogram format](https://valyala.medium.com/improving-histogram-usability-for-prometheus-and-grafana-bc7e5df0e350). See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/6354). * FEATURE: [vmsingle](https://docs.victoriametrics.com/single-server-victoriametrics/), `vminsert` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/cluster-victoriametrics/) and [vmagent](https://docs.victoriametrics.com/vmagent/): disable stream processing mode for data [ingested via InfluxDB](https://docs.victoriametrics.com/#how-to-send-data-from-influxdb-compatible-agents-such-as-telegraf) HTTP endpoints by default. With this change, the data is processed in batches (see `-influx.maxRequestSize`) and user will get parsing errors immediately as they happen. This also improves users' experience and resiliency against thundering herd problems caused by clients without backoff policies like telegraf. To enable stream mode back, pass HTTP header `Stream-Mode: "1"` with each request. For data sent via TCP and UDP (see `-influxListenAddr`) protocols streaming processing remains enabled. * FEATURE: [vmgateway](https://docs.victoriametrics.com/vmgateway/): allow parsing `scope` claim parsing in array format. This is useful for cases when identity provider does encode claims in array format. +* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add the ability to cancel running queries. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7097). * BUGFIX: [vmgateway](https://docs.victoriametrics.com/vmgateway/): fix possible panic during parsing of a token without `vm_access` claim. This issue was introduced in v1.104.0.