MouhibKhammassi opened a new issue, #38926:
URL: https://github.com/apache/superset/issues/38926
### Bug description
Every time I click Download as image on a chart, the entire Superset UI
locks up for roughly 6 seconds (longer on complex charts like large tables or
Deck.gl maps). The page becomes completely unresponsive during this time, I
cannot interact with anything. Once the freeze passes, the image downloads
normally.
The freeze is also synchronous. the browser does not show a spinner or
loading state. The tab simply stops responding entirely mid-click, which is a
distinct and more jarring experience than a normal async load.
What makes this feel like a bug rather than expected behaviour is that the
freeze is exactly the same duration every single time I download the same
chart. Whether it is my first download or my tenth, the UI hangs for the same
amount of time. It never gets faster. On most web applications, repeated
operations on the same data get faster because the browser or application
caches the work it has already done. That is not happening here.
Steps to Reproduce:
1-Open any dashboard containing a chart with moderate/high visual
complexity. a table chart with 50+ rows, a pivot table, or a bar/line chart
with a legend and axis labels all reproduce this reliably. Simpler charts (e.g.
a big number tile with a single value) may only produce a short freeze.
2-download the chart as an image
3-the entire browser tab freezes for 6 seconds. No spinner or loading
indicator , the page is just completely unresponsive. Then the .jpg file
downloads.
4-Without refreshing the page, click Download as image on the same chart
again immediately.
5- the tab freezes for exactly the same duration as the first time. The
second download is no faster than the first.
6-repeat n amount of times steps 2-4,The freeze duration is consistent on
every single attempt, it never decreases.
expected behavior:
Subsequent downloads of the same chart in the same session should be
significantly faster (ideally under 200ms) because the style information has
already been calculated and should be reused. Most of the expensive/complex
work is already done.
The UI should not completely freeze during the download. or at minimum, the
freeze should be short enough to be imperceptible on repeated downloads.
------
This is not a slow network request. The freeze happens before any file is
sent to the browser. it is the browser itself constructing the image from the
live DOM. The download dialog appears only after the freeze ends, confirming
the freeze is the image generation, not the file transfer.
The code that processes styles is supposed to remember the style
measurements it has already taken for each DOM element, so it does not have to
re-measure them on the next download. This caching mechanism is present in the
code. it is just being cleared at the end of every download, meaning the next
download starts from scratch with no memory of the previous one. The cache
fills up, the image is generated, and then the cache is immediately thrown away
before it can ever benefit a second download.
------
Proposed Fix:
I traced through superset-frontend/src/utils/downloadAsImage.tsx to find
where the cache is being cleared. The file is around 323 lines and the issue is
a single line (256) inside the createEnhancedClone function's cleanup callback:
const cleanup = () => {
styleCache.delete?.(originalElement); // ← this line is the problem
if (tempContainer.parentElement) {
tempContainer.parentElement.removeChild(tempContainer);
}
};
styleCache is a WeakMap declared at module scope on line 85. It is meant to
persist across downloads for the lifetime of the page. that is the whole point
of it being module-scoped rather than created fresh inside each download call.
Calling ".delete()" on it here wipes out the entry for originalElement
immediately after every download completes, so the next download finds an empty
cache and has to redo all the getComputedStyle work from scratch.
The fix is to remove that one line. as Weakmap is designed to release its
entries automatically once the DOM element they are keyed on is
garbage-collected by the browser. Removing entries by hand while the element is
still live and mounted in the DOM defeats the cache with no benefit.
The cleanup function still needs to remove the temporary off-screen
container it created (the tempContainer lines below), so those stay. Only the
styleCache.delete line is removed:
const cleanup = () => {
if (tempContainer.parentElement) {
tempContainer.parentElement.removeChild(tempContainer);
}
};
### Screenshots/recordings
_No response_
### Superset version
master / latest-dev
### Python version
3.9
### Node version
16
### Browser
Chrome
### Additional context
_No response_
### 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.
- [ ] 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.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]