Skip to content

feat: Define clone sync criteria in CoordinatorDynamicConfig - #20247

Open
kfaraz wants to merge 5 commits into
apache:masterfrom
kfaraz:clone_sync_threshold
Open

feat: Define clone sync criteria in CoordinatorDynamicConfig#20247
kfaraz wants to merge 5 commits into
apache:masterfrom
kfaraz:clone_sync_threshold

Conversation

@kfaraz

@kfaraz kfaraz commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Description

When cloning historicals for a blue-green deployment using CoordinatorDynamicConfig.cloneServers, it can often be difficult to determine if the clones are already synced and can be safely terminated. This is primarily because the source server keeps getting assigned new segments and the clone target is always playing catch up.

The metric that has been used so far to determine if a clone is sufficiently "synced" is the segmentLoadsRemaining returned by the /cloneStatus API, but that number often fails to fall below any reasonable threshold thereby slowing down the deployment.

Changes

  • Add a configurable CloneSyncCriteria to CoordinatorDynamicConfig
  • The criteria is a function of segments "pending sync" during a coordinator run i.e. segments which are already loaded on the source server but still loading at the target server at the start of the run.
  • If the criteria is met, update the state of the clone to SYNCED and emit a metric
  • In subsequent runs, it is possible for the state to go back to IN_PROGRESS if the clone starts lagging behind again

This PR has:

  • been self-reviewed.
  • added documentation for new or modified features or behaviors.
  • a release note entry in the PR description.
  • added Javadocs for most classes and all non-trivial methods. Linked related entities via Javadoc links.
  • added or updated version, license, or notice information in licenses.yaml
  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • added unit tests or modified existing tests to cover new code paths, ensuring the threshold for code coverage is met.
  • added integration tests.
  • been tested in a test Druid cluster.

@FrankChen021 FrankChen021 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Severity Findings
P0 0
P1 2
P2 1
P3 0
Total 3

Reviewed 14 of 14 changed files.


This is an automated review by Codex GPT-5.6-Luna(max)

private boolean isSynced(CloningStats stats, CloneSyncCriteria criteria)
{
return stats.segmentsPendingSync <= criteria.getMaxSegmentsPendingSync()
&& stats.percentPendingSync() <= criteria.getMaxPercentPendingSync();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P1] Allow either sync threshold to qualify

The two configured limits are intersected here, so maxSegmentsPendingSync cannot independently make a clone SYNCED. The new test configures (maxSegmentsPendingSync=1, maxPercentPendingSync=10.0) with nine source segments and one pending segment: the absolute limit is met, but the calculated percentage is 11.11%, leaving the status IN_PROGRESS and contradicting the test's expected SYNCED state. Combine the thresholds according to the documented number-or-percentage semantics (or change the public contract and test).


private final Set<String> turboLoadingNodes;
private final Map<String, String> cloneServers;
private final CloneSyncCriteria cloneSyncCriteria;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P1] Include clone criteria in config equality

The new cloneSyncCriteria field is not included in CoordinatorDynamicConfig.equals() or hashCode(). The existing CoordinatorDynamicConfigTest uses EqualsVerifier without ignoring this field, so the test will reject the class; in production, BaseDynamicConfigSyncer also treats a threshold-only update as unchanged and does not invalidate its inSyncBrokers snapshot. Add value-based equality for the criteria and include it in both config methods.

final ServerCloneStatus oldStatus = cloneStatusManager.getStatusForServer(targetServerName);

if (newStatus.state() == ServerCloneStatus.State.SYNCED
&& oldStatus != null && oldStatus.state() != ServerCloneStatus.State.SYNCED) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Emit the initial SYNCED transition metric

A coordinator starts with no entry in CloneStatusManager, so a clone that already satisfies the criteria on its first run has oldStatus == null. This condition then suppresses Stats.Segments.PENDING_SYNC even though the new state is SYNCED; the added simulation test expects that metric on its initial run. Treat the absent status as a transition (or otherwise emit the metric for the initial qualifying run).

@jtuglu1

jtuglu1 commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

cc @maytasm

@FrankChen021 FrankChen021 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Severity Findings
P0 0
P1 0
P2 2
P3 1
Total 3
Severity Findings
P0 0
P1 0
P2 2
P3 1
Total 3

Reviewed 16 of 16 changed files.


This is an automated review by Codex GPT-5.6-Luna(max)


stats.add(
Stats.Segments.PENDING_SYNC_ON_CLONE,
RowKey.of(Dimension.SERVER, targetServerName),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Include tier in pending-sync metric

segment/clone/pendingSync/count is documented with server,tier dimensions, and the other clone metrics now include the target tier, but this row key is still server-only. In a multi-tier deployment the emitted event cannot be filtered or grouped by tier and does not match the documented metric contract. Include Dimension.TIER for the target in this row key.

final PartialLoadProfile sourceProfile = sourceServer.getProjectedProfile(segment);
if (shouldLoadSegmentOnTargetServer(segment, sourceProfile, targetServer, targetProjectedSegments)) {
loadSegmentOnTargetServer(segment, sourceProfile, targetServer, params);
cloningStats.incrementMissingSegmentCount(sourceServer.isServingSegment(segment));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Do not count skipped segments as pending

getLoadableSegment explicitly returns null for unused segments, so this call can queue no load. The counter is incremented unconditionally after the call whenever the source still serves the segment. An unused segment that remains in the source inventory therefore contributes to segmentsPendingSync and its percentage until it is unloaded, and can keep a clone IN_PROGRESS even though the clone duty intentionally skips that segment. Count it only when the segment is eligible for reconciliation, and cover a served-but-unused source segment.

|`segment/moveSkipped/count`|Number of segments that were chosen for balancing but could not be moved. This can occur when segments are already optimally placed.|`dataSource`, `server`, `tier`, `description`|Varies|
|`segment/dropSkipped/count`|Number of segments that could not be dropped from any server.|`dataSource`, `server`, `tier`, `description`|Varies|
|`segment/clone/assigned/count`|Number of segments assigned to be loaded on a historical clone.|`dataSource`, `server`, `tier`|Varies|
|`segment/clone/dropped/count`|Number of segments assigned to be loaded on a historical clone.|`dataSource`, `server`, `tier`|Varies|

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P3] Correct dropped-clone metric description

This is the description for segment/clone/dropped/count, but it says segments are assigned to be loaded. The stat is emitted from DROPPED_FROM_CLONE when a drop is queued, so the description should say that segments are assigned to be dropped; otherwise operators will interpret this new metric backwards.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants