@@ -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+
142148interface 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<
524544export 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} ;
0 commit comments