From 4dec840f1bf6d5bd5cf8a788bef0f534564c6475 Mon Sep 17 00:00:00 2001 From: Hannah Mudge Date: Wed, 22 Oct 2025 14:43:56 -0600 Subject: [PATCH] [Dashboard] Fix search session restoration bug (#239822) Part of https://github.com/elastic/kibana/issues/239610 ## Summary Previously, we were **never** hitting the search session cache for dashboards with controls because we were removing the search session ID from the URL and generating a new session once controls published their filters, which lead us to fetch the **new** ID rather than the saved one: SearchSessionMismatch > [!NOTE] > Notice in the above network request that `isRestore` is false, which shows that we are not restoring an existing search session (`dd41248d-3dd7-41e6-99f0-e30c6b4a87f1`); instead; we are fetching from the newly generated one (`1f48aacc-90c5-4081-8157-b9b6d77b33b4`). This was because we weren't awaiting the dashboard controls to publish their filters, which caused `newSession$` to emit prematurely and triggered removing the search session ID from the URL before the search session actually had a chance to be restored. Now, we avoid generating a new search session ID until **after** controls have published their filters. ### Checklist - [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 - [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] 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. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> (cherry picked from commit 5b0b2dc18f39db53c33e1ecabefc423a0ad96094) --- ...rt_dashboard_search_session_integration.ts | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_api/search_sessions/start_dashboard_search_session_integration.ts b/src/platform/plugins/shared/dashboard/public/dashboard_api/search_sessions/start_dashboard_search_session_integration.ts index ac960be081764..07cc8fcfb3e93 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_api/search_sessions/start_dashboard_search_session_integration.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_api/search_sessions/start_dashboard_search_session_integration.ts @@ -7,15 +7,15 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { skip } from 'rxjs'; +import { combineLatest, filter, skip } from 'rxjs'; import { noSearchSessionStorageCapabilityMessage } from '@kbn/data-plugin/public'; -import { dataService } from '../../services/kibana_services'; import type { DashboardApi, DashboardCreationOptions } from '../..'; -import { newSession$ } from './new_session'; +import { dataService } from '../../services/kibana_services'; import { getDashboardCapabilities } from '../../utils/get_dashboard_capabilities'; import type { DashboardInternalApi } from '../types'; +import { newSession$ } from './new_session'; /** * Enables dashboard search sessions. @@ -53,30 +53,37 @@ export function startDashboardSearchSessionIntegration( ?.pipe(skip(1)) .subscribe(() => dashboardApi.forceRefresh()); - const newSessionSubscription = newSession$(dashboardApi).subscribe(() => { - const currentSearchSessionId = dashboardApi.searchSessionId$.value; + const newSessionSubscription = combineLatest([ + newSession$(dashboardApi), + dashboardApi.isFetchPaused$, + ]) + .pipe( + filter(([, isFetchPaused]) => !isFetchPaused) // don't generate new search session until fetch is unpaused + ) + .subscribe(() => { + const currentSearchSessionId = dashboardApi.searchSessionId$.value; - const updatedSearchSessionId: string | undefined = (() => { - let searchSessionIdFromURL = getSearchSessionIdFromURL(); - if (searchSessionIdFromURL) { - if ( - dataService.search.session.isRestore() && - dataService.search.session.isCurrentSession(searchSessionIdFromURL) - ) { - // we had previously been in a restored session but have now changed state so remove the session id from the URL. - removeSessionIdFromUrl(); - searchSessionIdFromURL = undefined; - } else { - dataService.search.session.restore(searchSessionIdFromURL); + const updatedSearchSessionId: string | undefined = (() => { + let searchSessionIdFromURL = getSearchSessionIdFromURL(); + if (searchSessionIdFromURL) { + if ( + dataService.search.session.isRestore() && + dataService.search.session.isCurrentSession(searchSessionIdFromURL) + ) { + // we had previously been in a restored session but have now changed state so remove the session id from the URL. + removeSessionIdFromUrl(); + searchSessionIdFromURL = undefined; + } else { + dataService.search.session.restore(searchSessionIdFromURL); + } } - } - return searchSessionIdFromURL ?? dataService.search.session.start(); - })(); + return searchSessionIdFromURL ?? dataService.search.session.start(); + })(); - if (updatedSearchSessionId && updatedSearchSessionId !== currentSearchSessionId) { - setSearchSessionId(updatedSearchSessionId); - } - }); + if (updatedSearchSessionId && updatedSearchSessionId !== currentSearchSessionId) { + setSearchSessionId(updatedSearchSessionId); + } + }); return () => { searchSessionIdChangeSubscription?.unsubscribe();