Skip to content

[server] Add in-memory rebalance round limit#3622

Open
fhan688 wants to merge 5 commits into
apache:mainfrom
fhan688:support-rebalance-round-window-no-zk
Open

[server] Add in-memory rebalance round limit#3622
fhan688 wants to merge 5 commits into
apache:mainfrom
fhan688:support-rebalance-round-window-no-zk

Conversation

@fhan688

@fhan688 fhan688 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Purpose

Linked issue: #3348 #3536

This PR is derived from the previous rebalance throttling work in #3498 and #3537.

#3537 adds coordinator.rebalance.max-inflight-tasks to control how many bucket-level rebalance tasks can run concurrently. #3498 explored limiting large rebalance plans by splitting them into rounds, but that approach introduced new ZooKeeper metadata models, serde, and znodes.

This PR keeps the round-limiting capability from #3498, but reworks it on top of #3537 with a lower-intrusion design: the coordinator uses an in-memory round/window inside RebalanceManager and does not add any new ZooKeeper model, serde, or znode.

The default behavior remains unchanged.

Brief change log

  • Add coordinator.rebalance.max-buckets-per-round with default value 0.

    • 0 disables round limiting.
    • A positive value limits how many bucket-level rebalance tasks are admitted into one in-memory scheduling round.
  • Extend RebalanceManager with an in-memory rebalance round/window.

    • Keep the full rebalance plan persisted in the existing legacy RebalanceTask znode.
    • Sort bucket tasks deterministically by tableId, partitionId, and bucketId.
    • Admit at most coordinator.rebalance.max-buckets-per-round bucket tasks into the current round.
    • Open the next round only after the current round has no pending or in-flight bucket tasks.
    • Continue using coordinator.rebalance.max-inflight-tasks from [server] Support configurable rebalance concurrency #3537 to control concurrent execution within the active round.
  • Keep the implementation ZooKeeper-compatible with the existing rebalance flow.

    • No new ZooKeeper metadata model.
    • No new JSON serde.
    • No new znode under the rebalance path.
    • Recovery still uses the existing persisted RebalanceTask and rebuilds the in-memory scheduling window after CoordinatorServer restart.
  • Update rebalance documentation to explain how:

    • coordinator.rebalance.max-inflight-tasks controls concurrent bucket movements.
    • coordinator.rebalance.max-buckets-per-round controls per-round admission.
    • Very small round sizes, such as 1, can create many small scheduling rounds and should be used only when strict serial admission is required.

Tests

  • git diff --check
  • mvn -pl fluss-server -am -DskipITs -Dcheckstyle.skip=true -DfailIfNoTests=false -Dtest=RebalanceManagerTest test

API and Format

  • Adds a new coordinator configuration:

    • coordinator.rebalance.max-buckets-per-round
  • No public client API changes.

  • No RPC protocol changes.

  • No storage format changes.

  • No new ZooKeeper metadata, serde, or znode.

Backward compatibility:

  • The default value 0 preserves the existing behavior.
  • Existing rebalance task metadata remains the only persisted rebalance execution format.
  • coordinator.rebalance.max-inflight-tasks keeps the [server] Support configurable rebalance concurrency #3537 behavior and remains responsible for concurrent execution throttling.

Documentation

  • Add coordinator.rebalance.max-buckets-per-round to website/docs/maintenance/configuration.md.
  • Update website/docs/maintenance/operations/rebalance.md with operational guidance for using coordinator.rebalance.max-inflight-tasks and coordinator.rebalance.max-buckets- per-round together.

@fhan688

fhan688 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

This PR is derived from the previous rebalance throttling work in #3498 and #3537, please help review, thanks! @swuferhong @wuchong

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds coordinator-side rebalance throttling that can (a) cap concurrent bucket movements dynamically and (b) optionally admit bucket tasks in bounded in-memory “rounds” to avoid activating very large plans all at once, without introducing new ZooKeeper metadata models.

Changes:

  • Introduce coordinator.rebalance.max-buckets-per-round (static, default 0) and implement in-memory round admission in RebalanceManager with deterministic bucket ordering.
  • Wire coordinator.rebalance.max-inflight-tasks into the dynamic-config framework for coordinators via coordinator events, plus recovery via a dedicated RecoverRebalanceEvent.
  • Update docs and expand tests for dynamic config changes, recovery, inflight throttling, and round behavior.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
website/docs/maintenance/operations/updating-configs.md Document coordinator.rebalance.max-inflight-tasks as a dynamically updatable option.
website/docs/maintenance/operations/rebalance.md Add operational guidance for tuning inflight concurrency and per-round admission.
website/docs/maintenance/configuration.md Clarify restart vs dynamic config behavior; document new rebalance options.
fluss-server/src/test/java/org/apache/fluss/server/DynamicConfigChangeTest.java Add tests validating dynamic updates for coordinator.rebalance.max-inflight-tasks.
fluss-server/src/test/java/org/apache/fluss/server/coordinator/rebalance/RebalanceManagerTest.java Add/adjust tests for inflight throttling, round admission, recovery, and timeouts.
fluss-server/src/test/java/org/apache/fluss/server/coordinator/CoordinatorEventProcessorTest.java Update in-progress rebalance counting logic for new manager behavior.
fluss-server/src/main/java/org/apache/fluss/server/DynamicServerConfig.java Allow dynamic updates for max inflight tasks; change registration behavior for reconfigurables.
fluss-server/src/main/java/org/apache/fluss/server/coordinator/rebalance/RebalanceManager.java Implement in-memory round/window scheduling and dynamic max-inflight updates via events.
fluss-server/src/main/java/org/apache/fluss/server/coordinator/event/RecoverRebalanceEvent.java New event to recover persisted rebalance tasks on the coordinator event thread.
fluss-server/src/main/java/org/apache/fluss/server/coordinator/event/RebalanceMaxInflightTasksChangedEvent.java New event to apply dynamic max-inflight changes on the coordinator event thread.
fluss-server/src/main/java/org/apache/fluss/server/coordinator/CoordinatorServer.java Register RebalanceManager with DynamicConfigManager.
fluss-server/src/main/java/org/apache/fluss/server/coordinator/CoordinatorEventProcessor.java Handle new recovery/config-change events and pass config into RebalanceManager.
fluss-common/src/main/java/org/apache/fluss/config/ConfigOptions.java Add config definitions for rebalance inflight and per-round limits.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 113 to 117
void register(ServerReconfigurable serverReconfigurable) {
serverReconfigures.put(serverReconfigurable.getClass(), serverReconfigurable);
serverReconfigurable.validate(currentConfig);
serverReconfigurable.reconfigure(currentConfig);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching this. Fixed by serializing register() with the same write lock used by dynamic config updates. The method now validates and applies the current config before adding the reconfigurable to serverReconfigures, so a failed registration will not leave a stale entry behind.

Comment on lines 2244 to 2253
RebalanceStatus status =
eventProcessor
.getRebalanceManager()
.listRebalanceProgress(null)
.progressForBucketMap()
.get(tb)
.status();
if (!RebalanceStatus.FINAL_STATUSES.contains(status)) {
count++;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Fixed the test helper to handle both a null RebalanceProgress and missing bucket progress entries. Missing progress is now treated as not in progress instead of throwing an NPE.

Comment on lines +715 to +719
assertThat(eventManager.events).hasSize(2);
assertThat(((RebalanceTaskTimeoutEvent) eventManager.events.get(0)).getTableBucket())
.isEqualTo(buckets.get(0));
assertThat(((RebalanceTaskTimeoutEvent) eventManager.events.get(1)).getTableBucket())
.isEqualTo(buckets.get(1));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Fixed the test to avoid depending on the timeout event order. It now asserts the timed-out buckets with containsExactlyInAnyOrder, matching the unordered iteration behavior in checkTimeout().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants