Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EuiDataGrid] resolve performance issues when interacting with a large grid #5136

Merged

Conversation

chandlerprall
Copy link
Contributor

@chandlerprall chandlerprall commented Sep 2, 2021

Summary

Fixes #5115 by making our call to tabbable more exact.

Background

Application-provided content, rendered in EuiDataGrid cells, need to be removed from the page's tab order as we only want arrow keys to move focus between cells, and tab should jump into the grid's focused cell, and then continue to the next interactive element after the grid.

We had a previous performance issue with our approach to solving that, where every cell created its own mutation observer. We solved that by moving the mutation observer to the body component, but called tabbable with the entire grid instead of the more specific cell content containers.

However, on mouse over, cells render any associated actions which creates a DOM mutation, triggering the mutation observer, which slowed an entire page beyond a crawl just by mousing over a large grid.

Solve

This PR changes the call to tabbable, providing any affected/mutated cell content container instead of the entire grid. This greatly optimizes the work done by tabbable (it removes the work entirely for this case of just hovering over cells), making it an O(1) execution time amortized over cell contents' complexity instead of O(n) dependent on all cells currently rendered.

Alternative

I tried creating the mutation observer as part of the grid's context and allowing each individual cell to add an observed DOM node, which would avoid needing the getParentCellContent function, but observers have no way to unobserve without destroying the entire observer. This would have leaked cells' DOM into memory until the grid (and it's single observer) was GC'd.

Before

moving cursor over 10 rows
broken_10_rows

moving cursor over 100 rows
broken_100_rows

After

moving cursor over 10 rows
fixed_10_rows

moving cursor over 100 rows
fixed_100_rows

Result

Moving from 1-1.5s (2-5s in siem's table) stop-the-world when leaving a cell to ~40ms.

But you said this fix removes all work

Yes, but React Does Things™ to support onMouseEnter event handlers, which each cell uses. This is the cause of the execution time in the result screen shots. I want to understand this better, because of course I do, but it's unrelated to the issue at hand. Also, most of that time is spent by React doing development environment things, and a prod build is significantly better:

fixed_prod

Testing

  • Mouse your cursor over the grid, see how the links now underscore as you do it instead of the page locking up for seconds.
  • The first example was modified (I'll remove before merging) to show the interactive-disabling logic still functions.

Checklist

- [ ] Check against all themes for compatibility in both light and dark modes
- [ ] Checked in mobile

  • Checked in Chrome, Safari, Edge, and Firefox
    - [ ] Props have proper autodocs and playground toggles
    - [ ] Added documentation
    - [ ] Checked Code Sandbox works for the any docs examples
    - [ ] Added or updated jest tests
  • Checked for breaking changes and labeled appropriately
  • Checked for accessibility including keyboard-only and screenreader modes
  • A changelog entry exists and is marked appropriately

@chandlerprall chandlerprall changed the title Perf/5115 cell interactives [EuiDataGrid] resolve performance issues when interacting with a large grid Sep 2, 2021
@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5136/

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5136/

@cee-chen
Copy link
Contributor

cee-chen commented Sep 2, 2021

This is super amazing, huge kudos for the fix Chandler! This isn't a blocker per se and it's definitely more important we get this resolved for the Security folks, but I might as well mention it since I have EuiDataGrid unit testing on the brain - it feels like a bit of a shame to not be able to write tests for these (although admittedly that would be pretty difficult in Jest), mostly as a way document the exact use-case/problem we're trying to solve here.

Any thoughts on it if it would be a significant dev improvement to add a longer comment block above getParentCellContent, or tweaking the comment on line 620 to note that this is being done for performance (iterating upwards to the closest cell rather than tabbable parsing the entire wrapper)? I think between the 2 I have a slight lean towards the former over the inline comment, but totally up to you :)

@chandlerprall
Copy link
Contributor Author

Indeed! I was thinking the updated snapshots would suffice for now against regressions, and then we can really start codifying a lot of the functionality, etc, with cypress.

@chandlerprall
Copy link
Contributor Author

@constancecchen pushed up two additional comments

Copy link
Contributor

@cee-chen cee-chen left a comment

Choose a reason for hiding this comment

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

Awesome, thanks!! Super appreciate the longer comment, it helped clarify things even more for me personally! I have some small option copy nits, but otherwise LGTM!

src/components/datagrid/body/data_grid_body.tsx Outdated Show resolved Hide resolved
src/components/datagrid/body/data_grid_body.tsx Outdated Show resolved Hide resolved
@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5136/

chandlerprall and others added 4 commits September 3, 2021 08:41
Co-authored-by: Constance <constancecchen@users.noreply.github.com>
Co-authored-by: Constance <constancecchen@users.noreply.github.com>
@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5136/

Copy link
Contributor

@snide snide left a comment

Choose a reason for hiding this comment

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

Verified gains and double checked our example docs to make sure they worked. Have one small comment around the docs presentation.

src-docs/src/views/datagrid/datagrid.js Outdated Show resolved Hide resolved
@chandlerprall
Copy link
Contributor Author

09:34:40 ERROR: Failed to set up Chromium r818858! Set "PUPPETEER_SKIP_DOWNLOAD" env variable to skip download.
09:34:40 { Error: read ECONNRESET
09:34:40 at TLSWrap.onStreamRead (internal/stream_base_commons.js:111:27) errno: 'ECONNRESET', code: 'ECONNRESET', syscall: 'read' }

gonna push the example revision and use that to re-kick CI

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5136/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[EuiDataGrid] Optimize tabbable usage
4 participants