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

wuchong pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fluss.git


The following commit(s) were added to refs/heads/main by this push:
     new 3b0afc0eb [server] Fix tableCount metric after dropping partitioned 
tables (#3482)
3b0afc0eb is described below

commit 3b0afc0eb0e839e6873a400fa3db49a0697eae1a
Author: yunhong <[email protected]>
AuthorDate: Sun Jun 14 09:03:14 2026 +0800

    [server] Fix tableCount metric after dropping partitioned tables (#3482)
---
 .../coordinator/CoordinatorEventProcessor.java     | 11 ++++++++++
 .../coordinator/event/CoordinatorEventManager.java | 15 ++++++++++++-
 .../coordinator/CoordinatorEventProcessorTest.java | 25 ++++++++++++++++++++++
 3 files changed, 50 insertions(+), 1 deletion(-)

diff --git 
a/fluss-server/src/main/java/org/apache/fluss/server/coordinator/CoordinatorEventProcessor.java
 
b/fluss-server/src/main/java/org/apache/fluss/server/coordinator/CoordinatorEventProcessor.java
index c2e297f19..0a93ed54b 100644
--- 
a/fluss-server/src/main/java/org/apache/fluss/server/coordinator/CoordinatorEventProcessor.java
+++ 
b/fluss-server/src/main/java/org/apache/fluss/server/coordinator/CoordinatorEventProcessor.java
@@ -959,6 +959,17 @@ public class CoordinatorEventProcessor implements 
EventProcessor {
 
         // remove table metrics.
         
coordinatorMetricGroup.removeTableMetricGroup(dropTableInfo.getTablePath(), 
tableId);
+
+        // For partitioned tables, the dropped table has no table-level 
replicas
+        // (all buckets live under partitionAssignments), so 
getAllReplicasForTable
+        // returns empty and areAllReplicasInState(.., 
ReplicaDeletionSuccessful)
+        // is vacuously true. Without this explicit trigger, the table would 
linger
+        // in tablesToBeDeleted (and the tableCount metric) until some 
unrelated
+        // replica-deletion response happens to invoke resumeDeletions(), which
+        // can be never if no other table is being dropped.
+        if (dropTableInfo.isPartitioned()) {
+            tableManager.resumeDeletions();
+        }
     }
 
     private void processDropPartition(DropPartitionEvent dropPartitionEvent) {
diff --git 
a/fluss-server/src/main/java/org/apache/fluss/server/coordinator/event/CoordinatorEventManager.java
 
b/fluss-server/src/main/java/org/apache/fluss/server/coordinator/event/CoordinatorEventManager.java
index 33df6f484..3bf64f1ef 100644
--- 
a/fluss-server/src/main/java/org/apache/fluss/server/coordinator/event/CoordinatorEventManager.java
+++ 
b/fluss-server/src/main/java/org/apache/fluss/server/coordinator/event/CoordinatorEventManager.java
@@ -32,6 +32,7 @@ import org.apache.fluss.utils.concurrent.ShutdownableThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.Set;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
@@ -119,7 +120,19 @@ public final class CoordinatorEventManager implements 
EventManager {
                         context -> {
                             int coordinatorServerCount = 
context.getLiveCoordinatorServers().size();
                             int tabletServerCount = 
context.getLiveTabletServers().size();
-                            int tableCount = context.allTables().size();
+                            // Exclude tables that have been queued for 
deletion (DropTable RPC
+                            // already acked, but completeDeleteTable has not 
yet removed them
+                            // from tablePathById). We dedup by tableId rather 
than subtracting
+                            // sizes so the result is robust even if 
tablesToBeDeleted ever
+                            // drifts out of sync with tablePathById -- it can 
never go negative
+                            // and only counts ids that are truly still in 
tablePathById.
+                            Set<Long> allTables = context.allTables().keySet();
+                            int tableCount = allTables.size();
+                            for (Long toDelete : 
context.getTablesToBeDeleted()) {
+                                if (allTables.contains(toDelete)) {
+                                    tableCount--;
+                                }
+                            }
                             int lakeTableCount = context.getLakeTableCount();
                             int bucketCount = 
context.bucketLeaderAndIsr().size();
                             int partitionCount = 
context.getTotalPartitionCount();
diff --git 
a/fluss-server/src/test/java/org/apache/fluss/server/coordinator/CoordinatorEventProcessorTest.java
 
b/fluss-server/src/test/java/org/apache/fluss/server/coordinator/CoordinatorEventProcessorTest.java
index 8f27cdd10..001a90f74 100644
--- 
a/fluss-server/src/test/java/org/apache/fluss/server/coordinator/CoordinatorEventProcessorTest.java
+++ 
b/fluss-server/src/test/java/org/apache/fluss/server/coordinator/CoordinatorEventProcessorTest.java
@@ -891,6 +891,31 @@ class CoordinatorEventProcessorTest {
                 });
     }
 
+    @Test
+    void testDropPartitionedTableWithNoPartitionsClearsTableState() throws 
Exception {
+        TablePath tablePath =
+                TablePath.of(defaultDatabase, 
"test_drop_partitioned_table_no_partitions");
+        initCoordinatorChannel();
+
+        long tableId =
+                metadataManager.createTable(
+                        tablePath, remoteDataDir, getPartitionedTable(), null, 
false);
+        retryVerifyContext(ctx -> 
assertThat(ctx.getTablePathById(tableId)).isNotNull());
+
+        metadataManager.dropTable(tablePath, false);
+
+        // The fix at the tail of processDropTable explicitly calls 
resumeDeletions(), which
+        // sees vacuous-true (getAllReplicasForTable returns empty) and runs
+        // completeDeleteTable -> removeTable, evicting the table from both 
tablesToBeDeleted
+        // and tablePathById.
+        retryVerifyContext(
+                ctx -> {
+                    assertThat(ctx.getTablePathById(tableId)).isNull();
+                    
assertThat(ctx.getTablesToBeDeleted()).doesNotContain(tableId);
+                    assertThat(ctx.allTables()).doesNotContainKey(tableId);
+                });
+    }
+
     @Test
     void testStartupResumesDropPartitionThroughCleanupManager() throws 
Exception {
         TablePath tablePath = TablePath.of(defaultDatabase, 
"test_startup_resume_via_cleanup");

Reply via email to