nsivabalan commented on code in PR #19033:
URL: https://github.com/apache/hudi/pull/19033#discussion_r3669746061


##########
hudi-sync/hudi-hive-sync/src/main/java/org/apache/hudi/hive/ddl/HiveQueryDDLExecutor.java:
##########
@@ -209,21 +219,96 @@ public void dropPartitionsToTable(String tableName, 
List<String> partitionsToDro
 
     log.info("Drop partitions {} on {}", partitionsToDrop.size(), tableName);
     try {
-      for (String dropPartition : partitionsToDrop) {
-        if (HivePartitionUtil.partitionExists(metaStoreClient, tableName, 
dropPartition, partitionValueExtractor,
-            config)) {
-          String partitionClause =
-              HivePartitionUtil.getPartitionClauseForDrop(dropPartition, 
partitionValueExtractor, config);
-          metaStoreClient.dropPartition(databaseName, tableName, 
partitionClause, false);
-        }
-        log.info("Drop partition {} on {}", dropPartition, tableName);
-      }
+      int batchSyncPartitionNum = 
config.getIntOrDefault(HIVE_BATCH_SYNC_PARTITION_NUM);
+      List<List<String>> batches = CollectionUtils.batches(partitionsToDrop, 
batchSyncPartitionNum);
+      runDropBatches(tableName, batches);
     } catch (Exception e) {
       log.error("{} drop partition failed", tableId(databaseName, tableName), 
e);
       throw new HoodieHiveSyncException(tableId(databaseName, tableName) + " 
drop partition failed", e);
     }
   }
 
+  /**
+   * Drops partitions one batch at a time. When {@link #metaStoreClientPool} 
is present,
+   * batches fan out across the pool's worker threads (each borrowing an 
independent
+   * IMetaStoreClient); otherwise batches are dispatched sequentially against 
the
+   * session client. Hive has no batch-drop primitive that matches 
dropPartition's
+   * semantics, so each worker still iterates its chunk one partition at a 
time — the
+   * win is fanning chunks across independent Thrift clients.
+   *
+   * <p>First-error semantics match {@code HiveDriverPool.awaitAll}: the first 
failure is
+   * rethrown, not-yet-started batches are cancelled, and later failures are 
logged at WARN.
+   */
+  private void runDropBatches(String tableName, List<List<String>> batches) 
throws Exception {
+    if (!metaStoreClientPool.isPresent()) {
+      for (List<String> batch : batches) {
+        applyDropBatch(metaStoreClient, tableName, batch);
+      }
+      return;
+    }
+    IMetaStoreClientPool pool = metaStoreClientPool.get();
+    List<Future<Void>> futures = new ArrayList<>(batches.size());
+    for (List<String> batch : batches) {
+      futures.add(pool.executor().submit(() ->
+          pool.run(poolClient -> {
+            applyDropBatch(poolClient, tableName, batch);
+            return null;
+          })
+      ));
+    }
+
+    Exception firstError = null;
+    int cancelled = 0;
+    for (Future<Void> f : futures) {
+      // Once a batch has failed the sync is going to abort anyway, so stop 
handing
+      // more DROPs to the metastore. mayInterruptIfRunning=false mirrors
+      // HiveDriverPool.awaitAll: a batch already mid-flight runs to 
completion rather
+      // than leaving the partition list half-dropped at an arbitrary point.
+      if (firstError != null && f.cancel(false)) {
+        cancelled++;
+        continue;
+      }
+      try {
+        f.get();

Review Comment:
   Good catch — you are right, and the abort was not merely weaker than 
advertised, it was doing nothing. I wrote a unit test against the previous code 
and every queued batch still ran after the first failure:
   
   ```
   expected: <[FAIL]> but was: <[FAIL, AFTER_A, AFTER_B]>
   ```
   
   The loop waited on futures in submission order, so a fast failure on a later 
batch went unobserved while the awaiting thread was parked on an earlier slow 
one, and the executor kept starting queued tasks the whole time. 
`Future.cancel` from the awaiting thread is always too late once a worker has 
dequeued its next task.
   
   Fixed by taking your suggestion and extracting `HiveDriverPool.Dispatch` 
into a shared `ParallelDispatch` rather than reimplementing the pattern a 
second time — so both mechanisms that make it work are now shared: the latch 
tripped by the first abort (awaiting thread wakes on failure, not on its turn 
in submission order) and the task-side abort check on entry.
   
   `IMetaStoreClientPool` now has `dispatchAll`/`awaitAll` built on it, and 
`runDropBatches` delegates instead of hand-rolling the loop — which also 
resolves the duplicate-loop nit hudi-agent raised earlier. Net effect is -139 
lines.
   
   Covered by two regression tests in the new `TestIMetaStoreClientPool`: 
`abortStopsQueuedBatchesAfterFirstFailure` and 
`abortStopsQueuedBatchesWhenEarlierBatchIsSlow` (the latter pins exactly the 
slow-earlier-future interleaving you described). Both fail against the previous 
implementation. Pushed in `8721c9a`.



##########
hudi-sync/hudi-hive-sync/src/main/java/org/apache/hudi/hive/HoodieHiveSyncClient.java:
##########
@@ -227,6 +241,22 @@ private IMetaStoreClient 
createMetaStoreClient(HiveSyncConfig config) {
     }
   }
 
+  private Option<IMetaStoreClientPool> 
maybeBuildPartitionClientPool(HiveSyncConfig config) {
+    if (!config.getBooleanOrDefault(HIVE_SYNC_BATCHING_ENABLED)) {
+      return Option.empty();
+    }
+    if (config.getBooleanOrDefault(HIVE_SYNC_USE_SPARK_CATALOG)) {
+      // The Spark catalog client is constructed via reflection against a 
Spark-side
+      // class and isn't compatible with the direct RetryingMetaStoreClient 
pool path.
+      // Fall back to single-client sequential behavior rather than failing 
the sync.
+      log.warn("hive_sync.batching.enabled=true is not supported with 
use_spark_catalog=true; "
+          + "falling back to sequential partition sync.");
+      return Option.empty();
+    }
+    int size = config.getIntOrDefault(HIVE_SYNC_BATCHING_THREADS);
+    return Option.of(new IMetaStoreClientPool(config, size));

Review Comment:
   Agreed — that text was the pre-DROP contract and now states the opposite of 
the behavior. Fixed in `6c81322`.
   
   `HIVE_SYNC_BATCHING_ENABLED` now documents that DROP is parallelized too, 
that it fans out over metastore (Thrift) clients rather than Hive Driver 
workers (since it is issued as `dropPartition` calls rather than SQL), that 
drops are split into `batch_num`-sized batches, and that it falls back to 
sequential execution on the single session client when `use_spark_catalog=true`.
   
   Also updated `batching.threads`, which the same value now sizes for both 
pools — that was equally misleading since it only mentioned Driver workers. And 
switched the spark-catalog fallback warning to emit real config keys via 
`.key()` (hudi-agent nit on the same change), so operators can grep the 
property they actually set.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to