[Discover] Fix Discover tab initialization#245752
Conversation
…itialization to avoid leaking tab state, and avoid initiating data fetching until reselected since it relies on global services
| export const InitializationError = ({ | ||
| error: originalError, | ||
| }: { | ||
| error: Error | SerializedError; | ||
| }) => { | ||
| const error = useMemo( | ||
| () => (originalError instanceof Error ? originalError : new Error(originalError.message)), | ||
| [originalError] | ||
| ); |
There was a problem hiding this comment.
We need to account for the possibility of SerializedError now since error is coming from the Redux store.
|
|
||
| return { | ||
| status: getPreviewStatus(fetchStatus), | ||
| status: tabState.forceFetchOnSelect ? TabStatus.DEFAULT : getPreviewStatus(fetchStatus), |
There was a problem hiding this comment.
It makes sense to use the default preview status when forceFetchOnSelect === true since whatever results the tab has are no longer valid.
| getState, | ||
| { services, runtimeStateManager, urlStateStorage, searchSessionManager } | ||
| ) => { | ||
| export const initializeSingleTab = createInternalStateAsyncThunk( |
There was a problem hiding this comment.
Migrated to createInternalStateAsyncThunk so we can sync the TabInitializationStatus when initializing.
| dispatch( | ||
| internalStateSlice.actions.setForceFetchOnSelect({ tabId, forceFetchOnSelect: true }) | ||
| ); |
There was a problem hiding this comment.
Ideally we could start fetching even if the user changed tabs, but our fetching logic relies on global services, so using forceFetchOnSelect is much lower impact in the meantime.
| const withTab = <TPayload extends TabActionPayload>( | ||
| state: DiscoverInternalState, | ||
| action: TAction, | ||
| payload: TPayload, |
There was a problem hiding this comment.
Switched to payload so we can use withTab in builder.addCase() too.
| builder.addCase(initializeSingleTab.rejected, (state, action) => | ||
| withTab(state, action.meta.arg, (tab) => { | ||
| tab.initializationState = { | ||
| initializationStatus: TabInitializationStatus.Error, | ||
| error: action.error, | ||
| }; | ||
| }) | ||
| ); |
There was a problem hiding this comment.
This will unfortunately put SavedObjectNotFound into the Redux store which isn't serializable (and partially why I originally handled it in the component). But the multiple initializations bug is worse, and there's not a great alternative with how RedirectWhenSavedObjectNotFound currently works.
We should change the redirect logic at some point to get rid of the SavedObjectNotFound weirdness, which would fix this too.
There was a problem hiding this comment.
I'd like to have FTR tests for these fixes, but I'm not sure of a good way to delay tab initialization in order to test it. Open to any suggestions.
There was a problem hiding this comment.
Hi @dmlemeshko,
Do we have any configuration options for FTR to slow down network requests or to make the backend to handle requests slower?
There was a problem hiding this comment.
I'm going to merge this for now to get the fix in, but totally open to adding FTR tests for it as a followup if we think of a good way.
Flaky Test Runner Stats🟠 Some tests failed. - kibana-flaky-test-suite-runner#10030[✅] src/platform/test/functional/apps/discover/tabs/config.ts: 25/25 tests passed. |
Flaky Test Runner Stats🎉 All tests passed! - kibana-flaky-test-suite-runner#10031[✅] src/platform/test/functional/apps/discover/tabs_disabled/config.ts: 25/25 tests passed. |
| }, | ||
| ...existingTab, | ||
| ...item, | ||
| ...omit(item, 'initializationState'), |
There was a problem hiding this comment.
Don't let tab state updates overwrite the initializationState, it should only be changed via initializeSingleTab.
|
|
||
| export const selectTab = (state: DiscoverInternalState, tabId: string) => | ||
| state.tabs.byId[tabId] ?? state.tabs.recentlyClosedTabsById[tabId]; | ||
| export const selectTab = (state: DiscoverInternalState, tabId: string) => state.tabs.byId[tabId]; |
There was a problem hiding this comment.
So this is one piece I was a bit iffy on, but afaict it should be ok.
We used to select from recently closed tabs too, but this actually caused an existing subtle bug where resetting unsaved changes with deleted tabs would actually pull in the recently closed tab state when resetting, instead of basing it just on the persistedDiscoverSession tab state. This issue was surfaced in this PR since it was pulling in an invalid initializationState.
It looks like anywhere we currently access recently closed tabs uses selectRecentlyClosedTabs instead, and I was unable to find any drawbacks to changing this.
@jughosta Maybe you have more thoughts on this?
There was a problem hiding this comment.
I think the changes make sense.
|
Pinging @elastic/kibana-data-discovery (Team:DataDiscovery) |
jughosta
left a comment
There was a problem hiding this comment.
LGTM 👍
Thanks for this important fix!
|
|
||
| export const selectTab = (state: DiscoverInternalState, tabId: string) => | ||
| state.tabs.byId[tabId] ?? state.tabs.recentlyClosedTabsById[tabId]; | ||
| export const selectTab = (state: DiscoverInternalState, tabId: string) => state.tabs.byId[tabId]; |
There was a problem hiding this comment.
I think the changes make sense.
💛 Build succeeded, but was flaky
Failed CI StepsMetrics [docs]Async chunks
History
cc @davismcphee |
|
Starting backport for target branches: 9.2 |
💔 All backports failed
Manual backportTo create the backport manually run: Questions ?Please refer to the Backport tool documentation |
|
Friendly reminder: Looks like this PR hasn’t been backported yet. |
💚 All backports created successfully
Note: Successful backport PRs will be merged automatically after passing CI. Questions ?Please refer to the Backport tool documentation |
## Summary This PR fixes the Discover tab initialization logic to prevent the following: - State leaking between tabs when switching to another tab while the previous tab is still initializing, caused by syncing tab state with global Kibana services. - Duplicate tab initializations when switching away from and back to a tab before initialization completes, caused by the initialization logic checking for the existence of `DiscoverStateContainer` instead of a dedicated status property. These are the main fixes: - Move retrieving the initial URL state to the start of tab initialization before any async work in case the current tab (and URL) changes while initializing. - Avoid the following if the current tab changes during tab initialization: - Syncing tab state to global services, which overrides the current tab's state. - Calling `stateContainer.actions.initializeAndSync()`, which causes the wrong tab to be synced to the URL and global services. - Calling `stateContainer.actions.fetchData(true)`, which results in the wrong search parameters being used during data fetching. - Stop relying on `DiscoverStateContainer` to check if a tab is initialized, and instead introduce a dedicated `TabInitializationStatus` to prevent duplicate initializations when switching away from and back to a tab during initialization. Fixes elastic#245754. ### Checklist - [ ] 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 - [ ] [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) - [x] 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. - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] 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) - [x] Review the [backport guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing) and apply applicable `backport:*` labels. ### Identify risks - This should probably be backported to 9.2 since it's a bad bug, but there are some significant changes to the tab initialization logic. We should test thoroughly for regressions before merging, and in the 9.2 backport. (cherry picked from commit 12ae847) # Conflicts: # src/platform/plugins/shared/discover/public/application/main/components/tabs_view/use_preview_data.ts # src/platform/plugins/shared/discover/public/application/main/state_management/redux/actions/initialize_single_tab.ts # src/platform/plugins/shared/discover/public/application/main/state_management/redux/internal_state.ts # src/platform/plugins/shared/discover/public/application/main/state_management/redux/tab_mapping_utils.test.ts # src/platform/plugins/shared/discover/public/application/main/state_management/redux/types.ts
# Backport This will backport the following commits from `main` to `9.2`: - [[Discover] Fix Discover tab initialization (#245752)](#245752) <!--- Backport version: 10.2.0 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Davis McPhee","email":"davis.mcphee@elastic.co"},"sourceCommit":{"committedDate":"2025-12-10T23:41:31Z","message":"[Discover] Fix Discover tab initialization (#245752)\n\n## Summary\n\nThis PR fixes the Discover tab initialization logic to prevent the\nfollowing:\n- State leaking between tabs when switching to another tab while the\nprevious tab is still initializing, caused by syncing tab state with\nglobal Kibana services.\n- Duplicate tab initializations when switching away from and back to a\ntab before initialization completes, caused by the initialization logic\nchecking for the existence of `DiscoverStateContainer` instead of a\ndedicated status property.\n\nThese are the main fixes:\n- Move retrieving the initial URL state to the start of tab\ninitialization before any async work in case the current tab (and URL)\nchanges while initializing.\n- Avoid the following if the current tab changes during tab\ninitialization:\n- Syncing tab state to global services, which overrides the current\ntab's state.\n- Calling `stateContainer.actions.initializeAndSync()`, which causes the\nwrong tab to be synced to the URL and global services.\n- Calling `stateContainer.actions.fetchData(true)`, which results in the\nwrong search parameters being used during data fetching.\n- Stop relying on `DiscoverStateContainer` to check if a tab is\ninitialized, and instead introduce a dedicated `TabInitializationStatus`\nto prevent duplicate initializations when switching away from and back\nto a tab during initialization.\n\nFixes #245754.\n\n### Checklist\n\n- [ ] Any text added follows [EUI's writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\nsentence case text and includes [i18n\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\n- [ ]\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\nwas added for features that require explanation or tutorials\n- [ ] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [ ] If a plugin configuration key changed, check if it needs to be\nallowlisted in the cloud and added to the [docker\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\n- [x] This was checked for breaking HTTP API changes, and any breaking\nchanges have been approved by the breaking-change committee. The\n`release_note:breaking` label should be applied in these situations.\n- [x] [Flaky Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\nused on any tests changed\n- [x] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n- [x] Review the [backport\nguidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)\nand apply applicable `backport:*` labels.\n\n### Identify risks\n\n- This should probably be backported to 9.2 since it's a bad bug, but\nthere are some significant changes to the tab initialization logic. We\nshould test thoroughly for regressions before merging, and in the 9.2\nbackport.","sha":"12ae8477cbe4641c636f043fb1601539f33fac5c","branchLabelMapping":{"^v9.3.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","backport missing","Team:DataDiscovery","backport:version","v9.3.0","v9.2.3"],"title":"[Discover] Fix Discover tab initialization","number":245752,"url":"https://github.com/elastic/kibana/pull/245752","mergeCommit":{"message":"[Discover] Fix Discover tab initialization (#245752)\n\n## Summary\n\nThis PR fixes the Discover tab initialization logic to prevent the\nfollowing:\n- State leaking between tabs when switching to another tab while the\nprevious tab is still initializing, caused by syncing tab state with\nglobal Kibana services.\n- Duplicate tab initializations when switching away from and back to a\ntab before initialization completes, caused by the initialization logic\nchecking for the existence of `DiscoverStateContainer` instead of a\ndedicated status property.\n\nThese are the main fixes:\n- Move retrieving the initial URL state to the start of tab\ninitialization before any async work in case the current tab (and URL)\nchanges while initializing.\n- Avoid the following if the current tab changes during tab\ninitialization:\n- Syncing tab state to global services, which overrides the current\ntab's state.\n- Calling `stateContainer.actions.initializeAndSync()`, which causes the\nwrong tab to be synced to the URL and global services.\n- Calling `stateContainer.actions.fetchData(true)`, which results in the\nwrong search parameters being used during data fetching.\n- Stop relying on `DiscoverStateContainer` to check if a tab is\ninitialized, and instead introduce a dedicated `TabInitializationStatus`\nto prevent duplicate initializations when switching away from and back\nto a tab during initialization.\n\nFixes #245754.\n\n### Checklist\n\n- [ ] Any text added follows [EUI's writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\nsentence case text and includes [i18n\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\n- [ ]\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\nwas added for features that require explanation or tutorials\n- [ ] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [ ] If a plugin configuration key changed, check if it needs to be\nallowlisted in the cloud and added to the [docker\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\n- [x] This was checked for breaking HTTP API changes, and any breaking\nchanges have been approved by the breaking-change committee. The\n`release_note:breaking` label should be applied in these situations.\n- [x] [Flaky Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\nused on any tests changed\n- [x] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n- [x] Review the [backport\nguidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)\nand apply applicable `backport:*` labels.\n\n### Identify risks\n\n- This should probably be backported to 9.2 since it's a bad bug, but\nthere are some significant changes to the tab initialization logic. We\nshould test thoroughly for regressions before merging, and in the 9.2\nbackport.","sha":"12ae8477cbe4641c636f043fb1601539f33fac5c"}},"sourceBranch":"main","suggestedTargetBranches":["9.2"],"targetPullRequestStates":[{"branch":"main","label":"v9.3.0","branchLabelMappingKey":"^v9.3.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/245752","number":245752,"mergeCommit":{"message":"[Discover] Fix Discover tab initialization (#245752)\n\n## Summary\n\nThis PR fixes the Discover tab initialization logic to prevent the\nfollowing:\n- State leaking between tabs when switching to another tab while the\nprevious tab is still initializing, caused by syncing tab state with\nglobal Kibana services.\n- Duplicate tab initializations when switching away from and back to a\ntab before initialization completes, caused by the initialization logic\nchecking for the existence of `DiscoverStateContainer` instead of a\ndedicated status property.\n\nThese are the main fixes:\n- Move retrieving the initial URL state to the start of tab\ninitialization before any async work in case the current tab (and URL)\nchanges while initializing.\n- Avoid the following if the current tab changes during tab\ninitialization:\n- Syncing tab state to global services, which overrides the current\ntab's state.\n- Calling `stateContainer.actions.initializeAndSync()`, which causes the\nwrong tab to be synced to the URL and global services.\n- Calling `stateContainer.actions.fetchData(true)`, which results in the\nwrong search parameters being used during data fetching.\n- Stop relying on `DiscoverStateContainer` to check if a tab is\ninitialized, and instead introduce a dedicated `TabInitializationStatus`\nto prevent duplicate initializations when switching away from and back\nto a tab during initialization.\n\nFixes #245754.\n\n### Checklist\n\n- [ ] Any text added follows [EUI's writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\nsentence case text and includes [i18n\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\n- [ ]\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\nwas added for features that require explanation or tutorials\n- [ ] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [ ] If a plugin configuration key changed, check if it needs to be\nallowlisted in the cloud and added to the [docker\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\n- [x] This was checked for breaking HTTP API changes, and any breaking\nchanges have been approved by the breaking-change committee. The\n`release_note:breaking` label should be applied in these situations.\n- [x] [Flaky Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\nused on any tests changed\n- [x] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n- [x] Review the [backport\nguidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing)\nand apply applicable `backport:*` labels.\n\n### Identify risks\n\n- This should probably be backported to 9.2 since it's a bad bug, but\nthere are some significant changes to the tab initialization logic. We\nshould test thoroughly for regressions before merging, and in the 9.2\nbackport.","sha":"12ae8477cbe4641c636f043fb1601539f33fac5c"}},{"branch":"9.2","label":"v9.2.3","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT-->
## Summary This PR fixes the Discover tab initialization logic to prevent the following: - State leaking between tabs when switching to another tab while the previous tab is still initializing, caused by syncing tab state with global Kibana services. - Duplicate tab initializations when switching away from and back to a tab before initialization completes, caused by the initialization logic checking for the existence of `DiscoverStateContainer` instead of a dedicated status property. These are the main fixes: - Move retrieving the initial URL state to the start of tab initialization before any async work in case the current tab (and URL) changes while initializing. - Avoid the following if the current tab changes during tab initialization: - Syncing tab state to global services, which overrides the current tab's state. - Calling `stateContainer.actions.initializeAndSync()`, which causes the wrong tab to be synced to the URL and global services. - Calling `stateContainer.actions.fetchData(true)`, which results in the wrong search parameters being used during data fetching. - Stop relying on `DiscoverStateContainer` to check if a tab is initialized, and instead introduce a dedicated `TabInitializationStatus` to prevent duplicate initializations when switching away from and back to a tab during initialization. Fixes elastic#245754. ### Checklist - [ ] 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 - [ ] [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) - [x] 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. - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] 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) - [x] Review the [backport guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing) and apply applicable `backport:*` labels. ### Identify risks - This should probably be backported to 9.2 since it's a bad bug, but there are some significant changes to the tab initialization logic. We should test thoroughly for regressions before merging, and in the 9.2 backport.
Summary
This PR fixes the Discover tab initialization logic to prevent the following:
DiscoverStateContainerinstead of a dedicated status property.These are the main fixes:
stateContainer.actions.initializeAndSync(), which causes the wrong tab to be synced to the URL and global services.stateContainer.actions.fetchData(true), which results in the wrong search parameters being used during data fetching.DiscoverStateContainerto check if a tab is initialized, and instead introduce a dedicatedTabInitializationStatusto prevent duplicate initializations when switching away from and back to a tab during initialization.Fixes #245754.
Checklist
release_note:breakinglabel should be applied in these situations.release_note:*label is applied per the guidelinesbackport:*labels.Identify risks