2024-07-15 13:21:14 -07:00
|
|
|
import { FC, PropsWithChildren, useEffect, useState } from "react";
|
2024-09-16 08:17:50 -07:00
|
|
|
import { IconAlertTriangle } from "@tabler/icons-react";
|
2024-07-15 13:21:14 -07:00
|
|
|
import { useAppDispatch } from "../state/hooks";
|
|
|
|
import { updateSettings, useSettings } from "../state/settingsSlice";
|
|
|
|
import { useSuspenseAPIQuery } from "../api/api";
|
|
|
|
import { WALReplayStatus } from "../api/responseTypes/walreplay";
|
2024-10-04 04:54:03 -07:00
|
|
|
import { Progress, Alert, Stack } from "@mantine/core";
|
2024-07-15 13:21:14 -07:00
|
|
|
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
|
|
|
2024-09-16 08:17:50 -07:00
|
|
|
const STATUS_STARTING = "is starting up...";
|
|
|
|
const STATUS_STOPPING = "is shutting down...";
|
|
|
|
const STATUS_LOADING = "is not ready...";
|
|
|
|
|
2024-07-15 13:21:14 -07:00
|
|
|
const ReadinessLoader: FC = () => {
|
2024-09-16 08:17:50 -07:00
|
|
|
const { pathPrefix, agentMode } = useSettings();
|
2024-07-15 13:21:14 -07:00
|
|
|
const dispatch = useAppDispatch();
|
|
|
|
|
|
|
|
// Query key is incremented every second to retrigger the status fetching.
|
|
|
|
const [queryKey, setQueryKey] = useState(0);
|
2024-09-16 08:17:50 -07:00
|
|
|
const [statusMessage, setStatusMessage] = useState("");
|
2024-07-15 13:21:14 -07:00
|
|
|
|
|
|
|
// Query readiness status.
|
|
|
|
const { data: ready } = useSuspenseQuery<boolean>({
|
2024-09-03 07:05:05 -07:00
|
|
|
queryKey: ["ready", queryKey],
|
2024-07-15 13:21:14 -07:00
|
|
|
retry: false,
|
|
|
|
refetchOnWindowFocus: false,
|
|
|
|
gcTime: 0,
|
|
|
|
queryFn: async ({ signal }: { signal: AbortSignal }) => {
|
|
|
|
try {
|
2024-08-08 08:01:40 -07:00
|
|
|
const res = await fetch(`${pathPrefix}/-/ready`, {
|
2024-07-15 13:21:14 -07:00
|
|
|
cache: "no-store",
|
|
|
|
credentials: "same-origin",
|
|
|
|
signal,
|
|
|
|
});
|
|
|
|
switch (res.status) {
|
|
|
|
case 200:
|
2024-09-16 08:17:50 -07:00
|
|
|
setStatusMessage(""); // Clear any status message when ready.
|
2024-07-15 13:21:14 -07:00
|
|
|
return true;
|
|
|
|
case 503:
|
2024-09-16 08:17:50 -07:00
|
|
|
// Check the custom header `X-Prometheus-Stopping` for stopping information.
|
|
|
|
if (res.headers.get("X-Prometheus-Stopping") === "true") {
|
|
|
|
setStatusMessage(STATUS_STOPPING);
|
|
|
|
} else {
|
|
|
|
setStatusMessage(STATUS_STARTING);
|
|
|
|
}
|
|
|
|
|
2024-07-15 13:21:14 -07:00
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
throw new Error(res.statusText);
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
throw new Error("Unexpected error while fetching ready status");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2024-09-16 08:17:50 -07:00
|
|
|
// Only call WAL replay status API if the service is starting up.
|
|
|
|
const shouldQueryWALReplay = statusMessage === STATUS_STARTING;
|
|
|
|
|
2024-10-04 04:54:03 -07:00
|
|
|
const { data: walData, isSuccess: walSuccess } =
|
|
|
|
useSuspenseAPIQuery<WALReplayStatus>({
|
|
|
|
path: "/status/walreplay",
|
|
|
|
key: ["walreplay", queryKey],
|
|
|
|
enabled: shouldQueryWALReplay, // Only enabled when service is starting up.
|
|
|
|
});
|
2024-07-15 13:21:14 -07:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (ready) {
|
|
|
|
dispatch(updateSettings({ ready: ready }));
|
|
|
|
}
|
|
|
|
}, [ready, dispatch]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
const interval = setInterval(() => setQueryKey((v) => v + 1), 1000);
|
|
|
|
return () => clearInterval(interval);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
return (
|
2024-09-16 08:17:50 -07:00
|
|
|
<Alert
|
|
|
|
color="yellow"
|
2024-10-04 04:54:03 -07:00
|
|
|
title={
|
|
|
|
"Prometheus " +
|
|
|
|
((agentMode && "Agent ") || "") +
|
|
|
|
(statusMessage || STATUS_LOADING)
|
|
|
|
}
|
|
|
|
icon={<IconAlertTriangle />}
|
2024-09-16 08:17:50 -07:00
|
|
|
maw={500}
|
|
|
|
mx="auto"
|
|
|
|
mt="lg"
|
|
|
|
>
|
|
|
|
{shouldQueryWALReplay && walSuccess && walData && (
|
2024-10-04 04:54:03 -07:00
|
|
|
<Stack>
|
2024-09-16 08:17:50 -07:00
|
|
|
<strong>
|
|
|
|
Replaying WAL ({walData.data.current}/{walData.data.max})
|
|
|
|
</strong>
|
2024-07-15 13:21:14 -07:00
|
|
|
<Progress
|
|
|
|
size="xl"
|
|
|
|
animated
|
2024-09-16 08:17:50 -07:00
|
|
|
color="yellow"
|
2024-10-04 04:54:03 -07:00
|
|
|
value={
|
|
|
|
((walData.data.current - walData.data.min + 1) /
|
|
|
|
(walData.data.max - walData.data.min + 1)) *
|
|
|
|
100
|
|
|
|
}
|
2024-07-15 13:21:14 -07:00
|
|
|
/>
|
2024-10-04 04:54:03 -07:00
|
|
|
</Stack>
|
2024-07-15 13:21:14 -07:00
|
|
|
)}
|
2024-09-16 08:17:50 -07:00
|
|
|
</Alert>
|
2024-07-15 13:21:14 -07:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export const ReadinessWrapper: FC<PropsWithChildren> = ({ children }) => {
|
|
|
|
const { ready } = useSettings();
|
|
|
|
|
|
|
|
if (ready) {
|
|
|
|
return <>{children}</>;
|
|
|
|
}
|
|
|
|
|
|
|
|
return <ReadinessLoader />;
|
|
|
|
};
|
|
|
|
|
|
|
|
export default ReadinessWrapper;
|