Skip to content

Commit e0c9a0a

Browse files
committed
Add thunk action abort signal cancellation
1 parent e37fc16 commit e0c9a0a

File tree

3 files changed

+110
-3
lines changed

3 files changed

+110
-3
lines changed

packages/compass-collection/src/modules/collection-tab.ts

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ type CollectionThunkAction<R, A extends AnyAction = AnyAction> = ThunkAction<
7979
preferences: PreferencesAccess;
8080
connectionInfoRef: ConnectionInfoRef;
8181
fakerSchemaGenerationAbortControllerRef: { current?: AbortController };
82+
schemaAnalysisAbortControllerRef: { current?: AbortController };
8283
},
8384
A
8485
>;
@@ -101,6 +102,7 @@ export enum CollectionActions {
101102
SchemaAnalysisStarted = 'compass-collection/SchemaAnalysisStarted',
102103
SchemaAnalysisFinished = 'compass-collection/SchemaAnalysisFinished',
103104
SchemaAnalysisFailed = 'compass-collection/SchemaAnalysisFailed',
105+
SchemaAnalysisCanceled = 'compass-collection/SchemaAnalysisCanceled',
104106
SchemaAnalysisReset = 'compass-collection/SchemaAnalysisReset',
105107
MockDataGeneratorModalOpened = 'compass-collection/MockDataGeneratorModalOpened',
106108
MockDataGeneratorModalClosed = 'compass-collection/MockDataGeneratorModalClosed',
@@ -139,6 +141,10 @@ interface SchemaAnalysisFailedAction {
139141
error: Error;
140142
}
141143

144+
interface SchemaAnalysisCanceledAction {
145+
type: CollectionActions.SchemaAnalysisCanceled;
146+
}
147+
142148
interface MockDataGeneratorModalOpenedAction {
143149
type: CollectionActions.MockDataGeneratorModalOpened;
144150
}
@@ -263,6 +269,20 @@ const reducer: Reducer<CollectionState, Action> = (
263269
};
264270
}
265271

272+
if (
273+
isAction<SchemaAnalysisCanceledAction>(
274+
action,
275+
CollectionActions.SchemaAnalysisCanceled
276+
)
277+
) {
278+
return {
279+
...state,
280+
schemaAnalysis: {
281+
status: SCHEMA_ANALYSIS_STATE_INITIAL,
282+
},
283+
};
284+
}
285+
266286
if (
267287
isAction<MockDataGeneratorModalOpenedAction>(
268288
action,
@@ -524,7 +544,11 @@ export const openMockDataGeneratorModal = (): CollectionThunkAction<
524544
export const analyzeCollectionSchema = (): CollectionThunkAction<
525545
Promise<void>
526546
> => {
527-
return async (dispatch, getState, { dataService, preferences, logger }) => {
547+
return async (
548+
dispatch,
549+
getState,
550+
{ dataService, preferences, logger, schemaAnalysisAbortControllerRef }
551+
) => {
528552
const { schemaAnalysis, namespace } = getState();
529553
const analysisStatus = schemaAnalysis.status;
530554
if (analysisStatus === SCHEMA_ANALYSIS_STATE_ANALYZING) {
@@ -534,6 +558,10 @@ export const analyzeCollectionSchema = (): CollectionThunkAction<
534558
return;
535559
}
536560

561+
// Create abort controller for this analysis
562+
const abortController = new AbortController();
563+
schemaAnalysisAbortControllerRef.current = abortController;
564+
537565
try {
538566
logger.debug('Schema analysis started.');
539567

@@ -552,8 +580,15 @@ export const analyzeCollectionSchema = (): CollectionThunkAction<
552580
driverOptions,
553581
{
554582
fallbackReadPreference: 'secondaryPreferred',
583+
abortSignal: abortController.signal,
555584
}
556585
);
586+
587+
// Check if analysis was aborted after sampling
588+
if (abortController.signal.aborted) {
589+
logger.debug('Schema analysis was aborted during sampling');
590+
return;
591+
}
557592
if (sampleDocuments.length === 0) {
558593
logger.debug(NO_DOCUMENTS_ERROR);
559594
dispatch({
@@ -565,6 +600,13 @@ export const analyzeCollectionSchema = (): CollectionThunkAction<
565600

566601
// Analyze sampled documents
567602
const schemaAccessor = await analyzeDocuments(sampleDocuments);
603+
604+
// Check if analysis was aborted after document analysis
605+
if (abortController.signal.aborted) {
606+
logger.debug('Schema analysis was aborted during document analysis');
607+
return;
608+
}
609+
568610
const schema = await schemaAccessor.getInternalSchema();
569611

570612
// Filter out internal fields from the schema
@@ -583,13 +625,29 @@ export const analyzeCollectionSchema = (): CollectionThunkAction<
583625
maxNestingDepth,
584626
validationRules,
585627
};
628+
629+
// Final check before dispatching results
630+
if (abortController.signal.aborted) {
631+
logger.debug('Schema analysis was aborted before completion');
632+
return;
633+
}
634+
586635
dispatch({
587636
type: CollectionActions.SchemaAnalysisFinished,
588637
processedSchema,
589638
sampleDocument: sampleDocuments[0],
590639
schemaMetadata,
591640
});
592641
} catch (err: any) {
642+
// Check if the error is due to cancellation
643+
if (isCancelError(err) || abortController.signal.aborted) {
644+
logger.debug('Schema analysis was aborted');
645+
dispatch({
646+
type: CollectionActions.SchemaAnalysisCanceled,
647+
});
648+
return;
649+
}
650+
593651
logger.log.error(
594652
mongoLogId(1_001_000_363),
595653
'Collection',
@@ -603,6 +661,23 @@ export const analyzeCollectionSchema = (): CollectionThunkAction<
603661
type: CollectionActions.SchemaAnalysisFailed,
604662
error: err as Error,
605663
});
664+
} finally {
665+
// Clean up abort controller
666+
schemaAnalysisAbortControllerRef.current = undefined;
667+
}
668+
};
669+
};
670+
671+
export const cancelSchemaAnalysis = (): CollectionThunkAction<void> => {
672+
return (
673+
_dispatch,
674+
_getState,
675+
{ schemaAnalysisAbortControllerRef, logger }
676+
) => {
677+
if (schemaAnalysisAbortControllerRef.current) {
678+
logger.debug('Canceling schema analysis');
679+
schemaAnalysisAbortControllerRef.current.abort();
680+
schemaAnalysisAbortControllerRef.current = undefined;
606681
}
607682
};
608683
};

packages/compass-collection/src/stores/collection-tab.spec.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ describe('Collection Tab Content store', function () {
134134
logger,
135135
preferences,
136136
},
137-
{ on() {}, cleanup() {} } as any
137+
{ on() {}, cleanup() {}, addCleanup() {} } as any
138138
));
139139
await waitFor(() => {
140140
expect(store.getState())
@@ -417,4 +417,28 @@ describe('Collection Tab Content store', function () {
417417
expect(analyzeCollectionSchemaStub).to.not.have.been.called;
418418
});
419419
});
420+
421+
describe('schema analysis cancellation', function () {
422+
it('should cancel schema analysis when cancelSchemaAnalysis is dispatched', async function () {
423+
const getAssignment = sandbox.spy(() =>
424+
Promise.resolve(
425+
createMockAssignment(ExperimentTestGroup.mockDataGeneratorVariant)
426+
)
427+
);
428+
const assignExperiment = sandbox.spy(() => Promise.resolve(null));
429+
430+
const store = await configureStore(undefined, undefined, {
431+
getAssignment,
432+
assignExperiment,
433+
});
434+
435+
// Dispatch cancel action
436+
store.dispatch(collectionTabModule.cancelSchemaAnalysis() as any);
437+
438+
// Verify the state is reset to initial
439+
expect((store.getState() as any).schemaAnalysis.status).to.equal(
440+
'initial'
441+
);
442+
});
443+
});
420444
});

packages/compass-collection/src/stores/collection-tab.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import reducer, {
77
selectTab,
88
collectionMetadataFetched,
99
analyzeCollectionSchema,
10+
cancelSchemaAnalysis,
1011
} from '../modules/collection-tab';
1112
import { MockDataGeneratorStep } from '../components/mock-data-generator-modal/types';
1213

@@ -58,7 +59,7 @@ export type CollectionTabServices = {
5859
export function activatePlugin(
5960
{ namespace, editViewName, tabId }: CollectionTabOptions,
6061
services: CollectionTabServices,
61-
{ on, cleanup }: ActivateHelpers
62+
{ on, cleanup, addCleanup }: ActivateHelpers
6263
): {
6364
store: ReturnType<typeof createStore>;
6465
deactivate: () => void;
@@ -84,6 +85,9 @@ export function activatePlugin(
8485
const fakerSchemaGenerationAbortControllerRef = {
8586
current: undefined,
8687
};
88+
const schemaAnalysisAbortControllerRef = {
89+
current: undefined,
90+
};
8791
const store = createStore(
8892
reducer,
8993
{
@@ -113,6 +117,7 @@ export function activatePlugin(
113117
logger,
114118
preferences,
115119
fakerSchemaGenerationAbortControllerRef,
120+
schemaAnalysisAbortControllerRef,
116121
})
117122
)
118123
);
@@ -190,6 +195,9 @@ export function activatePlugin(
190195
}
191196
});
192197

198+
// Cancel schema analysis when plugin is deactivated
199+
addCleanup(() => store.dispatch(cancelSchemaAnalysis()));
200+
193201
return {
194202
store,
195203
deactivate: cleanup,

0 commit comments

Comments
 (0)