Skip to content

[server] Support group-aware tiering table assignment - #4745

Open
litiliu wants to merge 6 commits into
apache:mainfrom
litiliu:codex/tiering-groups
Open

litiliu wants to merge 6 commits into
apache:mainfrom
litiliu:codex/tiering-groups

Conversation

@litiliu

@litiliu litiliu commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

Linked issue: close #3789

Allow operators to assign tables to separate tiering services so workloads with different resource or freshness requirements can be scaled independently. Services in the same group continue to share work.

Brief change log

  • Add the mutable table option table.datalake.tiering-group and service option --fluss.tiering.group, both defaulting to default, with shared group-name validation.
  • Assign pending tables only to services in the matching group. Apply group changes asynchronously through the existing metadata update path, allowing an already assigned round to finish with its current epoch.
  • Document group configuration, reassignment behavior, and rolling-upgrade compatibility; cover group-aware scheduling, heartbeat compatibility, and real Flink-to-Paimon tiering in tests.
  • Regenerate the checked-in Rust protobuf bindings for the heartbeat field with the CI-pinned generator.

Tests

Passed 90 unique targeted test cases after integrating apache/main at b25ba46d8:

  • TableConfigTest, TableDescriptorValidationTest
  • ServerConnectionTest
  • LakeTableTieringManagerTest, LakeTieringHeartbeatITCase
  • TieringSourceEnumeratorSchedulingTest, TieringSourceEnumeratorTest, TieringCommitOperatorTest
  • FlussLakeTieringTest
  • PaimonTieringITCase#testTieringWithGroups and the existing testTiering end-to-end case

The affected modules and their full dependency reactor passed Maven verify on JDK 17, including Spotless, Checkstyle, and validation checks. Both Paimon end-to-end cases passed twice after integrating the upstream tiering refactor. The full repository test suite has not been run.

Rust protobuf regeneration is deterministic with protoc 27.1, and cargo check --package fluss-rs --lib --locked --offline passed.

API and Format

Add the optional tiering_group field to the internal lake-tiering heartbeat request and advertise heartbeat protocol version 1 while retaining version 0 support. An absent group means default; non-default group requests are rejected before being sent to an older Coordinator. Storage formats are unchanged.

Documentation

Update website/docs/streaming-lakehouse/tiering-service.md with group configuration, dynamic reassignment, default behavior, legacy-table enablement, and upgrade/downgrade guidance.

Route tiering table assignments by the configured table and service group. Keep group changes asynchronous, preserve default-group compatibility, and expose per-group tiering metrics.
@litiliu
litiliu marked this pull request as ready for review October 20, 2026 14:36
@litiliu

litiliu commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

Hi @polyzos, CI is green and this PR is ready for review. Could you take a look when you have a chance? It adds group-aware tiering table assignment with Flink-to-Paimon end-to-end coverage. Thanks!

@beryllw
beryllw requested review from beryllw and luoyuxia October 21, 2026 08:10
@luoyuxia
luoyuxia requested a balanced review from Copilot October 21, 2026 12:19

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.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

3 open issues
What changed in this PR

Adds group-aware tiering so operators can isolate tiering workloads by assigning tables and tiering services to matching “tiering groups”, with backward-compatible heartbeat/version handling.

Changes:

  • Introduces table option table.datalake.tiering-group (default default) and validates group names.
  • Updates coordinator scheduling and heartbeat flow to request/assign pending tables by tiering group.
  • Adds/updates tests and documentation; bumps LAKE_TIERING_HEARTBEAT API max version and regenerates Rust protobuf bindings.
File Description
website/​docs/​streaming-lakehouse/​tiering-service.md Documents tiering group configuration, reassignment semantics, and upgrade/downgrade guidance
fluss-common/​src/​main/​java/​org/​apache/​fluss/​config/​ConfigOptions.java Adds new table config option for tiering group
fluss-common/​src/​main/​java/​org/​apache/​fluss/​config/​FlussConfigUtils.java Adds shared tiering-group validation helper
fluss-common/​src/​main/​java/​org/​apache/​fluss/​config/​TableConfig.java Exposes getDataLakeTieringGroup()
fluss-server/​src/​main/​java/​org/​apache/​fluss/​server/​utils/​TableDescriptorValidation.java Validates tiering-group option on descriptor parsing
fluss-server/​src/​main/​java/​org/​apache/​fluss/​server/​coordinator/​LakeTableTieringManager.java Stores per-table group and assigns pending tables by group
fluss-server/​src/​main/​java/​org/​apache/​fluss/​server/​coordinator/​CoordinatorService.java Routes heartbeat table requests to group-aware scheduling
fluss-server/​src/​main/​java/​org/​apache/​fluss/​server/​coordinator/​CoordinatorEventProcessor.java Propagates ALTER TABLE tiering-group changes to manager
fluss-rpc/​src/​main/​java/​org/​apache/​fluss/​rpc/​protocol/​ApiKeys.java Bumps LAKE_TIERING_HEARTBEAT max version to 1
fluss-rpc/​src/​main/​proto/​FlussApi.proto Adds optional tiering_group field to heartbeat request
fluss-rpc/​src/​main/​java/​org/​apache/​fluss/​rpc/​netty/​client/​ServerConnection.java Enforces version compatibility when non-default group is used
fluss-flink/​fluss-flink-common/​src/​main/​java/​org/​apache/​fluss/​flink/​tiering/​source/​TieringSourceOptions.java Adds tiering service option tiering.group
fluss-flink/​fluss-flink-common/​src/​main/​java/​org/​apache/​fluss/​flink/​tiering/​source/​enumerator/​TieringSourceEnumerator.java Includes group in all heartbeats and validates group
Tests (multiple) Adds coverage for group validation, scheduling, heartbeat compatibility, and end-to-end tiering with groups
fluss-rust/​crates/​fluss/​src/​proto/​fluss.rs Regenerates Rust bindings for new heartbeat field

Review effort: Lite

Comment on lines +80 to +86
/** Validates a tiering group shared by table configuration and tiering service heartbeats. */
public static void validateTieringGroup(String group) {
if (group == null || group.isEmpty() || !group.equals(group.trim())) {
throw new IllegalConfigurationException(
"Tiering group must not be empty or have leading or trailing whitespace.");
}
}

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.

Addressed in e84d7dce1.

The shared validator now reuses TablePath.validateTableName, so table configuration and tiering services follow the existing Fluss table-name rules: 1–200 ASCII letters, digits, _ or -, with the __ prefix reserved. This rejects embedded whitespace and control characters without adding a separate naming rule. Configuration descriptions and documentation are updated, with regression coverage for invalid characters and the length boundaries.

Comment on lines +454 to 478
/** Returns the next pending table in the requested group, or {@code null} if none exists. */
@Nullable
public LakeTieringTableInfo requestTable(String group) {
return inWriteLock(
lock,
() -> {
Long tableId = pendingTieringTables.poll();
// now no any pending table, return directly
if (tableId == null) {
return null;
}
TablePath tablePath = tablePaths.get(tableId);
// the table has been dropped, request again
if (tablePath == null) {
return requestTable();
Iterator<Long> iterator = pendingTieringTables.iterator();
while (iterator.hasNext()) {
long tableId = iterator.next();
TablePath tablePath = tablePaths.get(tableId);
if (tablePath == null) {
iterator.remove();
continue;
}
if (!group.equals(tableTieringGroups.get(tableId))) {
continue;
}
iterator.remove();
doHandleStateChange(tableId, TieringState.Tiering);
long tieringEpoch = tableTierEpoch.get(tableId);
return new LakeTieringTableInfo(tableId, tablePath, tieringEpoch);
}
doHandleStateChange(tableId, TieringState.Tiering);
long tieringEpoch = tableTierEpoch.get(tableId);
return new LakeTieringTableInfo(tableId, tablePath, tieringEpoch);
return null;
});
}

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.

Addressed in e84d7dce1.

Pending tables are now indexed by group using Map<String, LinkedHashSet<Long>>. This preserves FIFO order within each group and avoids scanning other groups on a request. Pending group changes and table removal update the queues and pending count under the same lock; an already assigned round retains its existing assignment and epoch. Added a regression for ordering and counts after group moves and deletions. LakeTableTieringManagerTest and LakeTieringHeartbeatITCase pass.

Comment on lines 870 to 879
private static void validateOptionValue(ReadableConfig options, ConfigOption<?> option) {
try {
options.get(option);
if (option == ConfigOptions.TABLE_DATALAKE_TIERING_GROUP) {
FlussConfigUtils.validateTieringGroup(
options.get(ConfigOptions.TABLE_DATALAKE_TIERING_GROUP));
}
} catch (Throwable t) {
throw new InvalidConfigException(
String.format(

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.

Addressed in e84d7dce1.

Validation now compares the configuration option by key and reads the tiering-group value only once before passing it to the shared validator. TableDescriptorValidationTest passes.

Index pending tables by group with FIFO ordered sets, keeping reassignment,
removal and pending metrics synchronized under the coordinator lock.

Reuse table-name validation for tiering groups and compare configuration
options by key. Document the naming contract and cover validation boundaries
and queue behavior after group changes and table removal.
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.

[lake/tiering] Support group-aware table assignment for tiering services

2 participants