Просмотр исходного кода

fix: status logs freeze on high log inflow

Soham Parekh 3 лет назад
Родитель
Сommit
7cd6877aeb
1 измененных файлов с 78 добавлено и 21 удалено
  1. 78 21
      dashboard/src/main/home/cluster-dashboard/expanded-chart/status/Logs.tsx

+ 78 - 21
dashboard/src/main/home/cluster-dashboard/expanded-chart/status/Logs.tsx

@@ -14,7 +14,8 @@ import { NewWebsocketOptions, useWebsockets } from "shared/hooks/useWebsockets";
 import CommandLineIcon from "assets/command-line-icon";
 import CommandLineIcon from "assets/command-line-icon";
 import ConnectToLogsInstructionModal from "./ConnectToLogsInstructionModal";
 import ConnectToLogsInstructionModal from "./ConnectToLogsInstructionModal";
 
 
-const MAX_LOGS = 250;
+const MAX_LOGS = 5000;
+const LOGS_BUFFER_SIZE = 1000;
 
 
 type SelectedPodType = {
 type SelectedPodType = {
   spec: {
   spec: {
@@ -257,6 +258,7 @@ const useLogs = (
   currentPod: SelectedPodType,
   currentPod: SelectedPodType,
   scroll?: (smooth: boolean) => void
   scroll?: (smooth: boolean) => void
 ) => {
 ) => {
+  let logsBuffer: Anser.AnserJsonEntry[][] = [];
   const currentPodName = useRef<string>();
   const currentPodName = useRef<string>();
 
 
   const { currentCluster, currentProject } = useContext(Context);
   const { currentCluster, currentProject } = useContext(Context);
@@ -340,6 +342,51 @@ const useLogs = (
     } catch (error) {}
     } catch (error) {}
   };
   };
 
 
+  /**
+   * Updates the `logs` for `containerName` with `newLogs`
+   * @param containerName Name of the container
+   * @param newLogs New logs to update for
+   */
+  const updateContainerLogs = (
+    containerName: string,
+    newLogs: Anser.AnserJsonEntry[][]
+  ) => {
+    setLogs((logs) => {
+      const tmpLogs = { ...logs };
+      let containerLogs = tmpLogs[containerName] || [];
+
+      containerLogs.push(...newLogs);
+      // this is technically not as efficient as things could be
+      // if there are performance issues, a deque can be used in place of a list
+      // for storing logs
+      if (containerLogs.length > MAX_LOGS) {
+        containerLogs.shift();
+      }
+
+      if (typeof scroll === "function") {
+        scroll(true);
+      }
+      return {
+        ...logs,
+        [containerName]: containerLogs,
+      };
+    });
+  };
+
+  /**
+   * Flushes the logs buffer. If `containerName` is provided,
+   * it will update logs for the `containerName` before executing
+   * the flush operation
+   * @param containerName Name of the container
+   */
+  const flushLogsBuffer = (containerName?: string) => {
+    if (containerName) {
+      updateContainerLogs(containerName, logsBuffer);
+    }
+
+    logsBuffer = [];
+  };
+
   const setupWebsocket = (containerName: string, websocketKey: string) => {
   const setupWebsocket = (containerName: string, websocketKey: string) => {
     if (!currentPod?.metadata?.name) return;
     if (!currentPod?.metadata?.name) return;
 
 
@@ -351,25 +398,12 @@ const useLogs = (
       },
       },
       onmessage: (evt: MessageEvent) => {
       onmessage: (evt: MessageEvent) => {
         let ansiLog = Anser.ansiToJson(evt.data);
         let ansiLog = Anser.ansiToJson(evt.data);
-        setLogs((logs) => {
-          const tmpLogs = { ...logs };
-          let containerLogs = tmpLogs[containerName] || [];
-
-          containerLogs.push(ansiLog);
-          // this is technically not as efficient as things could be
-          // if there are performance issues, a deque can be used in place of a list
-          // for storing logs
-          if (containerLogs.length > MAX_LOGS) {
-            containerLogs.shift();
-          }
-          if (typeof scroll === "function") {
-            scroll(true);
-          }
-          return {
-            ...logs,
-            [containerName]: containerLogs,
-          };
-        });
+        logsBuffer.push(ansiLog);
+
+        // If size of the logs buffer is exceeded, immediately flush the buffer
+        if (logsBuffer.length > LOGS_BUFFER_SIZE) {
+          flushLogsBuffer(containerName);
+        }
       },
       },
       onclose: () => {
       onclose: () => {
         console.log("Closed websocket:", websocketKey);
         console.log("Closed websocket:", websocketKey);
@@ -384,6 +418,8 @@ const useLogs = (
     const websocketKey = `${currentPodName.current}-${currentContainer}-websocket`;
     const websocketKey = `${currentPodName.current}-${currentContainer}-websocket`;
     closeWebsocket(websocketKey);
     closeWebsocket(websocketKey);
 
 
+    // Flush and re-initialize empty buffer
+    flushLogsBuffer();
     setPrevLogs((prev) => ({ ...prev, [currentContainer]: [] }));
     setPrevLogs((prev) => ({ ...prev, [currentContainer]: [] }));
     setLogs((prev) => ({ ...prev, [currentContainer]: [] }));
     setLogs((prev) => ({ ...prev, [currentContainer]: [] }));
 
 
@@ -420,6 +456,7 @@ const useLogs = (
 
 
     closeAllWebsockets();
     closeAllWebsockets();
 
 
+    flushLogsBuffer();
     setPrevLogs({});
     setPrevLogs({});
     setLogs({});
     setLogs({});
 
 
@@ -445,6 +482,22 @@ const useLogs = (
     };
     };
   }, []);
   }, []);
 
 
+  /**
+   * In some situations, we might never hit the limit for the max buffer size.
+   * An example is if the total logs for the pod < LOGS_BUFFER_SIZE.
+   * 
+   * For handling situations like this, we would want to force a flush operation on the buffer
+   * so that we dont have any stale logs
+   */
+  useEffect(() => {
+    const flushLogsBufferInterval = setInterval(
+      () => flushLogsBuffer(currentContainer),
+      3000
+    );
+
+    return () => clearInterval(flushLogsBufferInterval);
+  }, [currentContainer]);
+
   const currentLogs = useMemo(() => {
   const currentLogs = useMemo(() => {
     return logs[currentContainer] || [];
     return logs[currentContainer] || [];
   }, [currentContainer, logs]);
   }, [currentContainer, logs]);
@@ -456,7 +509,11 @@ const useLogs = (
   return {
   return {
     containers,
     containers,
     currentContainer,
     currentContainer,
-    setCurrentContainer,
+    setCurrentContainer: (newContainer: string) => {
+      // First flush the logs of the older container
+      flushLogsBuffer(currentContainer);
+      setCurrentContainer(newContainer);
+    },
     logs: currentLogs,
     logs: currentLogs,
     previousLogs: currentPreviousLogs,
     previousLogs: currentPreviousLogs,
     refresh,
     refresh,