Skip to content

[CELEBORN-2429][CIP-22] Batch worker/application heartbeats into aggregated raft log entries - #3810

Open
yew1eb wants to merge 1 commit into
apache:mainfrom
yew1eb:CELEBORN-2429
Open

[CELEBORN-2429][CIP-22] Batch worker/application heartbeats into aggregated raft log entries#3810
yew1eb wants to merge 1 commit into
apache:mainfrom
yew1eb:CELEBORN-2429

Conversation

@yew1eb

@yew1eb yew1eb commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

In HA mode, aggregate worker/application heartbeats on the master leader over a short time window (default 1s) and replicate them as ONE BatchHeartbeat raft log entry, instead of one entry per heartbeat.

  • New HeartbeatAggregator on the leader: pending heartbeats are kept in per-worker/per-app maps (newest wins, buffer intrinsically bounded), a scheduled thread flushes one batch per window; heartbeat RPCs are enqueued asynchronously and replied immediately from leader-local memory.
  • New internal protobuf messages BatchHeartbeatRequest / PbBatchHeartbeatRequest (Type.BatchHeartbeat = 31); MetaHandler expands the batch and applies each child heartbeat through the same handleWorkerHeartbeat / handleAppHeartbeat helpers as the single-entry path.
  • Fully additive and off by default:
    celeborn.master.ha.heartbeat.batch.enabled (false),
    celeborn.master.ha.heartbeat.batch.interval (1s).

Design doc (CIP-22 Batched Heartbeat Raft Writes): https://docs.google.com/document/d/1YXDCjk_kR_5jyRGmUz1DqImjnAtsr7oFEwpYKK1NxAE/edit?usp=sharing

Why are the changes needed?

Heartbeats are the most frequent metadata writes: A/10 + W/30 raft entries/s for A apps and W workers. On one of our production clusters at peak (900+ running apps + 200 workers, ~97 heartbeat entries/s), follower apply lag (RatisApplyCompletedIndexDiff) grows in lockstep with RunningApplicationCount (~50 -> 1000). A follower flame graph shows the cost is the per-entry raft pipeline (appendEntries ~35%, log flush/fsync ~20%), not apply compute (~7.7%).
image

image

Heartbeats are periodic, self-healing, timeout-tolerant, and their replies are built from leader-local memory — they do not need a synchronous raft commit each. Batching cuts raft write volume by ~100x at peak.

Does this PR resolve a correctness bug?

  • Yes

Does this PR introduce any user-facing change?

  • Yes

How was this patch tested?

  • New HeartbeatAggregatorSuiteJ (single-node raft, full offer → submit → replicate → apply chain): N offers produce far fewer entries; empty windows produce none; duplicate keys within one window collapse to the newest heartbeat.
  • MasterStateMachineSuiteJ#testBatchHeartbeat: batched entry applies correctly and survives the ResourceRequestPbMetaRequest wire round-trip.
  • Existing master suites pass.

@yew1eb

yew1eb commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

@yew1eb
yew1eb force-pushed the CELEBORN-2429 branch 9 times, most recently from f4f3e24 to 4ece3b1 Compare August 20, 2026 01:01
…egated raft log entries

Aggregate heartbeats on the leader over a short time window (default 1s)
into one BatchHeartbeat raft entry, cutting raft write volume by ~100x at
peak. Off by default: celeborn.master.ha.heartbeat.batch.enabled.
}

public void stop() {
flushExecutor.shutdownNow();

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.

There is no final flush before the master stops. Those heartbeats are always lost. Should we try to flush once before shut down.

.setRequestId(MasterClient.genRequestId())
.setBatchHeartbeatRequest(
ResourceProtos.BatchHeartbeatRequest.newBuilder()
.addAllWorkerHeartbeats(drainedWorkers)

@AmandeepSingh285 AmandeepSingh285 Aug 22, 2026

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.

If the active leader dies before the batched worker heartbeat is committed to Ratis, the new leader keeps the last applied worker snapshot. Load-aware slot allocation then ranks disks from stale flush/fetch times, usable space, and active slots, so traffic can keep landing on workers that have since become hot or unhealthy.
highWorkload is applied only when that heartbeat is applied. If the lost heartbeat was the one that marked the worker overloaded, the new leader will still treat it as available and offer new slots until the next heartbeat (~30s).
The same gap exists for decommission: the worker may already be draining from an event it received, but the status it reported in the lost heartbeat never reaches the new leader. Until the next heartbeat applies, the master can still give applications slots on a worker that is leaving the cluster.

The impact of losing a worker/app heartbeat after this change could be significantly higher, as losing the subsequent heartbeat would further extend the period during which the leader operates on stale worker state.

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.

2 participants