This is an automated email from the ASF dual-hosted git repository.

jt2594838 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new db8bc17a27f [Feature] Expose DROP TABLE mod file progress (#18855)
db8bc17a27f is described below

commit db8bc17a27f0ab9927f52b37199b7e7e740fb997
Author: Caideyipi <[email protected]>
AuthorDate: Thu Oct 15 10:32:55 2026 +0800

    [Feature] Expose DROP TABLE mod file progress (#18855)
    
    * [Feature] Expose DROP TABLE mod file progress
    
    * [Fix] Wait for in-flight DROP TABLE mod progress refresh
---
 .../it/schema/IoTDBDropTableModProgressIT.java     | 105 ++++++++++++
 .../iotdb/relational/it/schema/IoTDBTableIT.java   |   7 +-
 .../client/async/CnToDnAsyncRequestType.java       |   1 +
 .../CnToDnInternalServiceAsyncRequestManager.java  |   7 +
 .../rpc/DataNodeAsyncRequestRPCHandler.java        |   9 ++
 .../rpc/GetDropTableModProgressRPCHandler.java     |  59 +++++++
 .../iotdb/confignode/manager/ProcedureManager.java |  11 ++
 .../impl/schema/table/DropTableProcedure.java      | 178 ++++++++++++++++++++-
 .../impl/schema/table/DropTableProcedureTest.java  | 156 ++++++++++++++++++
 .../impl/DataNodeInternalRPCServiceImpl.java       |  28 +++-
 .../InformationSchemaContentSupplierFactory.java   |  10 ++
 .../db/storageengine/dataregion/DataRegion.java    |  39 ++++-
 .../modification/DropTableModProgress.java         |  65 ++++++++
 .../node/write/RelationalDeleteDataNodeTest.java   |  22 +++
 .../storageengine/dataregion/DataRegionTest.java   | 154 +++++++++++++++++-
 .../schema/column/ColumnHeaderConstant.java        |   2 +
 .../commons/schema/table/InformationSchema.java    |   6 +
 .../src/main/thrift/confignode.thrift              |   2 +
 .../src/main/thrift/datanode.thrift                |  19 +++
 19 files changed, 870 insertions(+), 10 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDropTableModProgressIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDropTableModProgressIT.java
new file mode 100644
index 00000000000..fe00da05a05
--- /dev/null
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDropTableModProgressIT.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.relational.it.schema;
+
+import org.apache.iotdb.db.it.utils.TestUtils;
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.TableClusterIT;
+import org.apache.iotdb.itbase.category.TableLocalStandaloneIT;
+import org.apache.iotdb.itbase.env.BaseEnv;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+import static org.awaitility.Awaitility.await;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({TableLocalStandaloneIT.class, TableClusterIT.class})
+public class IoTDBDropTableModProgressIT {
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    EnvFactory.getEnv()
+        .getConfig()
+        .getCommonConfig()
+        .setEnforceStrongPassword(false)
+        .setDataRegionGroupExtensionPolicy("CUSTOM")
+        .setDefaultDataRegionGroupNumPerDatabase(1);
+    EnvFactory.getEnv().initClusterEnvironment();
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    EnvFactory.getEnv().cleanClusterEnvironment();
+  }
+
+  @Test
+  public void testDropTableModFileProgress() throws Exception {
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      statement.execute("create database async_drop_mod_db");
+      statement.execute("use async_drop_mod_db");
+      statement.execute("create table drop_target (device string tag, reading 
int32)");
+      statement.execute("create table retained (device string tag, reading 
int32)");
+      // One region ensures both tables share a TsFile. Dropping just one 
table must write a mod
+      // file rather than delete that TsFile outright, and replicas must not 
multiply the count.
+      statement.execute("insert into drop_target(time, device, reading) values 
(1, 'd1', 10)");
+      statement.execute("insert into retained(time, device, reading) values 
(1, 'd1', 20)");
+      statement.execute("flush");
+      statement.execute("drop table drop_target");
+      await()
+          .atMost(60, TimeUnit.SECONDS)
+          .untilAsserted(
+              () -> {
+                try (final ResultSet resultSet =
+                    statement.executeQuery(
+                        "select state, written_mod_file_count, 
remaining_mod_file_count "
+                            + "from information_schema.drop_table_procedures "
+                            + "where database = 'async_drop_mod_db' and 
table_name = 'drop_target'")) {
+                  assertTrue(resultSet.next());
+                  assertEquals("SUCCESS", resultSet.getString(1));
+                  assertEquals(1, resultSet.getLong(2));
+                  assertFalse(resultSet.wasNull());
+                  assertEquals(0, resultSet.getLong(3));
+                  assertFalse(resultSet.wasNull());
+                  assertFalse(resultSet.next());
+                }
+              });
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("select device, reading from retained"),
+          "device,reading,",
+          Collections.singleton("d1,20,"));
+    }
+  }
+}
diff --git 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
index 4589ec5d49a..2b9b2d8ecce 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
@@ -1250,7 +1250,8 @@ public class IoTDBTableIT {
           Collections.emptySet());
       try (final ResultSet resultSet =
           statement.executeQuery(
-              "select procedure_id, state, progress, error_message "
+              "select procedure_id, state, progress, error_message, "
+                  + "written_mod_file_count, remaining_mod_file_count "
                   + "from information_schema.drop_table_procedures "
                   + "where database = 'async_drop_db' and table_name = 
'drop_target'")) {
         assertTrue(resultSet.next());
@@ -1258,6 +1259,10 @@ public class IoTDBTableIT {
         assertEquals("SUCCESS", resultSet.getString(2));
         assertEquals("SUCCESS", resultSet.getString(3));
         Assert.assertNull(resultSet.getString(4));
+        assertEquals(0, resultSet.getLong(5));
+        assertFalse(resultSet.wasNull());
+        assertEquals(0, resultSet.getLong(6));
+        assertFalse(resultSet.wasNull());
         assertFalse(resultSet.next());
       }
     }
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnAsyncRequestType.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnAsyncRequestType.java
index 0749ba5f9b0..3595644edf5 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnAsyncRequestType.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnAsyncRequestType.java
@@ -125,6 +125,7 @@ public enum CnToDnAsyncRequestType {
   UPDATE_TABLE,
   PRE_DELETE_TABLE,
   DELETE_DATA_FOR_DROP_TABLE,
+  GET_DROP_TABLE_MOD_PROGRESS,
   DELETE_DEVICES_FOR_DROP_TABLE,
   INVALIDATE_COLUMN_CACHE,
   DELETE_COLUMN_DATA,
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnInternalServiceAsyncRequestManager.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnInternalServiceAsyncRequestManager.java
index e73573ea0e8..3010f8b524c 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnInternalServiceAsyncRequestManager.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnInternalServiceAsyncRequestManager.java
@@ -39,6 +39,7 @@ import 
org.apache.iotdb.confignode.client.async.handlers.rpc.DataNodeAsyncReques
 import 
org.apache.iotdb.confignode.client.async.handlers.rpc.DataNodeTSStatusRPCHandler;
 import 
org.apache.iotdb.confignode.client.async.handlers.rpc.FetchSchemaBlackListRPCHandler;
 import 
org.apache.iotdb.confignode.client.async.handlers.rpc.GetBuiltInExternalServiceRPCHandler;
+import 
org.apache.iotdb.confignode.client.async.handlers.rpc.GetDropTableModProgressRPCHandler;
 import 
org.apache.iotdb.confignode.client.async.handlers.rpc.PipeHeartbeatRPCHandler;
 import 
org.apache.iotdb.confignode.client.async.handlers.rpc.PipePushMetaRPCHandler;
 import 
org.apache.iotdb.confignode.client.async.handlers.rpc.SchemaUpdateRPCHandler;
@@ -78,6 +79,7 @@ import 
org.apache.iotdb.mpp.rpc.thrift.TDropFunctionInstanceReq;
 import org.apache.iotdb.mpp.rpc.thrift.TDropPipePluginInstanceReq;
 import org.apache.iotdb.mpp.rpc.thrift.TDropTriggerInstanceReq;
 import org.apache.iotdb.mpp.rpc.thrift.TFetchSchemaBlackListReq;
+import org.apache.iotdb.mpp.rpc.thrift.TGetDropTableModProgressReq;
 import org.apache.iotdb.mpp.rpc.thrift.TInactiveTriggerInstanceReq;
 import org.apache.iotdb.mpp.rpc.thrift.TInvalidateCacheReq;
 import org.apache.iotdb.mpp.rpc.thrift.TInvalidateColumnCacheReq;
@@ -457,6 +459,11 @@ public class CnToDnInternalServiceAsyncRequestManager
         (req, client, handler) ->
             client.invalidateTableCache(
                 (TInvalidateTableCacheReq) req, (DataNodeTSStatusRPCHandler) 
handler));
+    actionMapBuilder.put(
+        CnToDnAsyncRequestType.GET_DROP_TABLE_MOD_PROGRESS,
+        (req, client, handler) ->
+            client.getDropTableModProgress(
+                (TGetDropTableModProgressReq) req, 
(GetDropTableModProgressRPCHandler) handler));
     actionMapBuilder.put(
         CnToDnAsyncRequestType.DELETE_DATA_FOR_DROP_TABLE,
         (req, client, handler) ->
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/DataNodeAsyncRequestRPCHandler.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/DataNodeAsyncRequestRPCHandler.java
index 7733d1f145a..cf4accb2f11 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/DataNodeAsyncRequestRPCHandler.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/DataNodeAsyncRequestRPCHandler.java
@@ -36,6 +36,7 @@ import 
org.apache.iotdb.mpp.rpc.thrift.TCheckTimeSeriesExistenceResp;
 import org.apache.iotdb.mpp.rpc.thrift.TCountPathsUsingTemplateResp;
 import org.apache.iotdb.mpp.rpc.thrift.TDeviceViewResp;
 import org.apache.iotdb.mpp.rpc.thrift.TFetchSchemaBlackListResp;
+import org.apache.iotdb.mpp.rpc.thrift.TGetDropTableModProgressResp;
 import org.apache.iotdb.mpp.rpc.thrift.TPullCommitProgressResp;
 import org.apache.iotdb.mpp.rpc.thrift.TPushConsumerGroupMetaResp;
 import org.apache.iotdb.mpp.rpc.thrift.TPushPipeMetaResp;
@@ -82,6 +83,14 @@ public abstract class 
DataNodeAsyncRequestRPCHandler<Response>
     Map<Integer, ?> responseMap = context.getResponseMap();
     CountDownLatch countDownLatch = context.getCountDownLatch();
     switch (requestType) {
+      case GET_DROP_TABLE_MOD_PROGRESS:
+        return new GetDropTableModProgressRPCHandler(
+            requestType,
+            requestId,
+            targetDataNode,
+            dataNodeLocationMap,
+            (Map<Integer, TGetDropTableModProgressResp>) responseMap,
+            countDownLatch);
       case CONSTRUCT_SCHEMA_BLACK_LIST:
       case ROLLBACK_SCHEMA_BLACK_LIST:
       case DELETE_DATA_FOR_DELETE_SCHEMA:
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/GetDropTableModProgressRPCHandler.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/GetDropTableModProgressRPCHandler.java
new file mode 100644
index 00000000000..60eebcae2cf
--- /dev/null
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/rpc/GetDropTableModProgressRPCHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.confignode.client.async.handlers.rpc;
+
+import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation;
+import org.apache.iotdb.confignode.client.async.CnToDnAsyncRequestType;
+import org.apache.iotdb.mpp.rpc.thrift.TGetDropTableModProgressResp;
+import org.apache.iotdb.rpc.RpcUtils;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+public class GetDropTableModProgressRPCHandler
+    extends DataNodeAsyncRequestRPCHandler<TGetDropTableModProgressResp> {
+
+  public GetDropTableModProgressRPCHandler(
+      CnToDnAsyncRequestType requestType,
+      int requestId,
+      TDataNodeLocation targetNode,
+      Map<Integer, TDataNodeLocation> nodeLocationMap,
+      Map<Integer, TGetDropTableModProgressResp> responseMap,
+      CountDownLatch countDownLatch) {
+    super(requestType, requestId, targetNode, nodeLocationMap, responseMap, 
countDownLatch);
+  }
+
+  @Override
+  public void onComplete(final TGetDropTableModProgressResp response) {
+    responseMap.put(requestId, response);
+    nodeLocationMap.remove(requestId);
+    countDownLatch.countDown();
+  }
+
+  @Override
+  public void onError(final Exception e) {
+    responseMap.put(
+        requestId,
+        new TGetDropTableModProgressResp(
+            RpcUtils.getStatus(TSStatusCode.CAN_NOT_CONNECT_DATANODE)));
+    countDownLatch.countDown();
+  }
+}
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java
index b9b84f20b65..18f65a98481 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java
@@ -171,6 +171,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TSubscribeReq;
 import org.apache.iotdb.confignode.rpc.thrift.TUnsubscribeReq;
 import org.apache.iotdb.consensus.ConsensusFactory;
 import org.apache.iotdb.db.exception.BatchProcessException;
+import org.apache.iotdb.mpp.rpc.thrift.TDropTableModProgress;
 import org.apache.iotdb.rpc.RpcUtils;
 import org.apache.iotdb.rpc.TSStatusCode;
 import org.apache.iotdb.rpc.subscription.config.TopicConfig;
@@ -2549,6 +2550,9 @@ public class ProcedureManager {
                     && !(procedure instanceof DropViewProcedure))
         .map(procedure -> (DropTableProcedure) procedure)
         .forEach(procedure -> dropProcedures.put(procedure.getProcId(), 
procedure));
+    dropProcedures.values().stream()
+        .filter(procedure -> !procedure.isFinished())
+        .forEach(DropTableProcedure::updateModFileProgress);
     return dropProcedures.values().stream()
         .map(
             procedure -> {
@@ -2559,6 +2563,13 @@ public class ProcedureManager {
                       procedure.getProcId(),
                       procedure.getState().name(),
                       procedure.getDropProgress());
+              final TDropTableModProgress modProgress = 
procedure.getModFileProgress();
+              if (modProgress != null) {
+                
info.setWrittenModFileCount(modProgress.getWrittenModFileCount());
+                if (modProgress.isSetRemainingModFileCount()) {
+                  
info.setRemainingModFileCount(modProgress.getRemainingModFileCount());
+                }
+              }
               if (procedure.hasException()) {
                 info.setErrorMessage(procedure.getException().getMessage());
               }
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/table/DropTableProcedure.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/table/DropTableProcedure.java
index 64ea7a04be1..cdb4d4b71c8 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/table/DropTableProcedure.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/table/DropTableProcedure.java
@@ -43,6 +43,9 @@ import 
org.apache.iotdb.confignode.procedure.impl.schema.table.view.DropViewProc
 import org.apache.iotdb.confignode.procedure.state.schema.DropTableState;
 import org.apache.iotdb.confignode.procedure.store.ProcedureType;
 import org.apache.iotdb.mpp.rpc.thrift.TDeleteDataOrDevicesForDropTableReq;
+import org.apache.iotdb.mpp.rpc.thrift.TDropTableModProgress;
+import org.apache.iotdb.mpp.rpc.thrift.TGetDropTableModProgressReq;
+import org.apache.iotdb.mpp.rpc.thrift.TGetDropTableModProgressResp;
 import org.apache.iotdb.mpp.rpc.thrift.TInvalidateTableCacheReq;
 import org.apache.iotdb.rpc.TSStatusCode;
 
@@ -51,13 +54,28 @@ import org.slf4j.LoggerFactory;
 
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
 
 public class DropTableProcedure extends 
AbstractAlterOrDropTableProcedure<DropTableState> {
 
   private static final Logger LOGGER = 
LoggerFactory.getLogger(DropTableProcedure.class);
 
+  // Track the replica actually executing each region, including changes 
caused by retries.
+  // Count each region once; followers may have different local TsFiles and 
apply asynchronously.
+  private final Map<TConsensusGroupId, TDataNodeLocation> modProgressTargets =
+      new ConcurrentHashMap<>();
+  private final Object modProgressRefreshMonitor = new Object();
+  private CompletableFuture<Void> modProgressRefreshFuture;
+  private volatile int modProgressRegionCount;
+  private volatile TDropTableModProgress modFileProgress;
+  private volatile boolean modProgressFinished;
+
   public DropTableProcedure(final boolean isGeneratedByPipe) {
     super(isGeneratedByPipe);
   }
@@ -78,6 +96,121 @@ public class DropTableProcedure extends 
AbstractAlterOrDropTableProcedure<DropTa
     return state == null ? getState().name() : state.name();
   }
 
+  public TDropTableModProgress getModFileProgress() {
+    final TDropTableModProgress progress = modFileProgress;
+    return progress == null ? null : progress.deepCopy();
+  }
+
+  /** Shares an in-flight counter refresh among concurrent observability 
requests. */
+  public void updateModFileProgress() {
+    runModProgressRefresh(this::refreshModFileProgress, false);
+  }
+
+  private void runModProgressRefresh(final Runnable action, final boolean 
retryAfterWait) {
+    while (true) {
+      final CompletableFuture<Void> refreshFuture;
+      final boolean refreshOwner;
+      synchronized (modProgressRefreshMonitor) {
+        if (modProgressRefreshFuture == null) {
+          refreshFuture = new CompletableFuture<>();
+          modProgressRefreshFuture = refreshFuture;
+          refreshOwner = true;
+        } else {
+          refreshFuture = modProgressRefreshFuture;
+          refreshOwner = false;
+        }
+      }
+      if (!refreshOwner) {
+        refreshFuture.join();
+        if (retryAfterWait) {
+          continue;
+        }
+        return;
+      }
+      try {
+        action.run();
+        return;
+      } catch (final RuntimeException | Error e) {
+        refreshFuture.completeExceptionally(e);
+        throw e;
+      } finally {
+        synchronized (modProgressRefreshMonitor) {
+          if (modProgressRefreshFuture == refreshFuture) {
+            modProgressRefreshFuture = null;
+          }
+          refreshFuture.complete(null);
+        }
+      }
+    }
+  }
+
+  private void refreshModFileProgress() {
+    if (modProgressFinished
+        || isFinished()
+        || getCurrentState() != DropTableState.DELETE_DATA
+        || modProgressTargets.isEmpty()
+        || modProgressTargets.size() != modProgressRegionCount) {
+      return;
+    }
+    final DataNodeAsyncRequestContext<TGetDropTableModProgressReq, 
TGetDropTableModProgressResp>
+        requestContext =
+            new 
DataNodeAsyncRequestContext<>(CnToDnAsyncRequestType.GET_DROP_TABLE_MOD_PROGRESS);
+    final Map<Integer, List<TConsensusGroupId>> regionsByNode = new 
HashMap<>();
+    final Map<TConsensusGroupId, TDataNodeLocation> targets = new 
HashMap<>(modProgressTargets);
+    targets.forEach(
+        (region, location) -> {
+          requestContext.putNodeLocation(location.getDataNodeId(), location);
+          regionsByNode
+              .computeIfAbsent(location.getDataNodeId(), ignored -> new 
ArrayList<>())
+              .add(region);
+        });
+    for (final Map.Entry<Integer, List<TConsensusGroupId>> entry : 
regionsByNode.entrySet()) {
+      requestContext.putRequest(
+          entry.getKey(), new TGetDropTableModProgressReq(getProcId(), 
entry.getValue()));
+    }
+    CnToDnInternalServiceAsyncRequestManager.getInstance()
+        .sendAsyncRequest(requestContext, 1, 2_000L, true);
+
+    // A deletion retry may have selected a different replica while these RPCs 
were in flight.
+    if (targets.equals(modProgressTargets)) {
+      // A failed refresh is unknown, including the final refresh. Do not 
freeze stale counters
+      // with remaining files after deletion has finished.
+      modFileProgress = aggregateModFileProgress(targets, 
requestContext.getResponseMap());
+    }
+  }
+
+  static TDropTableModProgress aggregateModFileProgress(
+      final Map<TConsensusGroupId, TDataNodeLocation> targets,
+      final Map<Integer, TGetDropTableModProgressResp> responses) {
+    long written = 0;
+    long remaining = 0;
+    boolean hasUnknown = false;
+    for (final Map.Entry<TConsensusGroupId, TDataNodeLocation> target : 
targets.entrySet()) {
+      final TGetDropTableModProgressResp response =
+          responses.get(target.getValue().getDataNodeId());
+      if (response == null
+          || response.getStatus().getCode() != 
TSStatusCode.SUCCESS_STATUS.getStatusCode()
+          || !response.isSetRegionProgress()
+          || 
!response.getRegionProgress().containsKey(target.getKey().getId())) {
+        // Unknown is different from zero. In particular, an old DataNode does 
not report counts.
+        return null;
+      }
+      final TDropTableModProgress progress =
+          response.getRegionProgress().get(target.getKey().getId());
+      written += progress.getWrittenModFileCount();
+      if (progress.isSetRemainingModFileCount()) {
+        remaining += progress.getRemainingModFileCount();
+      } else {
+        hasUnknown = true;
+      }
+    }
+    final TDropTableModProgress progress = new TDropTableModProgress(written);
+    if (!hasUnknown) {
+      progress.setRemainingModFileCount(remaining);
+    }
+    return progress;
+  }
+
   @Override
   protected String getActionMessage() {
     return "drop table";
@@ -189,18 +322,33 @@ public class DropTableProcedure extends 
AbstractAlterOrDropTableProcedure<DropTa
         env.getConfigManager().getRelatedDataRegionGroup4TableModel(database);
 
     if (!relatedDataRegionGroup.isEmpty()) {
+      modProgressRegionCount = relatedDataRegionGroup.size();
       new TableRegionTaskExecutor<>(
               "delete data for drop table",
               env,
               relatedDataRegionGroup,
               CnToDnAsyncRequestType.DELETE_DATA_FOR_DROP_TABLE,
-              ((dataNodeLocation, consensusGroupIdList) ->
-                  new TDeleteDataOrDevicesForDropTableReq(
-                      new ArrayList<>(consensusGroupIdList), tableName)))
+              ((dataNodeLocation, consensusGroupIdList) -> {
+                consensusGroupIdList.forEach(
+                    region -> modProgressTargets.put(region, 
dataNodeLocation));
+                return new TDeleteDataOrDevicesForDropTableReq(
+                        new ArrayList<>(consensusGroupIdList), tableName)
+                    .setProcedureId(getProcId());
+              }))
           .execute();
+      // Wait for an in-flight query, then publish the final snapshot 
atomically with the state
+      // transition so a query cannot overwrite it after deletion finishes.
+      runModProgressRefresh(
+          () -> {
+            refreshModFileProgress();
+            modProgressFinished = true;
+            setNextState(DropTableState.DELETE_DEVICES);
+          },
+          true);
+    } else {
+      modFileProgress = new 
TDropTableModProgress(0).setRemainingModFileCount(0);
+      setNextState(DropTableState.DELETE_DEVICES);
     }
-
-    setNextState(DropTableState.DELETE_DEVICES);
   }
 
   private void deleteSchema(final ConfigNodeProcedureEnv env) {
@@ -280,6 +428,26 @@ public class DropTableProcedure extends 
AbstractAlterOrDropTableProcedure<DropTa
             ? ProcedureType.PIPE_ENRICHED_DROP_TABLE_PROCEDURE.getTypeCode()
             : ProcedureType.DROP_TABLE_PROCEDURE.getTypeCode());
     super.serialize(stream);
+    final TDropTableModProgress progress = modFileProgress;
+    stream.writeBoolean(progress != null);
+    if (progress != null) {
+      stream.writeLong(progress.getWrittenModFileCount());
+      stream.writeLong(
+          progress.isSetRemainingModFileCount() ? 
progress.getRemainingModFileCount() : -1);
+    }
+  }
+
+  @Override
+  public void deserialize(final ByteBuffer buffer) {
+    super.deserialize(buffer);
+    if (!(this instanceof DropViewProcedure) && buffer.hasRemaining() && 
buffer.get() != 0) {
+      final TDropTableModProgress progress = new 
TDropTableModProgress(buffer.getLong());
+      final long remaining = buffer.getLong();
+      if (remaining >= 0) {
+        progress.setRemainingModFileCount(remaining);
+      }
+      modFileProgress = progress;
+    }
   }
 
   protected void innerSerialize(final DataOutputStream stream) throws 
IOException {
diff --git 
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/schema/table/DropTableProcedureTest.java
 
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/schema/table/DropTableProcedureTest.java
index fb141aab270..c74db40db14 100644
--- 
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/schema/table/DropTableProcedureTest.java
+++ 
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/schema/table/DropTableProcedureTest.java
@@ -19,7 +19,11 @@
 
 package org.apache.iotdb.confignode.procedure.impl.schema.table;
 
+import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId;
+import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType;
+import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation;
 import org.apache.iotdb.commons.exception.IllegalPathException;
+import org.apache.iotdb.commons.utils.StatusUtils;
 import org.apache.iotdb.confignode.procedure.NoopProcedureStore;
 import org.apache.iotdb.confignode.procedure.Procedure;
 import org.apache.iotdb.confignode.procedure.ProcedureExecutor;
@@ -28,6 +32,8 @@ import 
org.apache.iotdb.confignode.procedure.impl.schema.table.view.DropViewProc
 import org.apache.iotdb.confignode.procedure.state.schema.DropTableState;
 import org.apache.iotdb.confignode.procedure.store.ProcedureType;
 import org.apache.iotdb.confignode.procedure.util.ProcedureTestUtil;
+import org.apache.iotdb.mpp.rpc.thrift.TDropTableModProgress;
+import org.apache.iotdb.mpp.rpc.thrift.TGetDropTableModProgressResp;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -35,10 +41,13 @@ import org.junit.Test;
 import java.io.ByteArrayOutputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.lang.reflect.Field;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -225,5 +234,152 @@ public class DropTableProcedureTest {
     deserializedProcedure.deserialize(byteBuffer);
 
     Assert.assertEquals(dropTableProcedure, deserializedProcedure);
+    Assert.assertNull(deserializedProcedure.getModFileProgress());
+    Assert.assertFalse(byteBuffer.hasRemaining());
+  }
+
+  @Test
+  public void aggregateModProgressCountsEachRegionOnItsExecutingReplica() {
+    final TConsensusGroupId firstRegion = new 
TConsensusGroupId(TConsensusGroupType.DataRegion, 1);
+    final TConsensusGroupId secondRegion = new 
TConsensusGroupId(TConsensusGroupType.DataRegion, 2);
+    final Map<TConsensusGroupId, TDataNodeLocation> targets =
+        Map.of(
+            firstRegion, new TDataNodeLocation().setDataNodeId(1),
+            secondRegion, new TDataNodeLocation().setDataNodeId(2));
+    final Map<Integer, TGetDropTableModProgressResp> responses =
+        Map.of(
+            1,
+                new TGetDropTableModProgressResp(StatusUtils.OK)
+                    .setRegionProgress(
+                        Map.of(1, new 
TDropTableModProgress(2).setRemainingModFileCount(3))),
+            2,
+                new TGetDropTableModProgressResp(StatusUtils.OK)
+                    .setRegionProgress(
+                        Map.of(
+                            1, new 
TDropTableModProgress(100).setRemainingModFileCount(100),
+                            2, new 
TDropTableModProgress(5).setRemainingModFileCount(7))));
+
+    Assert.assertEquals(
+        new TDropTableModProgress(7).setRemainingModFileCount(10),
+        DropTableProcedure.aggregateModFileProgress(targets, responses));
+  }
+
+  @Test
+  public void aggregateModProgressBeforeAllRegionsStart() {
+    final Map<TConsensusGroupId, TDataNodeLocation> targets =
+        Map.of(
+            new TConsensusGroupId(TConsensusGroupType.DataRegion, 1),
+                new TDataNodeLocation().setDataNodeId(1),
+            new TConsensusGroupId(TConsensusGroupType.DataRegion, 2),
+                new TDataNodeLocation().setDataNodeId(1));
+    final Map<Integer, TGetDropTableModProgressResp> responses =
+        Map.of(
+            1,
+            new TGetDropTableModProgressResp(StatusUtils.OK)
+                .setRegionProgress(
+                    Map.of(
+                        1,
+                        new 
TDropTableModProgress(2).setRemainingModFileCount(3),
+                        2,
+                        new TDropTableModProgress(0))));
+
+    final TDropTableModProgress progress =
+        DropTableProcedure.aggregateModFileProgress(targets, responses);
+    Assert.assertEquals(2, progress.getWrittenModFileCount());
+    Assert.assertFalse(progress.isSetRemainingModFileCount());
+  }
+
+  @Test
+  public void aggregateUnavailableModProgressIsUnknown() {
+    final Map<TConsensusGroupId, TDataNodeLocation> targets =
+        Map.of(
+            new TConsensusGroupId(TConsensusGroupType.DataRegion, 1),
+            new TDataNodeLocation().setDataNodeId(1));
+    Assert.assertNull(DropTableProcedure.aggregateModFileProgress(targets, 
Collections.emptyMap()));
+    Assert.assertNull(
+        DropTableProcedure.aggregateModFileProgress(
+            targets, Map.of(1, new 
TGetDropTableModProgressResp(StatusUtils.OK))));
+    Assert.assertNull(
+        DropTableProcedure.aggregateModFileProgress(
+            targets,
+            Map.of(
+                1,
+                new TGetDropTableModProgressResp(StatusUtils.OK)
+                    .setRegionProgress(Collections.emptyMap()))));
+  }
+
+  @Test
+  public void concurrentModProgressQueriesWaitForInFlightRefresh() throws 
Exception {
+    final DropTableProcedure procedure =
+        new DropTableProcedure("database", "table", "query", false);
+    final Field futureField = 
DropTableProcedure.class.getDeclaredField("modProgressRefreshFuture");
+    futureField.setAccessible(true);
+    final CountDownLatch waitingQueryStarted = new CountDownLatch(1);
+    final CompletableFuture<Void> refreshFuture =
+        new CompletableFuture<Void>() {
+          @Override
+          public Void join() {
+            waitingQueryStarted.countDown();
+            return super.join();
+          }
+        };
+    futureField.set(procedure, refreshFuture);
+    final ExecutorService executor = Executors.newSingleThreadExecutor();
+    try {
+      final Future<?> waitingQuery = 
executor.submit(procedure::updateModFileProgress);
+      Assert.assertTrue(waitingQueryStarted.await(10, TimeUnit.SECONDS));
+      Assert.assertFalse(waitingQuery.isDone());
+      refreshFuture.complete(null);
+      waitingQuery.get(10, TimeUnit.SECONDS);
+    } finally {
+      refreshFuture.complete(null);
+      futureField.set(procedure, null);
+      executor.shutdownNow();
+      Assert.assertTrue(executor.awaitTermination(10, TimeUnit.SECONDS));
+    }
+  }
+
+  @Test
+  public void modProgressSurvivesSerialization() throws Exception {
+    for (final boolean isGeneratedByPipe : new boolean[] {false, true}) {
+      for (final TDropTableModProgress progress :
+          new TDropTableModProgress[] {
+            new TDropTableModProgress(4),
+            new TDropTableModProgress(4).setRemainingModFileCount(6),
+            new TDropTableModProgress(10).setRemainingModFileCount(0)
+          }) {
+        final DropTableProcedure procedure =
+            new DropTableProcedure("database", "table", "query", 
isGeneratedByPipe);
+        final Field progressField = 
DropTableProcedure.class.getDeclaredField("modFileProgress");
+        progressField.setAccessible(true);
+        progressField.set(procedure, progress);
+        final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        procedure.serialize(new DataOutputStream(bytes));
+        final ByteBuffer buffer = ByteBuffer.wrap(bytes.toByteArray());
+        buffer.getShort();
+        final DropTableProcedure recovered = new 
DropTableProcedure(isGeneratedByPipe);
+        recovered.deserialize(buffer);
+        Assert.assertEquals(progress, recovered.getModFileProgress());
+        Assert.assertFalse(buffer.hasRemaining());
+        // Callers cannot mutate the stored snapshot.
+        recovered.getModFileProgress().setWrittenModFileCount(0);
+        Assert.assertEquals(progress, recovered.getModFileProgress());
+      }
+    }
+  }
+
+  @Test
+  public void deserializeProcedureWithoutModProgress() throws Exception {
+    final DropTableProcedure procedure =
+        new DropTableProcedure("database", "table", "query", false);
+    final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+    // The previous procedure format ended immediately after the superclass 
fields.
+    procedure.innerSerialize(new DataOutputStream(bytes));
+    final ByteBuffer buffer = ByteBuffer.wrap(bytes.toByteArray());
+    final DropTableProcedure recovered = new DropTableProcedure(false);
+    recovered.deserialize(buffer);
+    Assert.assertEquals(procedure, recovered);
+    Assert.assertNull(recovered.getModFileProgress());
+    Assert.assertFalse(buffer.hasRemaining());
   }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
index adce7c280fc..c8f2a0820a1 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
@@ -215,6 +215,7 @@ import 
org.apache.iotdb.db.storageengine.dataregion.compaction.schedule.Compacti
 import 
org.apache.iotdb.db.storageengine.dataregion.compaction.settle.SettleRequestHandler;
 import org.apache.iotdb.db.storageengine.dataregion.flush.CompressionRatio;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.DropTableModProgress;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
 import org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate;
 import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileManager;
@@ -271,6 +272,7 @@ import org.apache.iotdb.mpp.rpc.thrift.TDeviceViewReq;
 import org.apache.iotdb.mpp.rpc.thrift.TDeviceViewResp;
 import org.apache.iotdb.mpp.rpc.thrift.TDropFunctionInstanceReq;
 import org.apache.iotdb.mpp.rpc.thrift.TDropPipePluginInstanceReq;
+import org.apache.iotdb.mpp.rpc.thrift.TDropTableModProgress;
 import org.apache.iotdb.mpp.rpc.thrift.TDropTriggerInstanceReq;
 import org.apache.iotdb.mpp.rpc.thrift.TExecuteCQ;
 import org.apache.iotdb.mpp.rpc.thrift.TFetchFragmentInstanceInfoReq;
@@ -285,6 +287,8 @@ import 
org.apache.iotdb.mpp.rpc.thrift.TGenerateDataPartitionTableHeartbeatResp;
 import org.apache.iotdb.mpp.rpc.thrift.TGenerateDataPartitionTableReq;
 import org.apache.iotdb.mpp.rpc.thrift.TGenerateDataPartitionTableResp;
 import 
org.apache.iotdb.mpp.rpc.thrift.TGetDataPartitionTableGeneratorProgressResp;
+import org.apache.iotdb.mpp.rpc.thrift.TGetDropTableModProgressReq;
+import org.apache.iotdb.mpp.rpc.thrift.TGetDropTableModProgressResp;
 import org.apache.iotdb.mpp.rpc.thrift.TGetEarliestTimeslotsResp;
 import org.apache.iotdb.mpp.rpc.thrift.TInactiveTriggerInstanceReq;
 import org.apache.iotdb.mpp.rpc.thrift.TInvalidateCacheReq;
@@ -2009,7 +2013,9 @@ public class DataNodeInternalRPCServiceImpl implements 
IDataNodeRPCService.Iface
                 .execute(
                     new DataRegionId(consensusGroupId.getId()),
                     new RelationalDeleteDataNode(
-                        new PlanNodeId(""),
+                        req.isSetProcedureId()
+                            ? 
DropTableModProgress.planNodeId(req.getProcedureId())
+                            : new PlanNodeId(""),
                         new TableDeletionEntry(
                             new DeletionPredicate(req.getTableName()),
                             new TimeRange(Long.MIN_VALUE, Long.MAX_VALUE)),
@@ -2018,6 +2024,26 @@ public class DataNodeInternalRPCServiceImpl implements 
IDataNodeRPCService.Iface
                 .getStatus());
   }
 
+  @Override
+  public TGetDropTableModProgressResp getDropTableModProgress(
+      final TGetDropTableModProgressReq req) {
+    final Map<Integer, TDropTableModProgress> progress = new HashMap<>();
+    for (final TConsensusGroupId regionId : req.getRegionIdList()) {
+      final DataRegion region =
+          StorageEngine.getInstance().getDataRegion(new 
DataRegionId(regionId.getId()));
+      if (region != null) {
+        final TDropTableModProgress regionProgress =
+            region.getDropTableModProgress(req.getProcedureId());
+        // Regions in the same deletion request execute sequentially. Report 
zero written and
+        // an unknown total until this region starts, so other regions' live 
counts are visible.
+        progress.put(
+            regionId.getId(),
+            regionProgress == null ? new TDropTableModProgress(0) : 
regionProgress);
+      }
+    }
+    return new 
TGetDropTableModProgressResp(StatusUtils.OK).setRegionProgress(progress);
+  }
+
   @Override
   public TSStatus deleteDevicesForDropTable(final 
TDeleteDataOrDevicesForDropTableReq req) {
     return executeInternalSchemaTask(
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java
index fb939fc5325..11a69b1ffca 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java
@@ -532,6 +532,16 @@ public class InformationSchemaContentSupplierFactory {
       } else {
         columnBuilders[5].appendNull();
       }
+      if (currentProcedure.isSetWrittenModFileCount()) {
+        columnBuilders[6].writeLong(currentProcedure.getWrittenModFileCount());
+      } else {
+        columnBuilders[6].appendNull();
+      }
+      if (currentProcedure.isSetRemainingModFileCount()) {
+        
columnBuilders[7].writeLong(currentProcedure.getRemainingModFileCount());
+      } else {
+        columnBuilders[7].appendNull();
+      }
       resultBuilder.declarePosition();
       currentProcedure = null;
     }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
index ead75db6bac..deaefdd2526 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
@@ -123,6 +123,7 @@ import 
org.apache.iotdb.db.storageengine.dataregion.flush.TsFileFlushPolicy;
 import org.apache.iotdb.db.storageengine.dataregion.memtable.IMemTable;
 import org.apache.iotdb.db.storageengine.dataregion.memtable.TsFileProcessor;
 import 
org.apache.iotdb.db.storageengine.dataregion.memtable.TsFileProcessorInfo;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.DropTableModProgress;
 import org.apache.iotdb.db.storageengine.dataregion.modification.ModEntry;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.ModificationFile;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
@@ -170,6 +171,7 @@ import org.apache.iotdb.db.utils.CommonUtils;
 import org.apache.iotdb.db.utils.EncryptDBUtils;
 import org.apache.iotdb.db.utils.ModificationUtils;
 import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.mpp.rpc.thrift.TDropTableModProgress;
 import org.apache.iotdb.rpc.RpcUtils;
 import org.apache.iotdb.rpc.TSStatusCode;
 
@@ -264,7 +266,6 @@ import static 
org.apache.tsfile.common.constant.TsFileConstant.TSFILE_SUFFIX;
  * will be called as a callback.
  */
 public class DataRegion implements IDataRegionForQuery {
-
   private static final IoTDBConfig config = 
IoTDBDescriptor.getInstance().getConfig();
   private static final Logger DEBUG_LOGGER = 
LoggerFactory.getLogger("QUERY_DEBUG");
 
@@ -290,6 +291,10 @@ public class DataRegion implements IDataRegionForQuery {
   /** Data region has been deleted or not. */
   private volatile boolean deleted = false;
 
+  // Only the latest drop is retained per region. The ConfigNode captures its 
final counters
+  // before the dedicated DROP TABLE worker executes another procedure's 
DELETE_DATA state.
+  private volatile Pair<Long, DropTableModProgress> dropTableModProgress;
+
   /** closeStorageGroupCondition is used to wait for all currently closing 
TsFiles to be done. */
   private final Object closeStorageGroupCondition = new Object();
 
@@ -3202,6 +3207,11 @@ public class DataRegion implements IDataRegionForQuery {
     }
   }
 
+  public TDropTableModProgress getDropTableModProgress(final long procedureId) 
{
+    final Pair<Long, DropTableModProgress> progress = dropTableModProgress;
+    return progress != null && progress.left == procedureId ? 
progress.right.snapshot() : null;
+  }
+
   public void deleteByTable(RelationalDeleteDataNode node) throws IOException {
     if (node.getDatabaseName() != null && 
!node.getDatabaseName().equals(databaseName)) {
       // not targeted on this database, return
@@ -3215,12 +3225,24 @@ public class DataRegion implements IDataRegionForQuery {
     }
     List<TableDeletionEntry> modEntries = node.getModEntries();
 
+    final long procedureId = 
DropTableModProgress.procedureId(node.getPlanNodeId());
+    final DropTableModProgress progress =
+        procedureId >= 0 && modEntries.size() == 1 && 
modEntries.get(0).isDroppingTable()
+            ? new DropTableModProgress()
+            : null;
+
     logger.info(StorageEngineMessages.DELETION_EXECUTING_TABLE_DELETION, node);
 
     writeLock("delete");
     boolean hasReleasedLock = false;
     try {
+      if (progress != null) {
+        dropTableModProgress = new Pair<>(procedureId, progress);
+      }
       if (deleted) {
+        if (progress != null) {
+          progress.setTotalModFileCount(0);
+        }
         return;
       }
       String tableName = modEntries.get(0).getTableName();
@@ -3302,7 +3324,7 @@ public class DataRegion implements IDataRegionForQuery {
       hasReleasedLock = true;
 
       for (int i = 0; i < modEntries.size(); i++) {
-        deleteDataInSealedFiles(sealedTsFileResourceLists.get(i), 
modEntries.get(i));
+        deleteDataInSealedFiles(sealedTsFileResourceLists.get(i), 
modEntries.get(i), progress);
       }
     } catch (Exception e) {
       throw new IOException(e);
@@ -3619,6 +3641,12 @@ public class DataRegion implements IDataRegionForQuery {
 
   private void deleteDataInSealedFiles(Collection<TsFileResource> 
sealedTsFiles, ModEntry deletion)
       throws IOException {
+    deleteDataInSealedFiles(sealedTsFiles, deletion, null);
+  }
+
+  private void deleteDataInSealedFiles(
+      Collection<TsFileResource> sealedTsFiles, ModEntry deletion, 
DropTableModProgress progress)
+      throws IOException {
     Set<ModificationFile> involvedModificationFiles = new HashSet<>();
     List<TsFileResource> deletedByMods = new ArrayList<>();
     List<TsFileResource> deletedByFiles = new ArrayList<>();
@@ -3753,6 +3781,10 @@ public class DataRegion implements IDataRegionForQuery {
       }
     }
 
+    if (progress != null) {
+      progress.setTotalModFileCount(involvedModificationFiles.size());
+    }
+
     if (involvedModificationFiles.isEmpty()) {
       logger.info(StorageEngineMessages.DELETION_DOES_NOT_INVOLVE_ANY_FILE, 
deletion);
       return;
@@ -3765,6 +3797,9 @@ public class DataRegion implements IDataRegionForQuery {
                   try {
                     modFile.write(deletion);
                     modFile.close();
+                    if (progress != null) {
+                      progress.modFileWritten();
+                    }
                   } catch (Exception e) {
                     return e;
                   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/DropTableModProgress.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/DropTableModProgress.java
new file mode 100644
index 00000000000..88c9f019b5e
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/modification/DropTableModProgress.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.storageengine.dataregion.modification;
+
+import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId;
+import org.apache.iotdb.mpp.rpc.thrift.TDropTableModProgress;
+
+/** Counts the distinct mod files processed by one region's current DROP TABLE 
attempt. */
+public class DropTableModProgress {
+  private static final String PLAN_NODE_ID_PREFIX = "drop-table-";
+
+  private long totalModFileCount = -1;
+  private long writtenModFileCount;
+
+  public static PlanNodeId planNodeId(final long procedureId) {
+    // PlanNodeId already travels through consensus. Reuse it so observability 
does not change
+    // the deletion plan, WAL or DAL serialization formats.
+    return new PlanNodeId(PLAN_NODE_ID_PREFIX + procedureId);
+  }
+
+  public static long procedureId(final PlanNodeId planNodeId) {
+    final String id = planNodeId.getId();
+    if (!id.startsWith(PLAN_NODE_ID_PREFIX)) {
+      return -1;
+    }
+    try {
+      return Long.parseLong(id.substring(PLAN_NODE_ID_PREFIX.length()));
+    } catch (final NumberFormatException e) {
+      return -1;
+    }
+  }
+
+  public synchronized void setTotalModFileCount(final long totalModFileCount) {
+    this.totalModFileCount = totalModFileCount;
+  }
+
+  public synchronized void modFileWritten() {
+    ++writtenModFileCount;
+  }
+
+  public synchronized TDropTableModProgress snapshot() {
+    final TDropTableModProgress progress = new 
TDropTableModProgress(writtenModFileCount);
+    if (totalModFileCount >= 0) {
+      progress.setRemainingModFileCount(totalModFileCount - 
writtenModFileCount);
+    }
+    return progress;
+  }
+}
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/RelationalDeleteDataNodeTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/RelationalDeleteDataNodeTest.java
index 910c38f44eb..bcbc490ea2a 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/RelationalDeleteDataNodeTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/RelationalDeleteDataNodeTest.java
@@ -24,6 +24,7 @@ import 
org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode;
 import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId;
 import 
org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.DropTableModProgress;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.And;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.FullExactMatch;
@@ -47,6 +48,27 @@ import static org.junit.Assert.assertEquals;
 
 public class RelationalDeleteDataNodeTest {
 
+  @Test
+  public void testDropTableProcedureIdSurvivesConsensusSerialization() throws 
IOException {
+    final RelationalDeleteDataNode node =
+        new RelationalDeleteDataNode(
+            DropTableModProgress.planNodeId(42),
+            new TableDeletionEntry(
+                new DeletionPredicate("table"), new TimeRange(Long.MIN_VALUE, 
Long.MAX_VALUE)),
+            null);
+    assertEquals(
+        42,
+        DropTableModProgress.procedureId(
+            
PlanNodeType.deserialize(node.serializeToByteBuffer()).getPlanNodeId()));
+    final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+    node.serialize(new DataOutputStream(bytes));
+    assertEquals(
+        42,
+        DropTableModProgress.procedureId(
+            
PlanNodeType.deserialize(ByteBuffer.wrap(bytes.toByteArray())).getPlanNodeId()));
+    assertEquals(-1, DropTableModProgress.procedureId(new 
PlanNodeId("query-plan")));
+  }
+
   @Test
   public void testSerialization() throws IOException {
     RelationalDeleteDataNode relationalDeleteDataNode =
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/DataRegionTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/DataRegionTest.java
index 4e2ca24a750..b287a669b80 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/DataRegionTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/DataRegionTest.java
@@ -70,6 +70,8 @@ import 
org.apache.iotdb.db.storageengine.dataregion.flush.TsFileFlushPolicy;
 import org.apache.iotdb.db.storageengine.dataregion.memtable.ReadOnlyMemChunk;
 import org.apache.iotdb.db.storageengine.dataregion.memtable.TsFileProcessor;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
+import 
org.apache.iotdb.db.storageengine.dataregion.modification.DropTableModProgress;
+import org.apache.iotdb.db.storageengine.dataregion.modification.ModEntry;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.ModificationFile;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.TagPredicate.NOP;
@@ -81,6 +83,7 @@ import 
org.apache.iotdb.db.storageengine.rescon.memory.MemTableManager;
 import org.apache.iotdb.db.storageengine.rescon.memory.SystemInfo;
 import org.apache.iotdb.db.utils.EnvironmentUtils;
 import org.apache.iotdb.db.utils.constant.TestConstant;
+import org.apache.iotdb.mpp.rpc.thrift.TDropTableModProgress;
 import org.apache.iotdb.rpc.RpcUtils;
 import org.apache.iotdb.rpc.TSStatusCode;
 
@@ -116,12 +119,15 @@ import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import static 
org.apache.iotdb.db.queryengine.plan.statement.StatementTestUtils.genInsertRowNode;
 import static 
org.apache.iotdb.db.queryengine.plan.statement.StatementTestUtils.genInsertTabletNode;
+import static org.awaitility.Awaitility.await;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -2234,7 +2240,11 @@ public class DataRegionTest {
             new TimeRange(Long.MIN_VALUE, Long.MAX_VALUE));
     dataRegion.deleteByTable(
         new RelationalDeleteDataNode(
-            new PlanNodeId("table-delete"), deletionEntry, 
dataRegion.getDatabaseName()));
+            DropTableModProgress.planNodeId(42), deletionEntry, 
dataRegion.getDatabaseName()));
+
+    Assert.assertEquals(
+        new TDropTableModProgress(1).setRemainingModFileCount(0),
+        dataRegion.getDropTableModProgress(42));
 
     assertTrue(sourceTsFile.getTsFile().exists());
     assertTrue(sourceTsFile.isCompacting());
@@ -2250,6 +2260,148 @@ public class DataRegionTest {
     }
   }
 
+  @Test
+  public void testDropTableModProgressWhileClosingSharedFile() throws 
Exception {
+    checkDropTableModProgressWhileClosingFile(true);
+  }
+
+  @Test
+  public void testDropTableModProgressWhileClosingFile() throws Exception {
+    checkDropTableModProgressWhileClosingFile(false);
+  }
+
+  private void checkDropTableModProgressWhileClosingFile(final boolean 
shareModFile)
+      throws Exception {
+    dataRegion.insertTablet(genInsertTabletNode(10, 0));
+    dataRegion.syncCloseAllWorkingTsFileProcessors();
+    dataRegion.insertTablet(genInsertTabletNode(10, 100));
+    dataRegion.syncCloseAllWorkingTsFileProcessors();
+    final List<TsFileResource> resources = 
dataRegion.getTsFileManager().getTsFileList(true);
+    Assert.assertEquals(2, resources.size());
+    final CountDownLatch closeStarted = new CountDownLatch(1);
+    final CountDownLatch allowClose = new CountDownLatch(1);
+    final AtomicInteger writeCount = new AtomicInteger();
+    final ModificationFile modFile =
+        new ModificationFile(resources.get(0).getModFileForWrite().getFile(), 
true) {
+          @Override
+          public int hashCode() {
+            // Separate HashSet buckets ensure the stream can split these two 
writes.
+            return 0;
+          }
+
+          @Override
+          public void write(final ModEntry entry) throws IOException {
+            writeCount.incrementAndGet();
+            super.write(entry);
+          }
+
+          @Override
+          public void close() throws IOException {
+            closeStarted.countDown();
+            try {
+              assertTrue(allowClose.await(10, TimeUnit.SECONDS));
+            } catch (final InterruptedException e) {
+              Thread.currentThread().interrupt();
+              throw new IOException(e);
+            }
+            super.close();
+          }
+        };
+    for (final TsFileResource resource : resources) {
+      
assertTrue(resource.transformStatus(TsFileResourceStatus.COMPACTION_CANDIDATE));
+      assertTrue(resource.transformStatus(TsFileResourceStatus.COMPACTING));
+      if (shareModFile || resource == resources.get(0)) {
+        resource.setExclusiveModFile(modFile);
+      } else {
+        resource.setExclusiveModFile(
+            new ModificationFile(resource.getModFileForWrite().getFile(), 
true) {
+              @Override
+              public int hashCode() {
+                return 15;
+              }
+            });
+      }
+    }
+
+    final ExecutorService executor = Executors.newSingleThreadExecutor();
+    try {
+      final Future<?> deletion =
+          executor.submit(
+              () -> {
+                dataRegion.deleteByTable(dropTableNode(43));
+                return null;
+              });
+      assertTrue(closeStarted.await(10, TimeUnit.SECONDS));
+      Assert.assertFalse(deletion.isDone());
+      // A successful write is still pending until close returns. With 
independent mod files,
+      // the other file finishes while this one is blocked. Shared mod files 
are counted once.
+      await()
+          .atMost(10, TimeUnit.SECONDS)
+          .untilAsserted(
+              () ->
+                  Assert.assertEquals(
+                      new TDropTableModProgress(shareModFile ? 0 : 
1).setRemainingModFileCount(1),
+                      dataRegion.getDropTableModProgress(43)));
+      allowClose.countDown();
+      deletion.get(10, TimeUnit.SECONDS);
+      Assert.assertEquals(
+          new TDropTableModProgress(shareModFile ? 1 : 
2).setRemainingModFileCount(0),
+          dataRegion.getDropTableModProgress(43));
+      Assert.assertEquals(1, writeCount.get());
+    } finally {
+      allowClose.countDown();
+      executor.shutdown();
+      assertTrue(executor.awaitTermination(10, TimeUnit.SECONDS));
+    }
+  }
+
+  @Test
+  public void testDropTableModProgressDoesNotCountFailedClose() throws 
Exception {
+    dataRegion.insertTablet(genInsertTabletNode(10, 0));
+    dataRegion.syncCloseAllWorkingTsFileProcessors();
+    final TsFileResource resource = 
dataRegion.getTsFileManager().getTsFileList(true).get(0);
+    
assertTrue(resource.transformStatus(TsFileResourceStatus.COMPACTION_CANDIDATE));
+    assertTrue(resource.transformStatus(TsFileResourceStatus.COMPACTING));
+    final ModificationFile modFile = 
Mockito.spy(resource.getModFileForWrite());
+    resource.setExclusiveModFile(modFile);
+    Mockito.doThrow(new IOException()).when(modFile).close();
+    try {
+      Assert.assertThrows(IOException.class, () -> 
dataRegion.deleteByTable(dropTableNode(44)));
+      Assert.assertEquals(
+          new TDropTableModProgress(0).setRemainingModFileCount(1),
+          dataRegion.getDropTableModProgress(44));
+    } finally {
+      Mockito.doCallRealMethod().when(modFile).close();
+      modFile.close();
+    }
+  }
+
+  @Test
+  public void testDropTableModProgressWithoutModFiles() throws Exception {
+    dataRegion.deleteByTable(dropTableNode(45));
+    Assert.assertEquals(
+        new TDropTableModProgress(0).setRemainingModFileCount(0),
+        dataRegion.getDropTableModProgress(45));
+    dataRegion.insertTablet(genInsertTabletNode(10, 0));
+    dataRegion.syncCloseAllWorkingTsFileProcessors();
+    final TsFileResource resource = 
dataRegion.getTsFileManager().getTsFileList(true).get(0);
+    dataRegion.deleteByTable(dropTableNode(46));
+    Assert.assertFalse(resource.getTsFile().exists());
+    Assert.assertEquals(
+        new TDropTableModProgress(0).setRemainingModFileCount(0),
+        dataRegion.getDropTableModProgress(46));
+    Assert.assertNull(dataRegion.getDropTableModProgress(45));
+  }
+
+  private RelationalDeleteDataNode dropTableNode(final long procedureId) {
+    return new RelationalDeleteDataNode(
+        DropTableModProgress.planNodeId(procedureId),
+        new TableDeletionEntry(
+            new DeletionPredicate(StatementTestUtils.tableName()),
+            new TimeRange(Long.MIN_VALUE, Long.MAX_VALUE)),
+        dataRegion.getDatabaseName());
+  }
+
   @Test
   public void testFlushSpecifiedResource()
       throws IllegalPathException, WriteProcessException, ExecutionException, 
InterruptedException {
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
index 56980f7e8df..d50be22154e 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
@@ -308,6 +308,8 @@ public class ColumnHeaderConstant {
   public static final String PROCEDURE_ID_TABLE_MODEL = "procedure_id";
   public static final String PROGRESS_TABLE_MODEL = "progress";
   public static final String ERROR_MESSAGE_TABLE_MODEL = "error_message";
+  public static final String WRITTEN_MOD_FILE_COUNT_TABLE_MODEL = 
"written_mod_file_count";
+  public static final String REMAINING_MOD_FILE_COUNT_TABLE_MODEL = 
"remaining_mod_file_count";
   public static final String REMAINING_EVENT_COUNT_TABLE_MODEL = 
"remaining_event_count";
   public static final String ESTIMATED_REMAINING_SECONDS_TABLE_MODEL =
       "estimated_remaining_seconds";
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java
index 90e916027d4..3efaf7e1a9a 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java
@@ -166,6 +166,12 @@ public class InformationSchema {
     dropTableProceduresTable.addColumnSchema(
         new AttributeColumnSchema(
             ColumnHeaderConstant.ERROR_MESSAGE_TABLE_MODEL, 
TSDataType.STRING));
+    dropTableProceduresTable.addColumnSchema(
+        new AttributeColumnSchema(
+            ColumnHeaderConstant.WRITTEN_MOD_FILE_COUNT_TABLE_MODEL, 
TSDataType.INT64));
+    dropTableProceduresTable.addColumnSchema(
+        new AttributeColumnSchema(
+            ColumnHeaderConstant.REMAINING_MOD_FILE_COUNT_TABLE_MODEL, 
TSDataType.INT64));
     dropTableProceduresTable.removeColumnSchema(TsTable.TIME_COLUMN_NAME);
     schemaTables.put(DROP_TABLE_PROCEDURES, dropTableProceduresTable);
 
diff --git a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift 
b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift
index e2f2595fa7d..0b733829d87 100644
--- a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift
+++ b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift
@@ -1326,6 +1326,8 @@ struct TDropTableProcedureInfo {
    4: required string state
    5: required string progress
    6: optional string errorMessage
+   7: optional i64 writtenModFileCount
+   8: optional i64 remainingModFileCount
 }
 
 struct TDescTableResp {
diff --git a/iotdb-protocol/thrift-datanode/src/main/thrift/datanode.thrift 
b/iotdb-protocol/thrift-datanode/src/main/thrift/datanode.thrift
index 73bd95942f1..76c0860a24f 100644
--- a/iotdb-protocol/thrift-datanode/src/main/thrift/datanode.thrift
+++ b/iotdb-protocol/thrift-datanode/src/main/thrift/datanode.thrift
@@ -380,6 +380,23 @@ struct TDeleteColumnDataReq {
 struct TDeleteDataOrDevicesForDropTableReq {
   1: required list<common.TConsensusGroupId> regionIdList
   2: required string tableName
+  3: optional i64 procedureId
+}
+
+struct TDropTableModProgress {
+  1: required i64 writtenModFileCount
+  // Unset until the region has identified all mod files involved in this 
deletion.
+  2: optional i64 remainingModFileCount
+}
+
+struct TGetDropTableModProgressReq {
+  1: required i64 procedureId
+  2: required list<common.TConsensusGroupId> regionIdList
+}
+
+struct TGetDropTableModProgressResp {
+  1: required common.TSStatus status
+  2: optional map<i32, TDropTableModProgress> regionProgress
 }
 
 struct TTableDeviceDeletionWithPatternAndFilterReq {
@@ -1356,6 +1373,8 @@ service IDataNodeRPCService {
   */
   common.TSStatus deleteDataForDropTable(TDeleteDataOrDevicesForDropTableReq 
req)
 
+  TGetDropTableModProgressResp 
getDropTableModProgress(TGetDropTableModProgressReq req)
+
   /**
   * Delete devices for drop table
   */

Reply via email to