Skip to content

Commit

Permalink
[Profiling] Auto-abort requests (#142750) (#142817)
Browse files Browse the repository at this point in the history
(cherry picked from commit c05190e)

Co-authored-by: Dario Gieselaar <[email protected]>
  • Loading branch information
kibanamachine and dgieselaar authored Oct 6, 2022
1 parent 195c262 commit 237173d
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 134 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,35 +43,40 @@ export function FlameGraphsView({ children }: { children: React.ReactElement })
services: { fetchElasticFlamechart },
} = useProfilingDependencies();

const state = useTimeRangeAsync(() => {
return Promise.all([
fetchElasticFlamechart({
timeFrom: new Date(timeRange.start).getTime() / 1000,
timeTo: new Date(timeRange.end).getTime() / 1000,
kuery,
}),
comparisonTimeRange.start && comparisonTimeRange.end
? fetchElasticFlamechart({
timeFrom: new Date(comparisonTimeRange.start).getTime() / 1000,
timeTo: new Date(comparisonTimeRange.end).getTime() / 1000,
kuery: comparisonKuery,
})
: Promise.resolve(undefined),
]).then(([primaryFlamegraph, comparisonFlamegraph]) => {
return {
primaryFlamegraph,
comparisonFlamegraph,
};
});
}, [
timeRange.start,
timeRange.end,
kuery,
comparisonTimeRange.start,
comparisonTimeRange.end,
comparisonKuery,
fetchElasticFlamechart,
]);
const state = useTimeRangeAsync(
({ http }) => {
return Promise.all([
fetchElasticFlamechart({
http,
timeFrom: new Date(timeRange.start).getTime() / 1000,
timeTo: new Date(timeRange.end).getTime() / 1000,
kuery,
}),
comparisonTimeRange.start && comparisonTimeRange.end
? fetchElasticFlamechart({
http,
timeFrom: new Date(comparisonTimeRange.start).getTime() / 1000,
timeTo: new Date(comparisonTimeRange.end).getTime() / 1000,
kuery: comparisonKuery,
})
: Promise.resolve(undefined),
]).then(([primaryFlamegraph, comparisonFlamegraph]) => {
return {
primaryFlamegraph,
comparisonFlamegraph,
};
});
},
[
timeRange.start,
timeRange.end,
kuery,
comparisonTimeRange.start,
comparisonTimeRange.end,
comparisonKuery,
fetchElasticFlamechart,
]
);

const { data } = state;

Expand Down Expand Up @@ -173,7 +178,6 @@ export function FlameGraphsView({ children }: { children: React.ReactElement })
<AsyncComponent {...state} style={{ height: '100%' }} size="xl">
<FlameGraph
id="flamechart"
height={'100%'}
primaryFlamegraph={data?.primaryFlamegraph}
comparisonFlamegraph={data?.comparisonFlamegraph}
comparisonMode={comparisonMode}
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/profiling/public/components/flamegraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,13 @@ function FlameGraphTooltip({

export interface FlameGraphProps {
id: string;
height: number | string;
comparisonMode: FlameGraphComparisonMode;
primaryFlamegraph?: ElasticFlameGraph;
comparisonFlamegraph?: ElasticFlameGraph;
}

export const FlameGraph: React.FC<FlameGraphProps> = ({
id,
height,
comparisonMode,
primaryFlamegraph,
comparisonFlamegraph,
Expand Down
50 changes: 29 additions & 21 deletions x-pack/plugins/profiling/public/components/functions_view/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,36 @@ export function FunctionsView({ children }: { children: React.ReactElement }) {
services: { fetchTopNFunctions },
} = useProfilingDependencies();

const state = useTimeRangeAsync(() => {
return fetchTopNFunctions({
timeFrom: new Date(timeRange.start).getTime() / 1000,
timeTo: new Date(timeRange.end).getTime() / 1000,
startIndex: 0,
endIndex: 1000,
kuery,
});
}, [timeRange.start, timeRange.end, kuery, fetchTopNFunctions]);
const state = useTimeRangeAsync(
({ http }) => {
return fetchTopNFunctions({
http,
timeFrom: new Date(timeRange.start).getTime() / 1000,
timeTo: new Date(timeRange.end).getTime() / 1000,
startIndex: 0,
endIndex: 1000,
kuery,
});
},
[timeRange.start, timeRange.end, kuery, fetchTopNFunctions]
);

const comparisonState = useTimeRangeAsync(() => {
if (!comparisonTimeRange.start || !comparisonTimeRange.end) {
return undefined;
}
return fetchTopNFunctions({
timeFrom: new Date(comparisonTimeRange.start).getTime() / 1000,
timeTo: new Date(comparisonTimeRange.end).getTime() / 1000,
startIndex: 0,
endIndex: 1000,
kuery: comparisonKuery,
});
}, [comparisonTimeRange.start, comparisonTimeRange.end, comparisonKuery, fetchTopNFunctions]);
const comparisonState = useTimeRangeAsync(
({ http }) => {
if (!comparisonTimeRange.start || !comparisonTimeRange.end) {
return undefined;
}
return fetchTopNFunctions({
http,
timeFrom: new Date(comparisonTimeRange.start).getTime() / 1000,
timeTo: new Date(comparisonTimeRange.end).getTime() / 1000,
startIndex: 0,
endIndex: 1000,
kuery: comparisonKuery,
});
},
[comparisonTimeRange.start, comparisonTimeRange.end, comparisonKuery, fetchTopNFunctions]
);

const routePath = useProfilingRoutePath() as
| '/functions'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,29 +50,33 @@ export function StackTracesView() {
rangeTo,
});

const state = useTimeRangeAsync(() => {
if (!topNType) {
return Promise.resolve({ charts: [], metadata: {} });
}
return fetchTopN({
type: topNType,
timeFrom: new Date(timeRange.start).getTime() / 1000,
timeTo: new Date(timeRange.end).getTime() / 1000,
kuery,
}).then((response: TopNResponse) => {
const totalCount = response.TotalCount;
const samples = response.TopN;
const charts = groupSamplesByCategory({
samples,
totalCount,
metadata: response.Metadata,
labels: response.Labels,
const state = useTimeRangeAsync(
({ http }) => {
if (!topNType) {
return Promise.resolve({ charts: [], metadata: {} });
}
return fetchTopN({
http,
type: topNType,
timeFrom: new Date(timeRange.start).getTime() / 1000,
timeTo: new Date(timeRange.end).getTime() / 1000,
kuery,
}).then((response: TopNResponse) => {
const totalCount = response.TotalCount;
const samples = response.TopN;
const charts = groupSamplesByCategory({
samples,
totalCount,
metadata: response.Metadata,
labels: response.Labels,
});
return {
charts,
};
});
return {
charts,
};
});
}, [topNType, timeRange.start, timeRange.end, fetchTopN, kuery]);
},
[topNType, timeRange.start, timeRange.end, fetchTopN, kuery]
);

const [highlightedSubchart, setHighlightedSubchart] = useState<TopNSubchart | undefined>(
undefined
Expand Down
56 changes: 52 additions & 4 deletions x-pack/plugins/profiling/public/hooks/use_async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { HttpStart } from '@kbn/core-http-browser';
import { useEffect, useState } from 'react';
import { HttpFetchOptions, HttpHandler, HttpStart } from '@kbn/core-http-browser';
import { AbortError } from '@kbn/kibana-utils-plugin/common';
import { useEffect, useRef, useState } from 'react';
import { Overwrite, ValuesType } from 'utility-types';
import { useProfilingDependencies } from '../components/contexts/profiling_dependencies/use_profiling_dependencies';

export enum AsyncStatus {
Expand All @@ -20,8 +22,22 @@ export interface AsyncState<T> {
status: AsyncStatus;
}

const HTTP_METHODS = ['fetch', 'get', 'post', 'put', 'delete', 'patch'] as const;

type HttpMethod = ValuesType<typeof HTTP_METHODS>;

type AutoAbortedHttpMethod = (
path: string,
options: Omit<HttpFetchOptions, 'signal'>
) => ReturnType<HttpHandler>;

export type AutoAbortedHttpService = Overwrite<
HttpStart,
Record<HttpMethod, AutoAbortedHttpMethod>
>;

export type UseAsync = <T>(
fn: ({ http }: { http: HttpStart }) => Promise<T> | undefined,
fn: ({ http }: { http: AutoAbortedHttpService }) => Promise<T> | undefined,
dependencies: any[]
) => AsyncState<T>;

Expand All @@ -37,8 +53,30 @@ export const useAsync: UseAsync = (fn, dependencies) => {

const { data, error } = asyncState;

const controllerRef = useRef(new AbortController());

useEffect(() => {
const returnValue = fn({ http });
controllerRef.current.abort();

controllerRef.current = new AbortController();

const autoAbortedMethods = {} as Record<HttpMethod, AutoAbortedHttpMethod>;

for (const key of HTTP_METHODS) {
autoAbortedMethods[key] = (path, options) => {
return http[key](path, { ...options, signal: controllerRef.current.signal }).catch(
(err) => {
if (err.name === 'AbortError') {
// return never-resolving promise
return new Promise(() => {});
}
throw err;
}
);
};
}

const returnValue = fn({ http: { ...http, ...autoAbortedMethods } });

if (returnValue === undefined) {
setAsyncState({
Expand All @@ -63,13 +101,23 @@ export const useAsync: UseAsync = (fn, dependencies) => {
});

returnValue.catch((nextError) => {
if (nextError instanceof AbortError) {
return;
}
setAsyncState({
status: AsyncStatus.Settled,
error: nextError,
});
throw nextError;
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [http, ...dependencies]);

useEffect(() => {
return () => {
controllerRef.current.abort();
};
}, []);

return asyncState;
};
2 changes: 1 addition & 1 deletion x-pack/plugins/profiling/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class ProfilingPlugin implements Plugin {
unknown
];

const profilingFetchServices = getServices(coreStart);
const profilingFetchServices = getServices();
const { renderApp } = await import('./app');

function pushKueryToSubject(location: Location) {
Expand Down
Loading

0 comments on commit 237173d

Please sign in to comment.