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

[Discover] Refactor app state functionality to getDiscoverAppStateContainer #149389

Merged
merged 26 commits into from
Feb 14, 2023

Conversation

kertal
Copy link
Member

@kertal kertal commented Jan 24, 2023

Summary

Migrating more code changes from #140765.

  • extracting AppState related code of discover_state.ts to discover_app_state_container.ts
  • extracting functions of use_discover_state.ts to separate files adding tests
  • adding a utility function to conditionally enable console.logs in the code which make it much easier to debug complex changes of state, without that I would have gone mad when doing [Discover] Create enhanced central state container #140765. I think its a valuable add-on, we could also use it e.g. when debugging cloud deployments, where we usually can't add additional code to debug
    • By entering window.ELASTIC_DISCOVER_LOGGER=true in the browser's console, logging messages by (addLog) are displayed in the console like this:
      Bildschirmfoto 2022-10-28 um 09 45 58 It was also helpful to temporarily add it in the root plugin of Discover for functional testing.
    • It was inspired by the Lens Redux logger window.ELASTIC_LENS_LOGGER=true

Testing

Nothing should change, nothing should break

Checklist

@kertal kertal added Feature:Discover Discover Application Team:DataDiscovery Discover, search (e.g. data plugin and KQL), data views, saved searches. For ES|QL, use Team:ES|QL. labels Jan 24, 2023
@kertal kertal self-assigned this Jan 24, 2023
@kertal
Copy link
Member Author

kertal commented Jan 25, 2023

@elasticmachine merge upstream

@kertal
Copy link
Member Author

kertal commented Jan 25, 2023

@elasticmachine merge upstream

@kertal kertal added chore release_note:skip Skip the PR/issue when compiling release notes labels Jan 27, 2023
@kertal kertal marked this pull request as ready for review January 27, 2023 10:34
@kertal kertal requested a review from a team as a code owner January 27, 2023 10:34
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-data-discovery (Team:DataDiscovery)

@kertal
Copy link
Member Author

kertal commented Jan 30, 2023

@elasticmachine merge upstream

@kertal
Copy link
Member Author

kertal commented Jan 31, 2023

@elasticmachine merge upstream

@kertal
Copy link
Member Author

kertal commented Feb 1, 2023

@elasticmachine merge upstream

Copy link
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.

Left a few comments and suggestions, but overall lots of good refactoring here. Didn't get a chance to test locally but will continue my review tomorrow.

);
return { fallback: !nextDataViewData.stateValFound, dataView: nextDataView };
};

return {
Copy link
Contributor

Choose a reason for hiding this comment

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

At some point I think it would make sense to get rid of some of these methods that seem to just call the equivalent methods on appStateContainer since we expose appState directly anyway. For example, I'm not sure methods like resetInitialAppState and getPreviousAppState are necessary, and the duplication might just cause confusion. It might also solve the circular import issue between discover_app_state_container and discover_state.

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, this should happen soon, nice cleanup potential for sure!

start();
return stop;
},
setAppState: (newPartial: AppState) => setState(appStateContainerModified, newPartial),
setAppState: (newPartial: AppState) => setState(appStateContainer, newPartial),
Copy link
Contributor

Choose a reason for hiding this comment

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

In what situations would we want to call setAppState instead of appStateContainer.update? They seem to do the same thing except appStateContainer.update also sets the previous state. Related to the comment above, but I feel like this could get confusing, and just calling appStateContainer.update would probably be preferred.

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, another potential cleanup, I would target this when moving more functions away from the main state container

Apply findings when chatting with Matt
@kertal
Copy link
Member Author

kertal commented Feb 7, 2023

@elasticmachine merge upstream

@kertal kertal requested a review from davismcphee February 7, 2023 07:01
Comment on lines +33 to +37
const prevState = stateContainer.appState.getPrevious();
if (isEqualState(prevState, nextState)) {
addLog('[appstate] subscribe update ignored due to no changes');
return;
}
Copy link
Member Author

Choose a reason for hiding this comment

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

FYI @mattkime this could help likely fix troubles with too much loading state triggers
FYI @davismcphee I've been adding this after you last review, it was part of the POC and would have been migrated next up, but it already makes sense

Copy link
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.

Finished local testing and a final round of code review, and everything seems to be working from what I can tell! 🎉

I left a couple suggestions that should help cleanup some imports and remove a circular dependency, but I'm also fine if we make those changes in a followup PR if preferred.

Unfortunately I did notice a significant performance degradation when switching data views, although it's not clear to me why this is happening. It seems the time it takes to switch has gone from ~150-200ms to ~500-750ms and is fairly noticeable as a user. See the videos below for examples with logging.

Before:

fast.mov

After:

slow.mov

With that said, since this isn't going in before FF and we have the whole minor to figure it out, I'm fine with merging this as is and fixing the performance issue in a followup PR if you'd prefer, so I'll approve and leave it up to you. Overall there's lots of great changes here and it definitely helps clean up Discover's state management.

@@ -165,7 +150,7 @@ export interface DiscoverStateContainer {
};
}

const APP_STATE_URL_KEY = '_a';
export const APP_STATE_URL_KEY = '_a';
Copy link
Contributor

@davismcphee davismcphee Feb 8, 2023

Choose a reason for hiding this comment

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

I thought I left a comment on this in my last review, but I can't find it anywhere, so maybe I'm just imagining things 😅. It seems like this is only used in discover_app_state_container and nowhere else, so it might make sense to move it there instead of exporting from here.

Copy link
Member Author

Choose a reason for hiding this comment

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

sure thing! done

import { DiscoverServices } from '../../../build_services';
import { handleSourceColumnState } from '../../../utils/state_helpers';
import { cleanupUrlState } from '../utils/cleanup_url_state';
import { getValidFilters } from '../../../utils/get_valid_filters';

export interface AppStateUrl extends Omit<AppState, 'sort'> {
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like AppStateUrl also isn't used in discover_state, and it might make sense to move it to discover_app_state_container instead. If we moved AppStateUrl and APP_STATE_URL_KEY to discover_app_state_container, it would have no imports left from discover_state and would fix the circular dependency between discover_app_state_container and discover_state.

Copy link
Member Author

Choose a reason for hiding this comment

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

good point! thx! done!

@kertal
Copy link
Member Author

kertal commented Feb 8, 2023

With that said, since this isn't going in before FF and we have the whole minor to figure it out, I'm fine with merging this as is and fixing the performance issue in a followup PR if you'd prefer, so I'll approve and leave it up to you. Overall there's lots of great changes here and it definitely helps clean up Discover's state management.

Well having a whole minor of bad performance dreams 😄 ... let's have a closer look at it, my basic first testing doesn't give bad results, so I'm interested how exactly you did measure? thx

@davismcphee
Copy link
Contributor

Well having a whole minor of bad performance dreams 😄 ... let's have a closer look at it, my basic first testing doesn't give bad results, so I'm interested how exactly you did measure? thx

Makes sense 😄. I tested by updating use_discover_state with console.time calls.

I updated onChangeDataView like this:

/**
 * Function triggered when user changes data view in the sidebar
 */
const onChangeDataView = useCallback(
  async (id: string) => {
    console.time('onChangeDataView');
    await changeDataView(id, { services, discoverState: stateContainer, setUrlTracking });
    setExpandedDoc(undefined);
  },
  [services, setExpandedDoc, setUrlTracking, stateContainer]
);

And updated the effect that watches the data view like this:

/**
 * Trigger data fetching on dataView or savedSearch changes
 */
useEffect(() => {
  if (dataView && initialFetchStatus === FetchStatus.LOADING) {
    console.timeEnd("onChangeDataView");
    refetch$.next(undefined);
  }
}, [initialFetchStatus, refetch$, dataView, savedSearch.id]);

In my testing it seems to be consistently slow across browsers (Chrome, Edge, Firefox, and Safari).

@kertal
Copy link
Member Author

kertal commented Feb 13, 2023

@elasticmachine merge upstream

stateContainer.dataState.reset();
stateContainer.actions.setDataView(nextDataView);
Copy link
Member Author

Choose a reason for hiding this comment

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

@davismcphee good performance catch! I've exchanged the order of resetting the data state and the setting of the next data view. and so the performance is like before, it was one of the changes I've suggested to ease the pain of #149294 , and it seems the re-rendering it triggered caused the performance issue (when you had just a single row, it didn't matter, but with many rows, et voila!, bad)

Copy link
Member Author

Choose a reason for hiding this comment

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

FYI @mattkime one of the changes that need to be reverted in #149294, but better once this is merged, and all testing is done

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
discover 432 435 +3

Async chunks

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

id before after diff
discover 392.0KB 391.7KB -250.0B

Page load bundle

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

id before after diff
discover 28.1KB 28.1KB -37.0B
Unknown metric groups

async chunk count

id before after diff
discover 10 9 -1

ESLint disabled line counts

id before after diff
discover 38 39 +1

References to deprecated APIs

id before after diff
discover 43 41 -2

Total ESLint disabled count

id before after diff
discover 39 40 +1

History

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

cc @kertal

Copy link
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.

Pulled and tested locally again, and no more performance issue 🙌 Thanks for looking into that, LGTM!

@kertal kertal merged commit 288054d into elastic:main Feb 14, 2023
@kibanamachine kibanamachine added v8.8.0 backport:skip This commit does not require backporting labels Feb 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting chore Feature:Discover Discover Application release_note:skip Skip the PR/issue when compiling release notes Team:DataDiscovery Discover, search (e.g. data plugin and KQL), data views, saved searches. For ES|QL, use Team:ES|QL. v8.8.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants