import { FC, PropsWithChildren, useEffect, useState } from "react"; import { IconAlertTriangle } from "@tabler/icons-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, Alert } from "@mantine/core"; import { useSuspenseQuery } from "@tanstack/react-query"; const STATUS_STARTING = "is starting up..."; const STATUS_STOPPING = "is shutting down..."; const STATUS_LOADING = "is not ready..."; const ReadinessLoader: FC = () => { const { pathPrefix, agentMode } = useSettings(); const dispatch = useAppDispatch(); // Query key is incremented every second to retrigger the status fetching. const [queryKey, setQueryKey] = useState(0); const [statusMessage, setStatusMessage] = useState(""); // Query readiness status. const { data: ready } = useSuspenseQuery({ queryKey: ["ready", queryKey], retry: false, refetchOnWindowFocus: false, gcTime: 0, queryFn: async ({ signal }: { signal: AbortSignal }) => { try { const res = await fetch(`${pathPrefix}/-/ready`, { cache: "no-store", credentials: "same-origin", signal, }); switch (res.status) { case 200: setStatusMessage(""); // Clear any status message when ready. return true; case 503: // 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); } return false; default: throw new Error(res.statusText); } } catch (error) { throw new Error("Unexpected error while fetching ready status"); } }, }); // Only call WAL replay status API if the service is starting up. const shouldQueryWALReplay = statusMessage === STATUS_STARTING; const { data: walData, isSuccess: walSuccess, } = useSuspenseAPIQuery({ path: "/status/walreplay", key: ["walreplay", queryKey], enabled: shouldQueryWALReplay, // Only enabled when service is starting up. }); useEffect(() => { if (ready) { dispatch(updateSettings({ ready: ready })); } }, [ready, dispatch]); useEffect(() => { const interval = setInterval(() => setQueryKey((v) => v + 1), 1000); return () => clearInterval(interval); }, []); return ( } maw={500} mx="auto" mt="lg" > {shouldQueryWALReplay && walSuccess && walData && ( <> Replaying WAL ({walData.data.current}/{walData.data.max}) )} ); }; export const ReadinessWrapper: FC = ({ children }) => { const { ready } = useSettings(); if (ready) { return <>{children}; } return ; }; export default ReadinessWrapper;