[Logs UI] Add helper hooks with search strategy request cancellation#83906
[Logs UI] Add helper hooks with search strategy request cancellation#83906weltenwort merged 30 commits intoelastic:masterfrom
Conversation
fbdc783 to
d6db953
Compare
55eb746 to
11d7b79
Compare
d9f6709 to
9af62cd
Compare
9af62cd to
b4b4ff9
Compare
|
Pinging @elastic/logs-metrics-ui (Team:logs-metrics-ui) |
afgomez
left a comment
There was a problem hiding this comment.
On a first pass the code looks good. I don't feel comfortable yet commenting on the RxJS parts but I promise I'll give it a shot tomorrow :D.
One thing I've noticed that I'm not sure it matters in practical effects: there seems to be a distinction between explicitly aborting a request and implicitly doing it when the containing component unmounts.
When the request is explicitly cancelled, there's an extra DELETE request, probably made to do housekeeping. However, when the component unmounts (by closing the flyout, for example), this request doesn't seem to happen.
x-pack/plugins/infra/public/components/data_search_error_callout.tsx
Outdated
Show resolved
Hide resolved
x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_fields_table.tsx
Outdated
Show resolved
Hide resolved
x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
Outdated
Show resolved
Hide resolved
x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
Show resolved
Hide resolved
x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
Outdated
Show resolved
Hide resolved
| logEntryId: string | null | undefined; | ||
| }) => { | ||
| const { search: fetchLogEntry, requests$: logEntrySearchRequests$ } = useDataSearch({ | ||
| getRequest: useCallback(() => { |
There was a problem hiding this comment.
Is the useCallback required here? What would happen if the identity of this function changes?
There was a problem hiding this comment.
The consequence is that the returned search() function's identity changes every render. That's probably not a problem depending on the downstream use of search. So this is a performance optimization.
| if (newUrlState && newUrlState.flyoutId) { | ||
| setFlyoutId(newUrlState.flyoutId); | ||
| setLogEntryId(newUrlState.flyoutId); | ||
| } | ||
| if (newUrlState && newUrlState.surroundingLogsId) { | ||
| setSurroundingLogsId(newUrlState.surroundingLogsId); | ||
| } | ||
| if (newUrlState && newUrlState.flyoutVisibility === FlyoutVisibility.visible) { | ||
| setFlyoutVisibility(true); | ||
| openFlyout(); | ||
| } | ||
| if (newUrlState && newUrlState.flyoutVisibility === FlyoutVisibility.hidden) { | ||
| setFlyoutVisibility(false); | ||
| closeFlyout(); | ||
| } | ||
| }} |
There was a problem hiding this comment.
I think we can simplify this block a bit. What do you think?
if (newUrlState && newUrlState.flyoutId && newUrlState.flyoutVisibility === FlyoutVisibility.visible) {
openFlyout(newUrlState.flyoutId);
}
if (newUrlState && newUrlState.flyoutVisibility === FlyoutVisibility.hidden) {
closeFlyout();
}
if (newUrlState && newUrlState.surroundingLogsId) {
setSurroundingLogsId(newUrlState.surroundingLogsId);
}That way I think that we no longer need to expose the setLogEntryId() callback from useLogEntryFlyoutContext()
There's something about this block that github doesn't like; it doesn't let me add a suggestion.
There was a problem hiding this comment.
But wouldn't that mean that we can't represent the scenario that the URL contains a flyoutId but the visibility is false? I personally don't like that split anyway, but I wonder how risky it would be to change the URL semantics.
| if (initialUrlState && initialUrlState.flyoutId) { | ||
| setFlyoutId(initialUrlState.flyoutId); | ||
| setLogEntryId(initialUrlState.flyoutId); | ||
| } | ||
| if (initialUrlState && initialUrlState.surroundingLogsId) { | ||
| setSurroundingLogsId(initialUrlState.surroundingLogsId); | ||
| } | ||
| if (initialUrlState && initialUrlState.flyoutVisibility === FlyoutVisibility.visible) { | ||
| setFlyoutVisibility(true); | ||
| openFlyout(); | ||
| } | ||
| if (initialUrlState && initialUrlState.flyoutVisibility === FlyoutVisibility.hidden) { | ||
| setFlyoutVisibility(false); | ||
| closeFlyout(); | ||
| } |
That's an interesting observation. I suspect it's a matter of timing. The |
That seems the case. If I cancel the request when the "Searching log entries in shards" message appears, it always sends a I don't think it's a big issue, but we can check with the data plugin owners to see if it is. If that's the case we can always fix it after feature freeze. |
|
It might indicate that we're leaking async search results on the ES side, so it could become a performance or stability problem. I'll investigate further. |
afgomez
left a comment
There was a problem hiding this comment.
Although I couldn't take a deep look at all the RxJS-related code, I feel comfortable approving the PR to move forward since the code works as intended.
I can take a look later at the code and write a proposal with changes if I find anything :)
Good job. LGTM!
💚 Build SucceededMetrics [docs]Module Count
Async chunks
Distributable file count
Page load bundle
History
To update your PR or re-run it, just comment with: |
Summary
This adds helper React hooks for interacting with
dataplugin search strategies. Using these hooks the log entry fly-out now indicates the loading progress, shows errors and cancels requests when closed.closes #78003
Release Notes
Displays progress for asynchronous loading of the log entry fly-out content and cancels pendig requests when closing the fly-out.
Review and implementation notes
The log entry fly-out structure has been cleaned up to remove timing issues and avoid eager fetching.
The fly-out now sports a loading progress bar with the option to cancel the running request. The request is also canceled when the fly-out is closed while loading is in progress. The cancellation can take the form of a canceled request if one is pending. Otherwise it just means that no further polling of the async results is performed.
To achieve that I'm tried to build a toolbox of hooks and components that can be used in future async search requests as well:
The producer hook
useDataSearchspawns new requests usingdata.search.searchand returns an observable that emits an object describing each new request. The response observables associated with each request are amended such that unsubscribing from them leads to cancellation of pending requests and polling cycles.The consumer hook
useLatestPartialDataSearchRequestconsumes that observable and subscribes to each request's response observable in a way that...Some utility hooks form the glue between observables and the react rendering cycle. Among them are
useObservableto create stablepipe()s,useSubscriptionto tie a subscription to the component life-cycle anduseObservableStateto turn an observable's emitted values into React component state.Several new "data search" stories in the
infrastorybook showcase some of the components used in the fly-out. They consist of a progress bar, an error call-out and fly-out layout helpers.I tried to expand on the above description in a MDX storybook, which can be built using
yarn storybook infra.Previews
Display progress based on the searched/total number of shards:
Display errors that occur during loading without partial results available: (this weird error message is caused by the chrome devtools request blocking feature and will differ depending on the cause of the failure)
Display a mixture of partial results and shard failures:
Task breakdown