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

[Dashboard] [Controls] Improve async controls flyout behaviour #154004

Conversation

Heenawter
Copy link
Contributor

@Heenawter Heenawter commented Mar 29, 2023

Closes #153572

Summary

This PR makes the calculation of the controls field registry truly async so that it can complete much faster - it also adds a better loading state to the field picker using the useAsync hook as a way to determine when the promise for the field registry has actually returned.

Previously, even though the function was marked as async and awaited, all of the internal logic was synchronous which meant that it always took O(3 * n) (where n is the number of fields, and 3 comes from the number of factories that are currently available for controls). In other words, if there are 10,000 fields in a given data view, the loop to generate the field registry would require 30,000 iterations.

This was such an intensive process that, once the calculation started, everything was completely consumed by it and Kibana would appear "frozen" - even the animation to open the flyout couldn't be completed, which is why it appeared to hang for so long; in fact, the animation could even get stuck in a "halfway open" state if there was a slight delay before the field registry calculation started.

Opening Flyout

  • Before

    Screen.Recording.2023-03-30.at.2.13.56.PM.mov
  • After

    Screen.Recording.2023-03-30.at.2.14.47.PM.mov

Changing Data View

  • Before

    Screen.Recording.2023-03-30.at.2.19.58.PM.mov
  • After

    Screen.Recording.2023-03-30.at.2.19.09.PM.mov

How to Test

  1. Load a large dataset with a large number of fields into Kibana - for example, you can run the following (which assumes that your Kibana instance is running on port 5601 and your ES server is running on port 9200):

    node scripts/es_archiver load test/functional/fixtures/es_archiver/large_fields --es-url=http://elastic:changeme@localhost:9200/ --kibana-url=http://elastic:changeme@localhost:5601/
    
    If you used the above technique to load the data, you'll also need to add a data view for that index.
  2. Make this new large_fields data view your default data view
  3. Create a new dashboard and add a control from the new large_fields data view
  4. The flyout should open quickly despite the fact that loading all the fields might take a little while

Checklist

For maintainers

@Heenawter Heenawter added Feature:Dashboard Dashboard related features release_note:fix Feature:Input Control Input controls visualization Team:Presentation Presentation Team for Dashboard, Input Controls, and Canvas loe:medium Medium Level of Effort impact:high Addressing this issue will have a high level of impact on the quality/strength of our product. backport:prev-minor Backport to (8.x) the previous minor version (i.e. one version back from main) v8.8.0 labels Mar 29, 2023
@Heenawter Heenawter self-assigned this Mar 29, 2023
@Heenawter Heenawter force-pushed the improve-async-controls-flyout-behaviour_2023-03-28 branch from afe79f8 to a446b21 Compare March 29, 2023 22:10
@Heenawter Heenawter force-pushed the improve-async-controls-flyout-behaviour_2023-03-28 branch from a446b21 to cf520af Compare March 29, 2023 22:12
@Heenawter Heenawter force-pushed the improve-async-controls-flyout-behaviour_2023-03-28 branch from af4fbae to 4a18b61 Compare March 30, 2023 15:26
panelPaddingSize="s"
ownFocus
panelClassName="presDataViewPicker__panel"
<EuiPopover
Copy link
Contributor Author

Choose a reason for hiding this comment

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

All I did here was remove the outer <> ... </> to remove an unnecessary layer 😆

}}
listProps={{
isVirtualized: true,
showIcons: false,
bordered: true,
}}
height={300}
height="full"
Copy link
Contributor Author

@Heenawter Heenawter Mar 30, 2023

Choose a reason for hiding this comment

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

I'm applying the height to the EuiSelectable via the fieldPickerSelectable class instead - this makes it so that switching to and from the loading state does not cause layout shifting:

Before After
Mar-30-2023 14-45-09 Mar-30-2023 14-43-27

Not sure if this is actually preferable? I'm torn 🤷 I tried to make the rounded box fill the space for the loading message, but it causes weird issues with the field type filter box... Not sure what's going on there exactly, but if we would prefer this, I can continue to fight with it :) Just didn't want to hold up the PR for such a small thing, especially if we decide to go with the "before" behaviour anyway.

cc @elastic/kibana-design

Copy link
Contributor

Choose a reason for hiding this comment

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

@Heenawter no layout shifting is definitely preferred. I suggest you make the height of the "Loading" container the same as that of the field list. Then you can vertically center the Loading options + spinner elements. I think that'll achieve an even cleaner UI.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 8e852cb 💃

Apr-05-2023 12-19-44

Copy link
Contributor

Choose a reason for hiding this comment

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

That looks so much better!

@Heenawter Heenawter marked this pull request as ready for review March 30, 2023 20:47
@Heenawter Heenawter requested review from a team as code owners March 30, 2023 20:47
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-presentation (Team:Presentation)

@ThomThomson ThomThomson self-requested a review April 3, 2023 14:27
Copy link
Contributor

@ThomThomson ThomThomson left a comment

Choose a reason for hiding this comment

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

This is looking / working so much better. Ran this locally with the 10k fields and looked through the code.

The only comment I have is that I think creating a list of promises for each field and using promise.all incurs some slight overhead.

I did some quick tests with a single top-level promise instead and was able to take the time to get the 10k fields from roughly 75ms to roughly 20ms. This isn't a huge performance improvement or anything, but I think it also results in cleaner code.

loadFieldRegistryFromDataView would look like:

const loadFieldRegistryFromDataView = async (
  dataView: DataView
): Promise<DataControlFieldRegistry> => {
  const {
    controls: { getControlTypes, getControlFactory },
  } = pluginServices.getServices();

  const controlFactories = getControlTypes().map(
    (controlType) => getControlFactory(controlType) as IEditableControlFactory
  );
  const fieldRegistry: DataControlFieldRegistry = {};
  return new Promise<DataControlFieldRegistry>((resolve) => {
    dataView.fields.getAll().map((field) => {
      const compatibleControlTypes = [];
      for (const factory of controlFactories) {
        if (factory.isFieldCompatible && factory.isFieldCompatible(field)) {
          compatibleControlTypes.push(factory.type);
        }
      }
      if (compatibleControlTypes.length > 0) {
        fieldRegistry[field.name] = { field, compatibleControlTypes };
      }
    });
    resolve(fieldRegistry);
  });
};

) {
dataControlField.compatibleControlTypes.push(this.type);
}
public isFieldCompatible = (field: DataViewField) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice cleanup!

factory.isFieldCompatible(test);
}
const fieldRegistry: DataControlFieldRegistry = {};
await Promise.all(
Copy link
Contributor

Choose a reason for hiding this comment

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

One other performance consideration is to use for loop for iterating through large collections. Each time map is run, a new function is created and this creates a large garbage collection penalty.

Copy link
Contributor

Choose a reason for hiding this comment

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

True! I bet we could increase this performance even further, cause the code snipped that I suggested also uses map unnecessarily.

Copy link
Contributor Author

@Heenawter Heenawter Apr 3, 2023

Choose a reason for hiding this comment

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

Changed to forEach in 9d29b33

Changed to for loop in afb1718

@Heenawter
Copy link
Contributor Author

@ThomThomson Good point - that's a lot cleaner, I agree :) Done in 9d29b33

Copy link
Contributor

@ThomThomson ThomThomson left a comment

Choose a reason for hiding this comment

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

Thanks for implementing the suggestion! Re-reviewed and looked over the code again, everything LGTM!

const test: DataControlField = { field, compatibleControlTypes: [] };
const fieldRegistry: DataControlFieldRegistry = {};
return new Promise<DataControlFieldRegistry>((resolve) => {
for (const field of dataView.fields.getAll()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

for ... of is not much better. How about an imperative for loop, the one with the iterator for(i=0; i<length;i++)

Copy link
Contributor Author

@Heenawter Heenawter Apr 3, 2023

Choose a reason for hiding this comment

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

I did some performance testing of all 3 options:

  1. Using .forEach with a callback - took 11.64ms on average over 5 tries
  2. Using for ... of for loop - took 12.52ms on average over 5 tries
  3. Using for(let i=0....) imperative for loop - took 12.80ms on average over 5 tries.

So it seems like the imperative for loop is actually the worst of the three, although the differences here are so minimal I'm sure all three options would average out to be more-or-less equivalent over 100+ tests instead of 5 (for example, in my tests for the imperative for loop there was one run that randomly took 17.9ms - doing more iterations would eliminate this variance).

I'm wondering if it is worth going this deep over such minimal improvements (~1ms for 10,000 fields), though? I'm in favour of sticking with readability in a situation like this, and I personally vote for the for ... of loop in this case because it matches the style of the internal factory loop. 🤷

Copy link
Contributor

Choose a reason for hiding this comment

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

right, looking at https://leanylabs.com/blog/js-forEach-map-reduce-vs-for-for_of/ for..in and a traditional for loop have about the same performance, with for..in having slightly better performance.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Was just going to link the same article 😆 Awesome, thanks!

Copy link
Contributor

Choose a reason for hiding this comment

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

I was reading that one too hahaha.

Copy link
Contributor

@nreese nreese left a comment

Choose a reason for hiding this comment

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

LGTM

@andreadelrio andreadelrio self-requested a review April 5, 2023 15:07
Copy link
Contributor

@andreadelrio andreadelrio left a comment

Choose a reason for hiding this comment

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

Design changes LGTM. Nice work @Heenawter 🎉

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
controls 153 156 +3

Async chunks

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

id before after diff
controls 189.4KB 190.4KB +1002.0B
presentationUtil 129.1KB 129.6KB +520.0B
total +1.5KB

Public APIs missing exports

Total count of every type that is part of your API that should be exported but is not. This will cause broken links in the API documentation system. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats exports for more detailed information.

id before after diff
controls 11 10 -1

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
controls 33.3KB 33.1KB -132.0B
Unknown metric groups

ESLint disabled line counts

id before after diff
securitySolution 432 435 +3

Total ESLint disabled count

id before after diff
securitySolution 512 515 +3

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

cc @Heenawter

@Heenawter Heenawter merged commit 608886f into elastic:main Apr 5, 2023
@Heenawter Heenawter deleted the improve-async-controls-flyout-behaviour_2023-03-28 branch April 5, 2023 19:48
@kibanamachine
Copy link
Contributor

💔 All backports failed

Status Branch Result
8.7 Backport failed because of merge conflicts

Manual backport

To create the backport manually run:

node scripts/backport --pr 154004

Questions ?

Please refer to the Backport tool documentation

@Heenawter
Copy link
Contributor Author

💚 All backports created successfully

Status Branch Result
8.7

Note: Successful backport PRs will be merged automatically after passing CI.

Questions ?

Please refer to the Backport tool documentation

Heenawter added a commit to Heenawter/kibana that referenced this pull request Apr 10, 2023
…ic#154004)

Closes elastic#153572

This PR makes the calculation of the controls field registry **truly
async** so that it can complete much faster - it also adds a better
loading state to the field picker using the `useAsync` hook as a way to
determine when the promise for the field registry has actually returned.

Previously, even though the function was marked as async and awaited,
all of the internal logic was synchronous which meant that it always
took `O(3 * n)` (where `n` is the number of fields, and `3` comes from
the number of factories that are currently available for controls). In
other words, if there are `10,000` fields in a given data view, the loop
to generate the field registry would require `30,000` iterations.

This was such an intensive process that, once the calculation started,
**everything** was completely consumed by it and Kibana would appear
"frozen" - even the animation to open the flyout couldn't be completed,
which is why it appeared to hang for so long; in fact, the animation
could even get stuck in a "halfway open" state if there was a slight
delay before the field registry calculation started.

- **Before**

https://user-images.githubusercontent.com/8698078/228954005-343d96b6-2029-4d9a-9501-5aff409c1f1a.mov

- **After**

https://user-images.githubusercontent.com/8698078/228954264-c1b725f2-cf93-4e8f-884d-32a1ab07d399.mov

- **Before**

https://user-images.githubusercontent.com/8698078/228955105-b0ef374c-7482-4a7d-9d4d-5de589a126ba.mov

- **After**

https://user-images.githubusercontent.com/8698078/228955133-d253afff-0cea-472d-a844-09ac0765b487.mov

1. Load a large dataset with a large number of fields into Kibana - for
example, you can run the following (which assumes that your Kibana
instance is running on port `5601` and your ES server is running on port
`9200`):<br><br>
    ```
node scripts/es_archiver load
test/functional/fixtures/es_archiver/large_fields
--es-url=http://elastic:changeme@localhost:9200/
--kibana-url=http://elastic:changeme@localhost:5601/
    ```
If you used the above technique to load the data, you'll also need to
add a data view for that index.
2. Make this new  `large_fields` data view your **default** data view
3. Create a new dashboard and add a control from the new `large_fields`
data view
4. The flyout should open **quickly** despite the fact that loading all
the fields might take a little while

- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

(cherry picked from commit 608886f)
Heenawter added a commit that referenced this pull request Apr 10, 2023
…154004) (#154499)

# Backport

This will backport the following commits from `main` to `8.7`:
- [[Dashboard] [Controls] Improve async controls flyout behaviour
(#154004)](#154004)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Hannah
Mudge","email":"Heenawter@users.noreply.github.com"},"sourceCommit":{"committedDate":"2023-04-05T19:46:03Z","message":"[Dashboard]
[Controls] Improve async controls flyout behaviour (#154004)\n\nCloses
https://github.com/elastic/kibana/issues/153572\r\n\r\n##
Summary\r\n\r\nThis PR makes the calculation of the controls field
registry **truly\r\nasync** so that it can complete much faster - it
also adds a better\r\nloading state to the field picker using the
`useAsync` hook as a way to\r\ndetermine when the promise for the field
registry has actually returned.\r\n\r\n\r\nPreviously, even though the
function was marked as async and awaited,\r\nall of the internal logic
was synchronous which meant that it always\r\ntook `O(3 * n)` (where `n`
is the number of fields, and `3` comes from\r\nthe number of factories
that are currently available for controls). In\r\nother words, if there
are `10,000` fields in a given data view, the loop\r\nto generate the
field registry would require `30,000` iterations.\r\n\r\nThis was such
an intensive process that, once the calculation
started,\r\n**everything** was completely consumed by it and Kibana
would appear\r\n\"frozen\" - even the animation to open the flyout
couldn't be completed,\r\nwhich is why it appeared to hang for so long;
in fact, the animation\r\ncould even get stuck in a \"halfway open\"
state if there was a slight\r\ndelay before the field registry
calculation started.\r\n\r\n\r\n### Opening Flyout\r\n \r\n\r\n-
**Before**\r\n\r\n\r\nhttps://user-images.githubusercontent.com/8698078/228954005-343d96b6-2029-4d9a-9501-5aff409c1f1a.mov\r\n\r\n-
**After**\r\n\r\n\r\nhttps://user-images.githubusercontent.com/8698078/228954264-c1b725f2-cf93-4e8f-884d-32a1ab07d399.mov\r\n\r\n\r\n###
Changing Data View\r\n\r\n-
**Before**\r\n\r\n\r\nhttps://user-images.githubusercontent.com/8698078/228955105-b0ef374c-7482-4a7d-9d4d-5de589a126ba.mov\r\n\r\n-
**After**\r\n\r\n\r\nhttps://user-images.githubusercontent.com/8698078/228955133-d253afff-0cea-472d-a844-09ac0765b487.mov\r\n\r\n\r\n\r\n##
How to Test\r\n\r\n1. Load a large dataset with a large number of fields
into Kibana - for\r\nexample, you can run the following (which assumes
that your Kibana\r\ninstance is running on port `5601` and your ES
server is running on port\r\n`9200`):<br><br>\r\n ```\r\nnode
scripts/es_archiver
load\r\ntest/functional/fixtures/es_archiver/large_fields\r\n--es-url=http://elastic:changeme@localhost:9200/\r\n--kibana-url=http://elastic:changeme@localhost:5601/\r\n
```\r\nIf you used the above technique to load the data, you'll also
need to\r\nadd a data view for that index.\r\n2. Make this new
`large_fields` data view your **default** data view\r\n3. Create a new
dashboard and add a control from the new `large_fields`\r\ndata
view\r\n4. The flyout should open **quickly** despite the fact that
loading all\r\nthe fields might take a little while\r\n\r\n###
Checklist\r\n\r\n- [x] Any UI touched in this PR is usable by keyboard
only (learn more\r\nabout [keyboard
accessibility](https://webaim.org/techniques/keyboard/))\r\n- [x] Any UI
touched in this PR does not create any new axe failures\r\n(run axe in
browser:\r\n[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),\r\n[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))\r\n-
[x] This renders correctly on smaller devices using a
responsive\r\nlayout. (You can test this [in
your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))\r\n-
[x] This was checked for
[cross-browser\r\ncompatibility](https://www.elastic.co/support/matrix#matrix_browsers)\r\n\r\n\r\n###
For maintainers\r\n\r\n- [ ] This was checked for breaking API changes
and was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"608886ff862f89ca30d1d4126a64877168f4976e","branchLabelMapping":{"^v8.8.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Feature:Dashboard","release_note:fix","Feature:Input
Control","Team:Presentation","loe:days","impact:high","backport:prev-minor","v8.8.0"],"number":154004,"url":"https://github.com/elastic/kibana/pull/154004","mergeCommit":{"message":"[Dashboard]
[Controls] Improve async controls flyout behaviour (#154004)\n\nCloses
https://github.com/elastic/kibana/issues/153572\r\n\r\n##
Summary\r\n\r\nThis PR makes the calculation of the controls field
registry **truly\r\nasync** so that it can complete much faster - it
also adds a better\r\nloading state to the field picker using the
`useAsync` hook as a way to\r\ndetermine when the promise for the field
registry has actually returned.\r\n\r\n\r\nPreviously, even though the
function was marked as async and awaited,\r\nall of the internal logic
was synchronous which meant that it always\r\ntook `O(3 * n)` (where `n`
is the number of fields, and `3` comes from\r\nthe number of factories
that are currently available for controls). In\r\nother words, if there
are `10,000` fields in a given data view, the loop\r\nto generate the
field registry would require `30,000` iterations.\r\n\r\nThis was such
an intensive process that, once the calculation
started,\r\n**everything** was completely consumed by it and Kibana
would appear\r\n\"frozen\" - even the animation to open the flyout
couldn't be completed,\r\nwhich is why it appeared to hang for so long;
in fact, the animation\r\ncould even get stuck in a \"halfway open\"
state if there was a slight\r\ndelay before the field registry
calculation started.\r\n\r\n\r\n### Opening Flyout\r\n \r\n\r\n-
**Before**\r\n\r\n\r\nhttps://user-images.githubusercontent.com/8698078/228954005-343d96b6-2029-4d9a-9501-5aff409c1f1a.mov\r\n\r\n-
**After**\r\n\r\n\r\nhttps://user-images.githubusercontent.com/8698078/228954264-c1b725f2-cf93-4e8f-884d-32a1ab07d399.mov\r\n\r\n\r\n###
Changing Data View\r\n\r\n-
**Before**\r\n\r\n\r\nhttps://user-images.githubusercontent.com/8698078/228955105-b0ef374c-7482-4a7d-9d4d-5de589a126ba.mov\r\n\r\n-
**After**\r\n\r\n\r\nhttps://user-images.githubusercontent.com/8698078/228955133-d253afff-0cea-472d-a844-09ac0765b487.mov\r\n\r\n\r\n\r\n##
How to Test\r\n\r\n1. Load a large dataset with a large number of fields
into Kibana - for\r\nexample, you can run the following (which assumes
that your Kibana\r\ninstance is running on port `5601` and your ES
server is running on port\r\n`9200`):<br><br>\r\n ```\r\nnode
scripts/es_archiver
load\r\ntest/functional/fixtures/es_archiver/large_fields\r\n--es-url=http://elastic:changeme@localhost:9200/\r\n--kibana-url=http://elastic:changeme@localhost:5601/\r\n
```\r\nIf you used the above technique to load the data, you'll also
need to\r\nadd a data view for that index.\r\n2. Make this new
`large_fields` data view your **default** data view\r\n3. Create a new
dashboard and add a control from the new `large_fields`\r\ndata
view\r\n4. The flyout should open **quickly** despite the fact that
loading all\r\nthe fields might take a little while\r\n\r\n###
Checklist\r\n\r\n- [x] Any UI touched in this PR is usable by keyboard
only (learn more\r\nabout [keyboard
accessibility](https://webaim.org/techniques/keyboard/))\r\n- [x] Any UI
touched in this PR does not create any new axe failures\r\n(run axe in
browser:\r\n[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),\r\n[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))\r\n-
[x] This renders correctly on smaller devices using a
responsive\r\nlayout. (You can test this [in
your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))\r\n-
[x] This was checked for
[cross-browser\r\ncompatibility](https://www.elastic.co/support/matrix#matrix_browsers)\r\n\r\n\r\n###
For maintainers\r\n\r\n- [ ] This was checked for breaking API changes
and was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"608886ff862f89ca30d1d4126a64877168f4976e"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.8.0","labelRegex":"^v8.8.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/154004","number":154004,"mergeCommit":{"message":"[Dashboard]
[Controls] Improve async controls flyout behaviour (#154004)\n\nCloses
https://github.com/elastic/kibana/issues/153572\r\n\r\n##
Summary\r\n\r\nThis PR makes the calculation of the controls field
registry **truly\r\nasync** so that it can complete much faster - it
also adds a better\r\nloading state to the field picker using the
`useAsync` hook as a way to\r\ndetermine when the promise for the field
registry has actually returned.\r\n\r\n\r\nPreviously, even though the
function was marked as async and awaited,\r\nall of the internal logic
was synchronous which meant that it always\r\ntook `O(3 * n)` (where `n`
is the number of fields, and `3` comes from\r\nthe number of factories
that are currently available for controls). In\r\nother words, if there
are `10,000` fields in a given data view, the loop\r\nto generate the
field registry would require `30,000` iterations.\r\n\r\nThis was such
an intensive process that, once the calculation
started,\r\n**everything** was completely consumed by it and Kibana
would appear\r\n\"frozen\" - even the animation to open the flyout
couldn't be completed,\r\nwhich is why it appeared to hang for so long;
in fact, the animation\r\ncould even get stuck in a \"halfway open\"
state if there was a slight\r\ndelay before the field registry
calculation started.\r\n\r\n\r\n### Opening Flyout\r\n \r\n\r\n-
**Before**\r\n\r\n\r\nhttps://user-images.githubusercontent.com/8698078/228954005-343d96b6-2029-4d9a-9501-5aff409c1f1a.mov\r\n\r\n-
**After**\r\n\r\n\r\nhttps://user-images.githubusercontent.com/8698078/228954264-c1b725f2-cf93-4e8f-884d-32a1ab07d399.mov\r\n\r\n\r\n###
Changing Data View\r\n\r\n-
**Before**\r\n\r\n\r\nhttps://user-images.githubusercontent.com/8698078/228955105-b0ef374c-7482-4a7d-9d4d-5de589a126ba.mov\r\n\r\n-
**After**\r\n\r\n\r\nhttps://user-images.githubusercontent.com/8698078/228955133-d253afff-0cea-472d-a844-09ac0765b487.mov\r\n\r\n\r\n\r\n##
How to Test\r\n\r\n1. Load a large dataset with a large number of fields
into Kibana - for\r\nexample, you can run the following (which assumes
that your Kibana\r\ninstance is running on port `5601` and your ES
server is running on port\r\n`9200`):<br><br>\r\n ```\r\nnode
scripts/es_archiver
load\r\ntest/functional/fixtures/es_archiver/large_fields\r\n--es-url=http://elastic:changeme@localhost:9200/\r\n--kibana-url=http://elastic:changeme@localhost:5601/\r\n
```\r\nIf you used the above technique to load the data, you'll also
need to\r\nadd a data view for that index.\r\n2. Make this new
`large_fields` data view your **default** data view\r\n3. Create a new
dashboard and add a control from the new `large_fields`\r\ndata
view\r\n4. The flyout should open **quickly** despite the fact that
loading all\r\nthe fields might take a little while\r\n\r\n###
Checklist\r\n\r\n- [x] Any UI touched in this PR is usable by keyboard
only (learn more\r\nabout [keyboard
accessibility](https://webaim.org/techniques/keyboard/))\r\n- [x] Any UI
touched in this PR does not create any new axe failures\r\n(run axe in
browser:\r\n[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),\r\n[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))\r\n-
[x] This renders correctly on smaller devices using a
responsive\r\nlayout. (You can test this [in
your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))\r\n-
[x] This was checked for
[cross-browser\r\ncompatibility](https://www.elastic.co/support/matrix#matrix_browsers)\r\n\r\n\r\n###
For maintainers\r\n\r\n- [ ] This was checked for breaking API changes
and was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"608886ff862f89ca30d1d4126a64877168f4976e"}}]}]
BACKPORT-->

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:prev-minor Backport to (8.x) the previous minor version (i.e. one version back from main) Feature:Dashboard Dashboard related features Feature:Input Control Input controls visualization impact:high Addressing this issue will have a high level of impact on the quality/strength of our product. loe:medium Medium Level of Effort release_note:fix Team:Presentation Presentation Team for Dashboard, Input Controls, and Canvas v8.7.1 v8.8.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Controls] Improve loading state of add/edit flyout
7 participants