Uploaded image for project: 'Observability UI'
  1. Observability UI
  2. OU-1122

[Incidents] useEffect re-triggers groupAlertsForTable on every rules polling cycle

XMLWordPrintable

    • None
    • None
    • None
    • None
    • None
    • None

      Description

      The useEffect hook responsible for processing alerts for the table re-triggers approximately every 15 seconds due to rules polling. This occurs even when neither alertsData nor rules content has actually changed, resulting in wasteful computation.

      Root Cause

      The useAlerts hook polls for alerting rules every 15 seconds. It returns a new array reference on every poll, regardless of whether the data content has changed.

      Because the useEffect depends on [alertsData, rules], the referential change in rules forces the effect to fire repeatedly.

      useEffect(() => {
        if (rules && alertsData) {
          // This runs every 15s because 'rules' ref changes
          dispatch(setAlertsTableData({
            alertsTableData: groupAlertsForTable(alertsData, rules),
          }));
        }
      }, [alertsData, rules]);
      

      Observed Behavior

      • Frequency: Triggers every ~15 seconds.
      • Volume: After ~1 hour on the Incidents page, the effect triggered 237 times.
      • Logs: Debugging shows: alertsDataRefChanged: false, rulesRefChanged: true, but rulesLengthSame: true.

      Impact

      • Severity: Low-Moderate.
      • Consequence: Causes unnecessary execution of groupAlertsForTable (re-processing) while the user is viewing the page. While not a crash, it is inefficient resource usage.

      Proposed Solution

      Implement a data fingerprint check (using useRef) to validate if the data content (specifically length) has changed before processing. If only the reference has changed, skip the execution.

      Suggested Implementation:

      const prevRef = useRef({ alertsLength: 0, rulesLength: 0 });
      
      useEffect(() => {
        if (rules && alertsData) {
          // Create current fingerprint
          const fingerprint = { alertsLength: alertsData.length, rulesLength: rules.length };
      
          // Compare with previous fingerprint
          if (fingerprint.alertsLength === prevRef.current.alertsLength &&
              fingerprint.rulesLength === prevRef.current.rulesLength) {
            return; // Skip — only reference changed, data is effectively same
          }
      
          // Update ref and process
          prevRef.current = fingerprint;
          dispatch(setAlertsTableData({ alertsTableData: groupAlertsForTable(alertsData, rules) }));
        }
      }, [alertsData, rules]);
      

       Affected Files

      • web/src/components/Incidents/IncidentsPage.tsx

              drajnoha@redhat.com David Rajnoha
              drajnoha@redhat.com David Rajnoha
              None
              None
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

                Created:
                Updated: