Logs: Cancel log volume query when the main query is canceled (#41856)

* Cancel log volume query when the main query is canceled

* Test keeping complete log volume data when main query is canceled
This commit is contained in:
Piotr Jamróz 2021-11-24 15:34:19 +01:00 committed by GitHub
parent 177a0d69d1
commit 4e35d35e67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 80 additions and 23 deletions

View File

@ -3,12 +3,14 @@ import {
addResultsToCache,
cancelQueries,
cancelQueriesAction,
cleanLogsVolumeAction,
clearCache,
importQueries,
queryReducer,
runQueries,
scanStartAction,
scanStopAction,
storeLogsVolumeDataProviderAction,
} from './query';
import { ExploreId, ExploreItemState, StoreState, ThunkDispatch } from 'app/types';
import { interval, Observable, of } from 'rxjs';
@ -127,7 +129,12 @@ describe('running queries', () => {
.givenThunk(cancelQueries)
.whenThunkIsDispatched(exploreId);
expect(dispatchedActions).toEqual([scanStopAction({ exploreId }), cancelQueriesAction({ exploreId })]);
expect(dispatchedActions).toEqual([
scanStopAction({ exploreId }),
cancelQueriesAction({ exploreId }),
storeLogsVolumeDataProviderAction({ exploreId, logsVolumeDataProvider: undefined }),
cleanLogsVolumeAction({ exploreId }),
]);
});
});
@ -309,18 +316,24 @@ describe('reducer', () => {
});
});
describe('logs volume', () => {
describe('log volume', () => {
let dispatch: ThunkDispatch,
getState: () => StoreState,
unsubscribes: Function[],
mockLogsVolumeDataProvider: () => Observable<DataQueryResponse>;
beforeEach(() => {
unsubscribes = [];
mockLogsVolumeDataProvider = () => {
return of(
{ state: LoadingState.Loading, error: undefined, data: [] },
{ state: LoadingState.Done, error: undefined, data: [{}] }
);
return ({
subscribe: () => {
const unsubscribe = jest.fn();
unsubscribes.push(unsubscribe);
return {
unsubscribe,
};
},
} as unknown) as Observable<DataQueryResponse>;
};
const store: { dispatch: ThunkDispatch; getState: () => StoreState } = configureStore({
@ -346,22 +359,9 @@ describe('reducer', () => {
getState = store.getState;
setupQueryResponse(getState());
unsubscribes = [];
mockLogsVolumeDataProvider = () => {
return ({
subscribe: () => {
const unsubscribe = jest.fn();
unsubscribes.push(unsubscribe);
return {
unsubscribe,
};
},
} as unknown) as Observable<DataQueryResponse>;
};
});
it('should cancel any unfinished logs volume queries', async () => {
it('should cancel any unfinished logs volume queries when a new query is run', async () => {
await dispatch(runQueries(ExploreId.left));
// first query is run automatically
// loading in progress - one subscription created, not cleaned up yet
@ -377,9 +377,56 @@ describe('reducer', () => {
expect(unsubscribes[1]).not.toBeCalled();
});
it('should cancel log volume query when the main query is canceled', async () => {
await dispatch(runQueries(ExploreId.left));
expect(unsubscribes).toHaveLength(1);
expect(unsubscribes[0]).not.toBeCalled();
await dispatch(cancelQueries(ExploreId.left));
expect(unsubscribes).toHaveLength(1);
expect(unsubscribes[0]).toBeCalled();
expect(getState().explore[ExploreId.left].logsVolumeData).toBeUndefined();
expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeUndefined();
});
it('should load logs volume after running the query', async () => {
await dispatch(runQueries(ExploreId.left));
expect(unsubscribes).toHaveLength(1);
});
it('should clean any incomplete log volume data when main query is canceled', async () => {
mockLogsVolumeDataProvider = () => {
return of({ state: LoadingState.Loading, error: undefined, data: [] });
};
await dispatch(runQueries(ExploreId.left));
expect(getState().explore[ExploreId.left].logsVolumeData).toBeDefined();
expect(getState().explore[ExploreId.left].logsVolumeData!.state).toBe(LoadingState.Loading);
expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeDefined();
await dispatch(cancelQueries(ExploreId.left));
expect(getState().explore[ExploreId.left].logsVolumeData).toBeUndefined();
expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeUndefined();
});
it('keeps complete log volume data when main query is canceled', async () => {
mockLogsVolumeDataProvider = () => {
return of(
{ state: LoadingState.Loading, error: undefined, data: [] },
{ state: LoadingState.Done, error: undefined, data: [{}] }
);
};
await dispatch(runQueries(ExploreId.left));
expect(getState().explore[ExploreId.left].logsVolumeData).toBeDefined();
expect(getState().explore[ExploreId.left].logsVolumeData!.state).toBe(LoadingState.Done);
expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeDefined();
await dispatch(cancelQueries(ExploreId.left));
expect(getState().explore[ExploreId.left].logsVolumeData).toBeDefined();
expect(getState().explore[ExploreId.left].logsVolumeData!.state).toBe(LoadingState.Done);
expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeUndefined();
});
});
});

View File

@ -110,11 +110,11 @@ export interface StoreLogsVolumeDataProvider {
/**
* Stores available logs volume provider after running the query. Used internally by runQueries().
*/
const storeLogsVolumeDataProviderAction = createAction<StoreLogsVolumeDataProvider>(
export const storeLogsVolumeDataProviderAction = createAction<StoreLogsVolumeDataProvider>(
'explore/storeLogsVolumeDataProviderAction'
);
const cleanLogsVolumeAction = createAction<{ exploreId: ExploreId }>('explore/cleanLogsVolumeAction');
export const cleanLogsVolumeAction = createAction<{ exploreId: ExploreId }>('explore/cleanLogsVolumeAction');
export interface StoreLogsVolumeDataSubscriptionPayload {
exploreId: ExploreId;
@ -221,9 +221,19 @@ export function addQueryRow(exploreId: ExploreId, index: number): ThunkResult<vo
* Cancel running queries
*/
export function cancelQueries(exploreId: ExploreId): ThunkResult<void> {
return (dispatch) => {
return (dispatch, getState) => {
dispatch(scanStopAction({ exploreId }));
dispatch(cancelQueriesAction({ exploreId }));
dispatch(
storeLogsVolumeDataProviderAction({
exploreId,
logsVolumeDataProvider: undefined,
})
);
// clear any incomplete data
if (getState().explore[exploreId]!.logsVolumeData?.state !== LoadingState.Done) {
dispatch(cleanLogsVolumeAction({ exploreId }));
}
dispatch(stateSave());
};
}