Copilot commented on code in PR #3174:
URL: https://github.com/apache/fluss/pull/3174#discussion_r3279599069


##########
fluss-server/src/main/java/org/apache/fluss/server/coordinator/AutoPartitionManager.java:
##########
@@ -494,13 +607,33 @@ private void dropPartitions(
 
                 // only remove when zk success, this reflects to the 
partitionsByTable
                 dropIterator.remove();
+                dropped++;
+                bucketsConsumed += numBucketsPerPartition;
                 LOG.info(
                         "Auto partitioning deleted partition {} for table 
[{}].",
                         partitionName,
                         tablePath);

Review Comment:
   dropIterator.remove(), dropped++, bucketsConsumed += numBucketsPerPartition, 
and the "deleted partition" log currently execute unconditionally even if 
dropPartition threw PartitionNotExistException. This makes 
throttling/backpressure accounting inaccurate (non-existent partitions don't 
fan out deletion events). Restructure so accounting/logging only happen when a 
drop actually results in a deletion event (or explicitly separate the "already 
absent" case without charging budget).



##########
fluss-common/src/main/java/org/apache/fluss/config/ConfigOptions.java:
##########
@@ -200,6 +200,36 @@ public class ConfigOptions {
                             "The interval of auto partition check. "
                                     + "The default value is 10 minutes.");
 
+    public static final ConfigOption<Integer> 
AUTO_PARTITION_DROP_QUEUE_BACKPRESSURE_THRESHOLD =
+            key("auto-partition.drop.queue-backpressure-threshold")
+                    .intType()
+                    .defaultValue(1000)
+                    .withDescription(
+                            "When the coordinator event queue size reaches 
this threshold, "
+                                    + "auto partition will skip the drop step 
for the current round and retry "
+                                    + "in the next 
AUTO_PARTITION_CHECK_INTERVAL. This prevents overwhelming "
+                                    + "the coordinator at midnight day 
rotation when one partition drop fans "
+                                    + "out into thousands of bucket/replica 
deletion events. Set to a "
+                                    + "non-positive value to disable 
backpressure (always drop).");

Review Comment:
   The description references "AUTO_PARTITION_CHECK_INTERVAL" as a literal 
token, which won’t be meaningful to users reading generated config docs (it’s 
not the actual config key). Prefer referencing the real key (e.g., 
"auto-partition.check.interval") or interpolating 
AUTO_PARTITION_CHECK_INTERVAL.key() in the description text for clarity.



##########
fluss-server/src/main/java/org/apache/fluss/server/coordinator/AutoPartitionManager.java:
##########
@@ -297,6 +341,12 @@ private void doAutoPartition(long tableId, boolean 
forceDoAutoPartition) {
 
     private void doAutoPartition(Instant now, Set<Long> tableIds, boolean 
forceDoAutoPartition) {
         LOG.info("Start auto partitioning for {} tables at {}.", 
tableIds.size(), now);
+
+        // Per-round drop budget across all tables, in unit of bucket-deletion 
events.
+        // Non-positive cap disables the budget (drop without limit).
+        boolean unlimitedBudget = dropMaxBucketsPerRound <= 0;
+        int dropBudgetThisRound = unlimitedBudget ? Integer.MAX_VALUE : 
dropMaxBucketsPerRound;
+
         for (Long tableId : tableIds) {

Review Comment:
   The shared per-round drop budget is consumed in iteration order of tableIds 
(backed by HashMap.keySet()), which is not deterministic and can lead to 
persistent unfairness: the same subset of tables may consistently consume the 
budget each round while later tables rarely get a chance to drop. Consider 
iterating tables in a stable order (e.g., sort by tableId or tablePath) when 
budgeting is enabled to make throttling behavior predictable and avoid 
starvation across tables.
   



##########
fluss-common/src/main/java/org/apache/fluss/config/ConfigOptions.java:
##########
@@ -200,6 +200,36 @@ public class ConfigOptions {
                             "The interval of auto partition check. "
                                     + "The default value is 10 minutes.");
 
+    public static final ConfigOption<Integer> 
AUTO_PARTITION_DROP_QUEUE_BACKPRESSURE_THRESHOLD =
+            key("auto-partition.drop.queue-backpressure-threshold")
+                    .intType()
+                    .defaultValue(1000)
+                    .withDescription(
+                            "When the coordinator event queue size reaches 
this threshold, "
+                                    + "auto partition will skip the drop step 
for the current round and retry "
+                                    + "in the next 
AUTO_PARTITION_CHECK_INTERVAL. This prevents overwhelming "
+                                    + "the coordinator at midnight day 
rotation when one partition drop fans "
+                                    + "out into thousands of bucket/replica 
deletion events. Set to a "
+                                    + "non-positive value to disable 
backpressure (always drop).");
+
+    public static final ConfigOption<Integer> 
AUTO_PARTITION_DROP_MAX_BUCKETS_PER_ROUND =
+            key("auto-partition.drop.max-buckets-per-round")
+                    .intType()
+                    .defaultValue(1000)
+                    .withDescription(
+                            "Per-round drop budget across all auto-partition 
tables, measured "
+                                    + "in number of bucket-deletion events. 
Because dropping one "
+                                    + "partition fans out into numBuckets * 
replicationFactor "
+                                    + "deletion events on the coordinator 
queue, accounting in "
+                                    + "buckets gives uniform protection 
regardless of each table's "
+                                    + "bucket count. To avoid starving tables 
whose single-partition "
+                                    + "bucket count exceeds the budget, every 
table is allowed at "
+                                    + "least one partition drop per round even 
if it temporarily "
+                                    + "overshoots the budget. Remaining 
over-quota drops will be "
+                                    + "processed in the next 
AUTO_PARTITION_CHECK_INTERVAL. Set to "
+                                    + "a non-positive value to disable the cap 
(drop without "
+                                    + "limit).");

Review Comment:
   Same as above: the description mentions "AUTO_PARTITION_CHECK_INTERVAL" 
instead of the actual configuration key, which can confuse operators reading 
the docs. Consider using AUTO_PARTITION_CHECK_INTERVAL.key() (or the literal 
key string) in the description text.



##########
fluss-server/src/main/java/org/apache/fluss/server/coordinator/AutoPartitionManager.java:
##########
@@ -335,12 +385,46 @@ private void doAutoPartition(Instant now, Set<Long> 
tableIds, boolean forceDoAut
                 continue;
             }
 
-            dropPartitions(
-                    tablePath,
-                    tableInfo.getPartitionKeys(),
-                    createPartitionInstant,
-                    tableInfo.getTableConfig().getAutoPartitionStrategy(),
-                    currentPartitions);
+            // Queue-aware backpressure gate: skip this table's drop for the 
current round
+            // when the coordinator event queue is heavily backed up. The 
skipped partitions
+            // remain in partitionsByTable and will be retried in the next 
check interval.
+            boolean skipDrop = false;
+            if (dropQueueBackpressureThreshold > 0) {
+                int qSize = coordinatorEventQueueSizeProvider.getAsInt();
+                if (qSize >= dropQueueBackpressureThreshold) {
+                    LOG.info(
+                            "Skipping auto-partition drop for table [{}] 
(id={}) this round "
+                                    + "because coordinator event queue size {} 
>= threshold {}.",
+                            tablePath,
+                            tableId,
+                            qSize,
+                            dropQueueBackpressureThreshold);
+                    skipDrop = true;
+                }
+            }
+
+            if (!skipDrop && (unlimitedBudget || dropBudgetThisRound > 0)) {
+                // Use the actual current time for drop to ensure timely 
cleanup.
+                // The random delay is only meant for spreading partition 
creation load,
+                // not for delaying retention cleanup.
+                int budgetForCall = unlimitedBudget ? Integer.MAX_VALUE : 
dropBudgetThisRound;
+                int bucketsConsumed =
+                        dropPartitions(
+                                tablePath,
+                                tableInfo.getPartitionKeys(),
+                                now,
+                                
tableInfo.getTableConfig().getAutoPartitionStrategy(),
+                                currentPartitions,
+                                budgetForCall,
+                                tableInfo.getNumBuckets());

Review Comment:
   The drop budget is described/documented as protecting against a fan-out of 
`numBuckets * replicationFactor` deletion events, but the implementation 
charges only `numBucketsPerPartition` (tableInfo.getNumBuckets()) per dropped 
partition. Since TableManager.onDeletePartition triggers replica deletions for 
all replicas in the partition (i.e., ~numBuckets * replicationFactor state 
changes), the current accounting likely underestimates queue impact and can 
allow bursts larger than intended. Consider charging budget in terms of total 
replicas affected (e.g., numBuckets * replicationFactor or actual replica count 
from assignments) or adjust the docs/config wording to match the implemented 
unit.



##########
fluss-server/src/main/java/org/apache/fluss/server/coordinator/CoordinatorServer.java:
##########
@@ -303,7 +303,18 @@ protected void initCoordinatorLeader() throws Exception {
             this.coordinatorChannelManager = new 
CoordinatorChannelManager(rpcClient);
 
             this.autoPartitionManager =
-                    new AutoPartitionManager(metadataCache, metadataManager, 
conf);
+                    new AutoPartitionManager(
+                            metadataCache,
+                            metadataManager,
+                            conf,
+                            // Late-bound queue size probe: 
coordinatorEventProcessor is built
+                            // after autoPartitionManager, so we read the 
field on each call.
+                            // Returns 0 before the processor is wired up, 
which simply disables
+                            // the queue-aware backpressure gate during that 
brief window.
+                            () -> {
+                                CoordinatorEventProcessor p = 
this.coordinatorEventProcessor;
+                                return p == null ? 0 : 
p.getCoordinatorEventManager().queueSize();

Review Comment:
   The queue-size supplier reads coordinatorEventProcessor from a different 
thread than the one that assigns/clears it under the CoordinatorServer lock. 
Since coordinatorEventProcessor is @GuardedBy("lock") and not volatile, this 
introduces an unsynchronized cross-thread read and can observe stale values. 
Consider making coordinatorEventProcessor (or a dedicated 
CoordinatorEventManager reference) safely published (e.g., 
volatile/AtomicReference), or have the supplier acquire the same lock when 
reading.
   



-- 
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