mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-25 05:34:05 -08:00
Add server readiness check and WAL replay progress display
Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
parent
8fae131733
commit
1aa79e29a2
|
@ -62,6 +62,7 @@ import { Notifications } from "@mantine/notifications";
|
|||
import { useAppDispatch } from "./state/hooks";
|
||||
import { updateSettings, useSettings } from "./state/settingsSlice";
|
||||
import SettingsMenu from "./components/SettingsMenu";
|
||||
import ReadinessWrapper from "./components/ReadinessWrapper";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
|
@ -379,15 +380,42 @@ function App() {
|
|||
}
|
||||
/>
|
||||
{agentMode ? (
|
||||
<Route path="/agent" element={<AgentPage />} />
|
||||
<Route
|
||||
path="/agent"
|
||||
element={
|
||||
<ReadinessWrapper>
|
||||
<AgentPage />
|
||||
</ReadinessWrapper>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Route path="/query" element={<QueryPage />} />
|
||||
<Route path="/alerts" element={<AlertsPage />} />
|
||||
<Route
|
||||
path="/query"
|
||||
element={
|
||||
<ReadinessWrapper>
|
||||
<QueryPage />
|
||||
</ReadinessWrapper>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/alerts"
|
||||
element={
|
||||
<ReadinessWrapper>
|
||||
<AlertsPage />
|
||||
</ReadinessWrapper>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{allStatusPages.map((p) => (
|
||||
<Route key={p.path} path={p.path} element={p.element} />
|
||||
<Route
|
||||
key={p.path}
|
||||
path={p.path}
|
||||
element={
|
||||
<ReadinessWrapper>{p.element}</ReadinessWrapper>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Routes>
|
||||
</Suspense>
|
||||
|
|
7
web/ui/mantine-ui/src/api/responseTypes/walreplay.ts
Normal file
7
web/ui/mantine-ui/src/api/responseTypes/walreplay.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
// Result type for /api/v1/status/walreplay endpoint.
|
||||
// See: https://prometheus.io/docs/prometheus/latest/querying/api/#wal-replay-stats
|
||||
export interface WALReplayStatus {
|
||||
min: number;
|
||||
max: number;
|
||||
current: number;
|
||||
}
|
92
web/ui/mantine-ui/src/components/ReadinessWrapper.tsx
Normal file
92
web/ui/mantine-ui/src/components/ReadinessWrapper.tsx
Normal file
|
@ -0,0 +1,92 @@
|
|||
import { FC, PropsWithChildren, useEffect, useState } from "react";
|
||||
import { useAppDispatch } from "../state/hooks";
|
||||
import { updateSettings, useSettings } from "../state/settingsSlice";
|
||||
import { useSuspenseAPIQuery } from "../api/api";
|
||||
import { WALReplayStatus } from "../api/responseTypes/walreplay";
|
||||
import { Progress, Stack, Title } from "@mantine/core";
|
||||
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||
|
||||
const ReadinessLoader: FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
// Query key is incremented every second to retrigger the status fetching.
|
||||
const [queryKey, setQueryKey] = useState(0);
|
||||
|
||||
// Query readiness status.
|
||||
const { data: ready } = useSuspenseQuery<boolean>({
|
||||
queryKey: [`ready-${queryKey}`],
|
||||
retry: false,
|
||||
refetchOnWindowFocus: false,
|
||||
gcTime: 0,
|
||||
queryFn: async ({ signal }: { signal: AbortSignal }) => {
|
||||
try {
|
||||
const res = await fetch("/-/ready", {
|
||||
cache: "no-store",
|
||||
credentials: "same-origin",
|
||||
signal,
|
||||
});
|
||||
switch (res.status) {
|
||||
case 200:
|
||||
return true;
|
||||
case 503:
|
||||
return false;
|
||||
default:
|
||||
throw new Error(res.statusText);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error("Unexpected error while fetching ready status");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Query WAL replay status.
|
||||
const {
|
||||
data: {
|
||||
data: { min, max, current },
|
||||
},
|
||||
} = useSuspenseAPIQuery<WALReplayStatus>({
|
||||
path: `/status/walreplay`,
|
||||
key: `walreplay-${queryKey}`,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (ready) {
|
||||
dispatch(updateSettings({ ready: ready }));
|
||||
}
|
||||
}, [ready, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => setQueryKey((v) => v + 1), 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Stack gap="lg" maw={1000} mx="auto" mt="xs">
|
||||
<Title order={2}>Starting up...</Title>
|
||||
{max > 0 && (
|
||||
<>
|
||||
<p>
|
||||
Replaying WAL ({current}/{max})
|
||||
</p>
|
||||
<Progress
|
||||
size="xl"
|
||||
animated
|
||||
value={((current - min + 1) / (max - min + 1)) * 100}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export const ReadinessWrapper: FC<PropsWithChildren> = ({ children }) => {
|
||||
const { ready } = useSettings();
|
||||
|
||||
if (ready) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
return <ReadinessLoader />;
|
||||
};
|
||||
|
||||
export default ReadinessWrapper;
|
|
@ -9,6 +9,9 @@ export default defineConfig({
|
|||
"/api": {
|
||||
target: "http://localhost:9090",
|
||||
},
|
||||
"/-/": {
|
||||
target: "http://localhost:9090",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue