Skip to content

[Cascade] Virtualizer Improvements#256699

Merged
eokoneyo merged 5 commits intoelastic:mainfrom
eokoneyo:chore/rework-virtualizer-optimizing-for-nesting
Mar 30, 2026
Merged

[Cascade] Virtualizer Improvements#256699
eokoneyo merged 5 commits intoelastic:mainfrom
eokoneyo:chore/rework-virtualizer-optimizing-for-nesting

Conversation

@eokoneyo
Copy link
Copy Markdown
Contributor

@eokoneyo eokoneyo commented Mar 9, 2026

Summary

This PR is part of the cascade performance improvement work(#255745), culled from #256037.

Also resolves #255745

In this PR the virtualizer implementation has been reworked, notable amongst the implementation change;

  • Introduces a ChildVirtualizerController that orchestrates nested virtualized lists within the data cascade, replacing the previous approach where each leaf cell independently managed its own useVirtualizer instance and manually wired scroll elements, margins, and offsets via individual props
  • Adds staggered activation for child virtualizers so expanded rows mount their heavy content (e.g. UnifiedDataTable) progressively rather than all at once, preventing main-thread stalls
  • introduces item-index-based anchoring (initialAnchorItemIndex), which survives row resizes and remeasurements more reliably than a raw pixel offset
  • Defers initial overscan to zero on mount and applies the configured value after the first paint, reducing DOM node count and layout thrashing during initial render
  • Exposes connectedChildren state and scrollAnchorItemIndex through the public API snapshot, enabling external consumers to observe nested virtualizer state.
  • Row measurements are now cached by the row key, this enables stable reference for previously measured rows reducing the style recalculation that needs to be performed when a new row gets rendered into the DOM on scroll
  • Lifts the skeleton/content gate from the consumer (ESQLDataCascadeLeafCell) into the library (CascadeRowCellPrimitive), so the skeleton is coordinated by the controller's activation signal rather than being opt-in per consumer

Checklist

Check the PR satisfies following conditions.

Reviewers should verify this PR satisfies this list as well.

@eokoneyo eokoneyo self-assigned this Mar 9, 2026
@eokoneyo eokoneyo force-pushed the chore/rework-virtualizer-optimizing-for-nesting branch 4 times, most recently from cdcb83b to 26bc412 Compare March 12, 2026 11:44
@eokoneyo eokoneyo force-pushed the chore/rework-virtualizer-optimizing-for-nesting branch 2 times, most recently from a033e53 to 24d22bb Compare March 24, 2026 11:34
- structural changes to virtualizer implementation
- favor anchor item index over scroll offset for scroll restoration
- use cascade virtualizer for nesting in discover
- deferred mounting for nested rows
- add foundation for controlled child virtualizer
- add orchestration for nested virtualized children
- adjustments so returning expanded rows do not show loading skeleton
- render a loader in place of white screen whilst cascade is bootstrapping
- scroll position restoration for nested virtualized grid
- adjustments so scroll restoration matches previous row exactly
@eokoneyo eokoneyo force-pushed the chore/rework-virtualizer-optimizing-for-nesting branch from 24d22bb to 6810255 Compare March 24, 2026 14:26
@eokoneyo
Copy link
Copy Markdown
Contributor Author

@elasticmachine merge upstream

@eokoneyo eokoneyo marked this pull request as ready for review March 25, 2026 06:53
@eokoneyo eokoneyo requested review from a team as code owners March 25, 2026 06:53
@eokoneyo eokoneyo added Team:SharedUX Platform AppEx-SharedUX (formerly Global Experience) t// backport:version Backport to applied version labels v9.3.3 labels Mar 25, 2026
@elasticmachine
Copy link
Copy Markdown
Contributor

Pinging @elastic/appex-sharedux (Team:SharedUX)

@eokoneyo eokoneyo added the release_note:skip Skip the PR/issue when compiling release notes label Mar 25, 2026
@eokoneyo
Copy link
Copy Markdown
Contributor Author

@elasticmachine merge upstream

Copy link
Copy Markdown
Contributor

@davismcphee davismcphee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Data Discovery code changes LGTM, and it feels like an improvement when testing, thanks!

cellId,
rowIndex,
// @ts-expect-error - required to allow the use of the visibleRows array
rows: visibleRows,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really not a fan of @ts-expect-error. Not a blocking issue or anything, but it tells me the types could be tightened up.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice cleanup in this file 👍

Comment on lines +155 to +160
const updateESQLQuery = useCallback(
(...args: Parameters<typeof onUpdateESQLQuery>) => {
onUpdateESQLQuery(...args);
},
[onUpdateESQLQuery]
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why wrap onUpdateESQLQuery? It's in the dependency array anyway so I don't think there's any advantage to memoizing it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the restoration PR we apply a condition here

Copy link
Copy Markdown
Member

@kertal kertal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming that with this #255745 can be closed, even the huge lifting seems to have been done in previous PRs. Great work @eokoneyo.

The most significant change with this PR appears to be a further reduction the computation, compared to what we currently do in main

Image

it's roughly 60% compared what we had before. I've tested loading and restoring a Group By Tab

All in all this make the experience a little bit faster, but as I said before, the main enhancements have been applied before

race-side-by-side-8.mov

@elasticmachine
Copy link
Copy Markdown
Contributor

💛 Build succeeded, but was flaky

Failed CI Steps

Test Failures

  • [job] [logs] Jest Tests #11 / AssetDocumentTab should select table tab when path tab is table

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
discover 2011 2014 +3

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
@kbn/shared-ux-document-data-cascade 23 106 +83

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
discover 1.6MB 1.6MB +8.3KB
Unknown metric groups

API count

id before after diff
@kbn/shared-ux-document-data-cascade 31 127 +96

ESLint disabled line counts

id before after diff
@kbn/shared-ux-document-data-cascade 8 7 -1

Total ESLint disabled count

id before after diff
@kbn/shared-ux-document-data-cascade 8 7 -1

History

cc @eokoneyo

@eokoneyo eokoneyo merged commit 99884e1 into elastic:main Mar 30, 2026
18 checks passed
@eokoneyo eokoneyo deleted the chore/rework-virtualizer-optimizing-for-nesting branch March 30, 2026 09:01
@kibanamachine
Copy link
Copy Markdown
Contributor

Starting backport for target branches: 9.3

https://github.com/elastic/kibana/actions/runs/23736598901

@kibanamachine
Copy link
Copy Markdown
Contributor

💔 All backports failed

Status Branch Result
9.3 Backport failed because of merge conflicts

You might need to backport the following PRs to 9.3:
- fix issue with cascade not showing on mobile (#255838)
- [Cascade] Improve scroll sync implementation (#256511)

Manual backport

To create the backport manually run:

node scripts/backport --pr 256699

Questions ?

Please refer to the Backport tool documentation

@kibanamachine kibanamachine added the backport missing Added to PRs automatically when the are determined to be missing a backport. label Mar 31, 2026
@kibanamachine
Copy link
Copy Markdown
Contributor

Friendly reminder: Looks like this PR hasn’t been backported yet.
To create automatically backports add a backport:* label or prevent reminders by adding the backport:skip label.
You can also create backports manually by running node scripts/backport --pr 256699 locally
cc: @eokoneyo

1 similar comment
@kibanamachine
Copy link
Copy Markdown
Contributor

Friendly reminder: Looks like this PR hasn’t been backported yet.
To create automatically backports add a backport:* label or prevent reminders by adding the backport:skip label.
You can also create backports manually by running node scripts/backport --pr 256699 locally
cc: @eokoneyo

jeramysoucy pushed a commit to jeramysoucy/kibana that referenced this pull request Apr 1, 2026
## Summary

This PR is part of the cascade performance improvement
work(elastic#255745), culled from
elastic#256037.

Also resolves elastic#255745

In this PR the virtualizer implementation has been reworked, notable
amongst the implementation change;

- Introduces a ChildVirtualizerController that orchestrates nested
virtualized lists within the data cascade, replacing the previous
approach where each leaf cell independently managed its own
useVirtualizer instance and manually wired scroll elements, margins, and
offsets via individual props
- Adds staggered activation for child virtualizers so expanded rows
mount their heavy content (e.g. UnifiedDataTable) progressively rather
than all at once, preventing main-thread stalls
- introduces item-index-based anchoring (initialAnchorItemIndex), which
survives row resizes and remeasurements more reliably than a raw pixel
offset
- Defers initial overscan to zero on mount and applies the configured
value after the first paint, reducing DOM node count and layout
thrashing during initial render
- Exposes connectedChildren state and scrollAnchorItemIndex through the
public API snapshot, enabling external consumers to observe nested
virtualizer state.
- Row measurements are now cached by the row key, this enables stable
reference for previously measured rows reducing the style recalculation
that needs to be performed when a new row gets rendered into the DOM on
scroll
- Lifts the skeleton/content gate from the consumer
(ESQLDataCascadeLeafCell) into the library (CascadeRowCellPrimitive), so
the skeleton is coordinated by the controller's activation signal rather
than being opt-in per consumer

### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.
<!--
- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials -->
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

<!--
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...

-->

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: Matthias Polman <matthias.wilhelm@elastic.co>
paulinashakirova pushed a commit to paulinashakirova/kibana that referenced this pull request Apr 2, 2026
## Summary

This PR is part of the cascade performance improvement
work(elastic#255745), culled from
elastic#256037.

Also resolves elastic#255745

In this PR the virtualizer implementation has been reworked, notable
amongst the implementation change;

- Introduces a ChildVirtualizerController that orchestrates nested
virtualized lists within the data cascade, replacing the previous
approach where each leaf cell independently managed its own
useVirtualizer instance and manually wired scroll elements, margins, and
offsets via individual props
- Adds staggered activation for child virtualizers so expanded rows
mount their heavy content (e.g. UnifiedDataTable) progressively rather
than all at once, preventing main-thread stalls
- introduces item-index-based anchoring (initialAnchorItemIndex), which
survives row resizes and remeasurements more reliably than a raw pixel
offset
- Defers initial overscan to zero on mount and applies the configured
value after the first paint, reducing DOM node count and layout
thrashing during initial render
- Exposes connectedChildren state and scrollAnchorItemIndex through the
public API snapshot, enabling external consumers to observe nested
virtualizer state.
- Row measurements are now cached by the row key, this enables stable
reference for previously measured rows reducing the style recalculation
that needs to be performed when a new row gets rendered into the DOM on
scroll
- Lifts the skeleton/content gate from the consumer
(ESQLDataCascadeLeafCell) into the library (CascadeRowCellPrimitive), so
the skeleton is coordinated by the controller's activation signal rather
than being opt-in per consumer

### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.
<!--
- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials -->
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

<!--
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- [ ] Review the [backport
guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)
and apply applicable `backport:*` labels.

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...

-->

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: Matthias Polman <matthias.wilhelm@elastic.co>
@kibanamachine
Copy link
Copy Markdown
Contributor

Friendly reminder: Looks like this PR hasn’t been backported yet.
To create automatically backports add a backport:* label or prevent reminders by adding the backport:skip label.
You can also create backports manually by running node scripts/backport --pr 256699 locally
cc: @eokoneyo

4 similar comments
@kibanamachine
Copy link
Copy Markdown
Contributor

Friendly reminder: Looks like this PR hasn’t been backported yet.
To create automatically backports add a backport:* label or prevent reminders by adding the backport:skip label.
You can also create backports manually by running node scripts/backport --pr 256699 locally
cc: @eokoneyo

@kibanamachine
Copy link
Copy Markdown
Contributor

Friendly reminder: Looks like this PR hasn’t been backported yet.
To create automatically backports add a backport:* label or prevent reminders by adding the backport:skip label.
You can also create backports manually by running node scripts/backport --pr 256699 locally
cc: @eokoneyo

@kibanamachine
Copy link
Copy Markdown
Contributor

Friendly reminder: Looks like this PR hasn’t been backported yet.
To create automatically backports add a backport:* label or prevent reminders by adding the backport:skip label.
You can also create backports manually by running node scripts/backport --pr 256699 locally
cc: @eokoneyo

@kibanamachine
Copy link
Copy Markdown
Contributor

Friendly reminder: Looks like this PR hasn’t been backported yet.
To create automatically backports add a backport:* label or prevent reminders by adding the backport:skip label.
You can also create backports manually by running node scripts/backport --pr 256699 locally
cc: @eokoneyo

@davismcphee davismcphee added backport:skip This PR does not require backporting and removed backport missing Added to PRs automatically when the are determined to be missing a backport. backport:version Backport to applied version labels v9.3.3 labels Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting release_note:skip Skip the PR/issue when compiling release notes Team:SharedUX Platform AppEx-SharedUX (formerly Global Experience) t// v9.4.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Discover][Group By] Lag when restoring a previously visited tab

6 participants