From ca885b23410b341bdedb3d474e88eea122f5fa89 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Tue, 19 May 2026 00:57:08 -0500 Subject: [PATCH] docs(perf): add Dashboard Performance guide covering virtualization, lazy tabs, and chart-count guidance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses a coverage gap kapa.ai surfaced: users repeatedly ask about max chart count per dashboard, a switch to cap concurrent chart loads, and how per-tab lazy loading works. Existing docs only mentioned DASHBOARD_VIRTUALIZATION in passing via the feature flags page, leaving users digging through GitHub issues and source for definitive answers. New page at /admin-docs/configuration/dashboard-performance covers: - No hard chart-count cap; practical thresholds (~25/~50 friction points) with caveats about query complexity. - DASHBOARD_VIRTUALIZATION (default True) — row-level viewport rendering, with the concrete behavior pulled from Row.tsx (1 viewport-height render-in margin, 4 viewport-heights unmount, embedded mode keeps charts mounted, headless skips entirely). - DASHBOARD_VIRTUALIZATION_DEFER_DATA (default False) — supplementary flag that also defers the data fetch. - Per-tab lazy loading is on by default with no flag — content in inactive tabs doesn't render or fetch. - No frontend concurrent-query limiter; concurrency is bounded by browser per-origin caps (~6) and backend worker count. - Splitting strategies in order of effort: tabs → cache → async queries → multiple dashboards → warehouse-side pre-aggregation. - Cross-links to Caching, Async Queries, and Feature Flags docs. --- .../configuration/dashboard-performance.mdx | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 docs/admin_docs/configuration/dashboard-performance.mdx diff --git a/docs/admin_docs/configuration/dashboard-performance.mdx b/docs/admin_docs/configuration/dashboard-performance.mdx new file mode 100644 index 00000000000..6b54b337c5b --- /dev/null +++ b/docs/admin_docs/configuration/dashboard-performance.mdx @@ -0,0 +1,168 @@ +--- +title: Dashboard Performance +hide_title: true +sidebar_position: 5 +version: 1 +--- + + + +# Dashboard Performance + +A dashboard's perceived speed is determined by three independent things: how +many charts have to render, how many queries the backend can execute +concurrently, and how quickly the underlying data warehouse can return +results. Superset gives you levers for the first two; the third belongs to +your warehouse. This page covers the dashboard-side levers and the practical +guidance around them. + +## Is there a maximum chart count per dashboard? + +**No hard limit is enforced** — Superset has no configuration key that +caps the number of charts on a dashboard. In practice, dashboards behave +well up to a few dozen charts. Beyond that, you'll typically feel friction +on the initial load and during cross-filter / time-range updates, even with +the lazy-loading optimizations described below. + +Rough thresholds to keep in mind: + +- **Under ~25 charts**: usually no perceptible problem. +- **25–50 charts**: still fine, but you start to want tabs to break the + page into chunks the user actually looks at. +- **Over ~50 charts**: split into multiple dashboards or use tabs + aggressively. The bottleneck is rarely Superset itself — it's the + warehouse executing dozens of queries in parallel and the browser + rendering dozens of chart frames. + +These are guidelines, not guarantees. A dashboard of 100 sparkline-style +charts hitting a fast cache behaves very differently from a dashboard of +20 heavy aggregations against a cold warehouse. + +## Lazy rendering — `DASHBOARD_VIRTUALIZATION` + +Superset's dashboard layout is virtualized at the row level. Charts that +are far below the user's current scroll position are not rendered (and +therefore don't fetch data) until the user scrolls them into view, and they +are unmounted again if scrolled well past. This is on by default. + +**Feature flag**: `DASHBOARD_VIRTUALIZATION` (default: `True`) + +The flag is `stable` and marked for path-to-deprecation — meaning the +behavior will eventually be non-optional, but the flag still exists so +operators can disable it if a specific layout misbehaves. + +**Behavior** (from `superset-frontend/src/dashboard/components/gridComponents/Row/Row.tsx`): + +- A chart is rendered when its row scrolls within **1 viewport height** of + the visible area. +- A chart is unmounted when its row scrolls more than **4 viewport + heights** away from the visible area. +- Tabs that aren't currently selected don't render their content at all + (see below). +- The unmounting half is skipped in **embedded** mode (so an embedded + dashboard keeps its charts mounted once they've been seen, which avoids + re-fetching on scroll-up). Both halves are skipped for **headless / + bot** rendering (so screenshot / report jobs load every chart). + +## Deferred data fetch — `DASHBOARD_VIRTUALIZATION_DEFER_DATA` + +By default, `DASHBOARD_VIRTUALIZATION` controls *rendering* — but charts +that don't render also don't fetch data, because Superset's chart +components issue their data request on mount. `DASHBOARD_VIRTUALIZATION_DEFER_DATA` +is a supplementary flag that further defers the data request itself, useful +for backends where opening a connection or compiling a query is expensive +even if the result is later thrown away. + +**Feature flag**: `DASHBOARD_VIRTUALIZATION_DEFER_DATA` (default: `False`) + +Enable this if you see warehouse load spike on dashboard *open* even +though most charts are off-screen. + +## Per-tab lazy loading + +**This is on by default and has no flag.** A tab's content is not rendered +until the user activates that tab, so charts inside an unselected tab do +not fetch data on dashboard open. When the user clicks the tab, that +tab's charts mount and fetch in the normal way. + +Practically: tabs are the single most effective tool for a large +dashboard. Splitting 60 charts across 4 tabs effectively turns dashboard +open into "load ~15 charts," and the remaining ones lazy-load only if the +user goes looking. + +## Is there a switch to cap concurrent chart queries? + +**No.** Superset does not implement a frontend-side concurrent-request +limiter. Each chart issues its own data request when it mounts, and the +browser handles parallelism (typically ~6 in-flight HTTP requests per +origin, then the rest queue). Backend throughput is bounded by your +Gunicorn worker count for synchronous query execution, or by your Celery +worker pool when [async queries](./async-queries-celery.mdx) are enabled. + +If you need to throttle warehouse load, the right place is: + +1. The warehouse itself (connection pool / concurrency limits). +2. Superset's Celery configuration (smaller worker pool when async + queries are on). +3. Splitting heavy charts across tabs or separate dashboards (each + dashboard load only fetches what's visible). + +## Splitting strategies + +When a dashboard outgrows comfortable performance, the options in order +of effort: + +**1. Move sections into tabs.** Same dashboard, but only the active tab's +charts fetch. This is the cheapest change and often the only one needed. + +**2. Cache aggressively.** A Redis cache backend (see +[Caching](./cache.mdx)) means repeat dashboard loads serve from cache +rather than re-hitting the warehouse. This is especially impactful for +dashboards opened by many users in close succession. + +**3. Enable async queries.** [Async query execution](./async-queries-celery.mdx) +via Celery decouples query duration from request lifetime, so a slow +chart doesn't block the page. The user sees other charts come in as +their queries complete. + +**4. Split into multiple dashboards.** Group related charts into purpose- +specific dashboards rather than one mega-dashboard. Link them from a +landing dashboard or a navigation menu. + +**5. Pre-aggregate at the warehouse level.** If the same expensive +aggregation appears across many charts, materialize it as a view or +scheduled table in the warehouse so each chart query is a cheap lookup. + +## Operational notes + +- The feature flags above are set in `superset_config.py`, e.g.: + + ```python + FEATURE_FLAGS = { + "DASHBOARD_VIRTUALIZATION": True, + "DASHBOARD_VIRTUALIZATION_DEFER_DATA": True, + } + ``` + +- See [Feature Flags](./feature-flags.mdx) for the full list of supported + flags and their lifecycle stages. +- Report and screenshot jobs (alerts, scheduled reports, dashboard + exports) intentionally bypass row virtualization so the rendered + artifact includes every chart, not just the ones above the fold.