2525import org .apache .iotdb .db .exception .CorruptedTsFileException ;
2626import org .apache .iotdb .db .i18n .DataNodeQueryMessages ;
2727import org .apache .iotdb .db .queryengine .execution .fragment .FragmentInstanceContext ;
28+ import org .apache .iotdb .db .queryengine .execution .fragment .FragmentInstanceState ;
2829import org .apache .iotdb .db .queryengine .execution .fragment .QueryContext ;
2930import org .apache .iotdb .db .queryengine .metric .SeriesScanCostMetricSet ;
3031import org .apache .iotdb .db .queryengine .plan .analyze .cache .schema .DataNodeTTLCache ;
@@ -285,6 +286,7 @@ protected DescPriorityMergeReader getDescPriorityMergeReader() {
285286 // Optional.empty(), it needs to return directly to the checkpoint method that checks the operator
286287 // execution time slice.
287288 public Optional <Boolean > hasNextFile () throws IOException {
289+ checkFragmentInstanceState ();
288290 if (runtimeFilterExhausted || !paginationController .hasCurLimit ()) {
289291 return Optional .of (false );
290292 }
@@ -2016,8 +2018,10 @@ private void unpackAllOverlappedTsFilesToTimeSeriesMetadata(long endpointTime)
20162018 }
20172019
20182020 private Optional <ITimeSeriesMetadata > unpackSeqTsFileResource () throws IOException {
2021+ checkFragmentInstanceState ();
20192022 ITimeSeriesMetadata timeseriesMetadata =
20202023 loadTimeSeriesMetadata (orderUtils .getNextSeqFileResource (true ), true );
2024+ checkFragmentInstanceState ();
20212025 // skip if data type is mismatched which may be caused by delete
20222026 if (timeseriesMetadata != null && timeseriesMetadata .typeMatch (getTsDataTypeList ())) {
20232027 timeseriesMetadata .setSeq (true );
@@ -2029,8 +2033,10 @@ private Optional<ITimeSeriesMetadata> unpackSeqTsFileResource() throws IOExcepti
20292033 }
20302034
20312035 private Optional <ITimeSeriesMetadata > unpackUnseqTsFileResource () throws IOException {
2036+ checkFragmentInstanceState ();
20322037 ITimeSeriesMetadata timeseriesMetadata =
20332038 loadTimeSeriesMetadata (orderUtils .getNextUnseqFileResource (true ), false );
2039+ checkFragmentInstanceState ();
20342040 // skip if data type is mismatched which may be caused by delete
20352041 if (timeseriesMetadata != null && timeseriesMetadata .typeMatch (getTsDataTypeList ())) {
20362042 timeseriesMetadata .setSeq (false );
@@ -2041,6 +2047,24 @@ private Optional<ITimeSeriesMetadata> unpackUnseqTsFileResource() throws IOExcep
20412047 }
20422048 }
20432049
2050+ private void checkFragmentInstanceState () throws IOException {
2051+ // Compaction also uses this scanner, but its context has no fragment state machine.
2052+ if (context .getStateMachine () == null ) {
2053+ return ;
2054+ }
2055+ FragmentInstanceState state = context .getStateMachine ().getState ();
2056+ if (state .isDone ()) {
2057+ // A scan over many overlapping files may stay in one operator call long after cancellation.
2058+ // Exit on the driver thread so it can release its lock and finish resource cleanup.
2059+ throw new IOException (
2060+ String .format (
2061+ DataNodeQueryMessages .EXCEPTION_FRAGMENT_INSTANCE_ARG_IS_ALREADY_ARG_B44984B4 ,
2062+ context .getId (),
2063+ state ),
2064+ context .getFailureCause ().orElse (null ));
2065+ }
2066+ }
2067+
20442068 protected ITimeSeriesMetadata loadTimeSeriesMetadata (TsFileResource resource , boolean isSeq )
20452069 throws IOException {
20462070 return FileLoaderUtils .loadTimeSeriesMetadata (
0 commit comments