Bug description

Dashboard gets stuck on the Loader logo of Superset when embedded into Angular application using Public role.

How to reproduce the bug

  1. Load 4-5 dashboards on a single page, create 4-5 such pages.
  2. Then try switching between the dashboards and pages.
  3. After browsing for some time, on any one random dashboard, issue occurs where the loader does not disappear.

Even if the page has two dashboards, the issue appears after a few navigations. If the user scrolls to the next dashboard, the next dashboard loads. When the user goes back to the same dashboard where the loader was stuck forever, the dashboard loads correctly. Alternatively, if the user refreshes the dashboard, it loads. It looks like the dashboard rendering gets stuck due to some reason within the embedded iframe.

Screenshots/recordings

https://github.com/user-attachments/assets/36cb0b78-4328-4255-8de4-b1b9d03a719c

What we have already tried:

  1. We preloaded couple of dashboards in the background to reduce the download overhead. The actual dashboards load all the static contents from memory. (Ex. JS, CSS, Images Etc. common across multiple dashboards)
  2. We have minimised all the other API calls from the Angular components to leave all the resources free for Superset to use. Basically, apart from visible dashboard's API calls, there are no other API calls which are executed at the time of loading that specific dashboard.
  3. We are loading the dashboards dynamically so that they do not use too many resources. i.e. based on the scroll position.
  4. The dashboards are loaded based on the scroll position.

Operating system

Ubuntu 22.04 LTS

Superset version

4.0.1

Python version

3.9

Node version

16

Browser

Chrome

Additional context

It is our understanding that the issue could be present in the rendering mechanism of the dashboard and how the rendering is handled between multiple API calls which are required for the dashboard to load.

Checklist

  • [X] I have searched Superset docs and Slack and didn't find a solution to my problem.
  • [X] I have searched the GitHub issue tracker and didn't find a similar bug report.
  • [X] I have checked Superset's logs for errors and if I found a relevant Python stacktrace, I included it here as text in the "additional context" section.

Comment From: dosubot[bot]

Hey @Prachil-MI, I'm here to help with any bugs, questions, or contributions you have for Superset! Let's tackle this issue together and get it sorted out. 🤖

I found a similar solved discussion and several closed issues that might help you resolve the problem where the Superset dashboard gets stuck on the Loader logo when embedded into an Angular application using the Public role.

  1. Similar Discussion:
  2. Embedded Dashboards don't load consistently (last updated on April 16, 2024). The issue was related to the load balancer on AWS and not Superset itself [1].

  3. Closed Issues:

  4. Dashboard shows endless loading (last updated on June 04, 2024). The issue was related to the "Select first filter item" feature. Removing this feature fixed the problem temporarily [2].
  5. Dashboard embedded issue in standalone mode with 2.1.0 (last updated on March 06, 2024) [3].
  6. Error fullscreen embedded dashboard (last updated on January 11, 2024). This issue was closed by a pull request that hid the fullscreen option for embedded contexts [4].

To resolve the issue, ensure that the guest token is being fetched and sent correctly to the iframe. Here is a code snippet that shows how to embed a Superset dashboard and handle guest tokens:

const [guestToken, ourPort]: [string, Switchboard] = await Promise.all([
  fetchGuestToken(),
  mountIframe(),
]);

ourPort.emit('guestToken', { guestToken });
log('sent guest token');

async function refreshGuestToken() {
  const newGuestToken = await fetchGuestToken();
  ourPort.emit('guestToken', { guestToken: newGuestToken });
  setTimeout(refreshGuestToken, getGuestTokenRefreshTiming(newGuestToken));
}

setTimeout(refreshGuestToken, getGuestTokenRefreshTiming(guestToken));

Ensure that: 1. The fetchGuestToken function is correctly implemented and returns a valid guest token. 2. The guest token is being emitted to the iframe using ourPort.emit('guestToken', { guestToken }).

Additionally, ensure that the embedded page is correctly set up to handle the guest token. The following code from superset-frontend/src/embedded/index.tsx shows how the guest token is processed:

function setupGuestClient(guestToken: string) {
  setupClient({
    guestToken,
    guestTokenHeaderName: bootstrapData.config?.GUEST_TOKEN_HEADER_NAME,
    unauthorizedHandler: guestUnauthorizedHandler,
  });
}

window.addEventListener('message', function embeddedPageInitializer(event) {
  try {
    validateMessageEvent(event);
  } catch (err) {
    log('ignoring message unrelated to embedded comms', err, event);
    return;
  }

  const port = event.ports?.[0];
  if (event.data.handshake === 'port transfer' && port) {
    log('message port received', event);

    Switchboard.init({
      port,
      name: 'superset',
      debug: debugMode,
    });

    let started = false;

    Switchboard.defineMethod(
      'guestToken',
      ({ guestToken }: { guestToken: string }) => {
        setupGuestClient(guestToken);
        if (!started) {
          start();
          started = true;
        }
      },
    );

    Switchboard.start();
  }
});

You can also use the Superset Embedded SDK to simplify the embedding process:

import { embedDashboard } from "@superset-ui/embedded-sdk";

embedDashboard({
  id: "abc123", // given by the Superset embedding UI
  supersetDomain: "https://superset.example.com",
  mountPoint: document.getElementById("my-superset-container"), // any html element that can contain an iframe
  fetchGuestToken: () => fetchGuestTokenFromBackend(),
  dashboardUiConfig: { // dashboard UI config: hideTitle, hideTab, hideChartControls, filters.visible, filters.expanded (optional), urlParams (optional)
      hideTitle: true,
      filters: {
          expanded: true,
      },
      urlParams: {
          foo: 'value1',
          bar: 'value2',
          // ...
      }
  },
  // optional additional iframe sandbox attributes
  iframeSandboxExtras: ['allow-top-navigation', 'allow-popups-to-escape-sandbox']
});

Ensure that your backend correctly creates and provides the guest token by making a POST request to /security/guest_token with the necessary parameters [5][6][7].

To continue talking to Dosu, mention @dosu.

Comment From: Prachil-MI

@dosu We have already used the Embedded SDK with the exact given code. The issue still seems to persist in Superset version 4.0.1. The issues mentioned are already resolved and are related to one of the filter option, which is a different case.

Comment From: dosubot[bot]

The issue where the Superset dashboard gets stuck on the Loader logo when embedded into an Angular application using the Public role, even when using the Embedded SDK with Superset version 4.0.1, could be due to several factors. Here are some specific conditions and configurations to check:

  1. Invalid supersetDomain: Ensure that the supersetDomain does not end with a trailing slash and is a valid URL format.

  2. Failure to Fetch Guest Token: Verify that the fetchGuestToken function is correctly implemented and returns a valid guest token. If the guest token is invalid or not fetched correctly, the dashboard will not load properly.

  3. Incorrect mountPoint: Ensure that the mountPoint HTML element is correctly specified and exists in the DOM.

  4. Misconfigured dashboardUiConfig: Check that the dashboardUiConfig contains valid configurations and parameters.

  5. Iframe Sandbox Configuration: Ensure that the iframeSandboxExtras contains valid sandbox attributes.

  6. Message Channel Initialization: Verify that the message channel between the host window and the iframe initializes correctly.

  7. Unauthorized Embedding: Ensure that the page is embedded in an origin authorized by the curator.

Additionally, specific error messages or logs generated by Superset can help diagnose the issue. Look for errors such as DashboardInvalidError, DashboardNotFoundError, DashboardAccessDeniedError, and others in the Superset logs.

Here is a detailed code snippet for embedding a dashboard and handling guest tokens:

import { embedDashboard } from "@superset-ui/embedded-sdk";

const fetchGuestToken = async () => {
  const response = await fetch('https://your-backend-api.com/get-guest-token', {
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
    },
  });

  if (!response.ok) {
    throw new Error('Failed to fetch guest token');
  }

  const data = await response.json();
  return data.guestToken;
};

embedDashboard({
  id: 'your-dashboard-id',
  supersetDomain: 'https://superset.example.com',
  mountPoint: document.getElementById('your-mount-point'),
  fetchGuestToken: fetchGuestToken,
  dashboardUiConfig: {
    hideTitle: true,
    filters: {
      expanded: true,
    },
    urlParams: {
      foo: 'value1',
      bar: 'value2',
    }
  },
  iframeSandboxExtras: ['allow-top-navigation', 'allow-popups-to-escape-sandbox']
});

Ensure that your backend correctly creates and provides the guest token by making a POST request to /security/guest_token with the necessary parameters [1][2][3].

If the issue persists, it might be helpful to look into potential performance issues or limitations when embedding multiple dashboards on a single page, as this could also contribute to the problem.

This is a tough nut to crack, could really use your help on this, @rusackas.

To continue talking to Dosu, mention @dosu.

Comment From: rusackas

We are loading the dashboards dynamically so that they do not use too many resources. i.e. based on the scroll position.

Does it work when you don't get tricky with it like that? Superset dashboards already try to load what's in the viewport on their own, so you might be causing issues over-optimizing.

Does the dashboard itself behave as expected in Superset itself (non embedded)? This also looks like what happens when the DB is being the bottleneck, but if you're messing with the dashboard rendering process, I'd suspect that first.

Also, make sure your Angular app isn't doing something strange like re-rendeing the iframe repeatedly on scroll or some other event.

Comment From: Prachil-MI

Thank you @rusackas for your reply and help. Here are the answers for the questions that you asked -

Does it work when you don't get tricky with it like that?

It does not. We have at least 5 dashboards and at most 16 dashboards on single component / page. Rest of the contents are static. When we do not apply the optimisation, whenever the first dashboard appears in viewport, all dashboards start loading simultaneously. At the same time, for each dashboard, the assets like JS and CSS starts loading and the overall loading process becomes so slow that one dashboard takes 15-20+ seconds to load. Once the first dashboard is loaded, rest of the dashboard takes 4-8 seconds to load.

Does the dashboard itself behave as expected in Superset itself (non embedded)?

No. It loads in couple of seconds.

This also looks like what happens when the DB is being the bottleneck, but if you're messing with the dashboard rendering process, I'd suspect that first.

The database does not have more than 1000 rows on most of the dashboards. We reviewed everything with database and applied the necessary optimisations ever though the data was very small. We ensured that every query execution takes less than 60ms.

Also, make sure your Angular app isn't doing something strange like re-rendeing the iframe repeatedly on scroll or some other event.

Checked this behaviour as well. It does not load the specific dashboard repeatedly.

Additional Information

  1. Most of the dashboards have one chart in them.
  2. The issue still exists and appears randomly with any dashboard.
  3. The issue does not appear on Safari, but a different one. In Safari, the data fails to load with http code 400. However, if the user refreshes the dashboard using the manual refresh button, the dashboard loads correctly.

Comment From: rusackas

Are you still facing this issue? It's been silent here for > 6 months, and I certainly don't have an Angular host app to test anything with. Maybe @dosu has further advice here, but this may be clsoed as stale/not-planned if we can't figure out reproducible troubleshooting steps. Or we can move this to a Discussion, since I suspect it's a config thing more than an SDK bug.

Comment From: dosubot[bot]

Given the information provided, here are some additional suggestions to address the issue of Superset dashboards getting stuck on the loader when embedded in an Angular application:

  1. Asset Loading Optimization: Since loading multiple dashboards simultaneously causes a slowdown, consider optimizing asset loading. Ensure that common assets (JS, CSS) are cached effectively to prevent redundant downloads. You might also explore lazy loading techniques for assets specific to each dashboard.

  2. Network and Browser Caching: Verify that your server and browser caching settings are optimized. Proper caching can significantly reduce load times by reusing previously loaded resources.

  3. Concurrency Management: Limit the number of concurrent requests to the server when loading multiple dashboards. This can prevent server overload and improve response times.

  4. Error Handling in Safari: The HTTP 400 error in Safari suggests a potential issue with request parameters or headers. Double-check the requests being made in Safari to ensure they match those in other browsers. Consider adding more robust error handling and logging to capture detailed information about these failures.

  5. Angular Change Detection: Ensure that Angular's change detection isn't causing unnecessary re-renders or updates. You might need to adjust the change detection strategy or use OnPush to minimize unnecessary checks.

  6. Superset Configuration: Review Superset's configuration settings related to embedding and performance. Ensure that settings like SUPERSET_FEATURE_EMBEDDED_SUPERSET and others related to embedding are correctly configured.

  7. Debugging and Logging: Implement detailed logging both in your Angular application and Superset to capture more information about when and why the issue occurs. This can help identify patterns or specific conditions that trigger the problem.

If these suggestions do not resolve the issue, it might be beneficial to isolate the problem by testing with a minimal setup (e.g., fewer dashboards, simpler configurations) to identify the root cause.

To continue talking to Dosu, mention @dosu.


Help Dosu learn! Give it feedback: Great Response | Irrelevant Answer | Incorrect Sources | Too Verbose | Hallucination | Bug Report | Other

Comment From: rusackas

Closing as inactive/not-planned, but happy to reopen this as a Discussion if you'd like. Thanks!