Skip to content

HDDS-15533. DNS refresh on heartbeat failure for DN to SCM#10844

Open
kerneltime wants to merge 5 commits into
apache:masterfrom
kerneltime:HDDS-15533-dn-scm-refresh
Open

HDDS-15533. DNS refresh on heartbeat failure for DN to SCM#10844
kerneltime wants to merge 5 commits into
apache:masterfrom
kerneltime:HDDS-15533-dn-scm-refresh

Conversation

@kerneltime

@kerneltime kerneltime commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

Adds DNS refresh on the DN→SCM heartbeat path, completing the // TODO: HDDS-15533 left in HostAndPort by HDDS-15682 (#10612).

#10612 keys SCM-node identity on the stable configured host:port, but it resolves the address once and freezes it — so a datanode keeps dialing a dead IP after an SCM pod is rescheduled to a new IP (e.g. Kubernetes). This change:

  • HostAndPort — makes the cached address volatile and splits refresh into resolveLatest() (pure re-resolve; returns the new address only if the IP changed, never mutates) and setAddress() (commit). Identity (equals/hashCode) stays keyed on host:port, so instances remain stable map keys across refreshes.
  • SCMConnectionManager.refreshSCMServer() — builds the replacement endpoint dialing the candidate address first, then, only on success and under the write lock, commits setAddress and swaps the map value under the same host:port key; the stale proxy is closed outside the lock and the swap is logged at INFO. A build failure or a lost race with a concurrent removeSCMServer commits nothing, so the next heartbeat failure retries cleanly. Because the key is stable there is no re-keying, collision checking, or StateContext queue migration.
  • HeartbeatEndpointTask — triggers the refresh on a connection-class heartbeat failure (ConnectionFailureUtils) once the consecutive-missed-heartbeat threshold is reached, gated by the existing ozone.client.failover.resolve-needed flag. Recon (passive) endpoints are excluded.
  • EndpointStateMachine.close() — shuts the executor down in a finally block so a proxy close failure cannot leak the endpoint thread (refresh makes close() a recurring runtime path).

Behavior note: the rebuilt endpoint starts at GETVERSION, so the DN re-runs version/register against the refreshed SCM before heartbeating. This is intentional — a rescheduled SCM pod is effectively a fresh process — and matches what a DN restart would do.

New config: hdds.heartbeat.address.refresh.threshold (default 3). With ozone.client.failover.resolve-needed=false (the default), behaviour is unchanged.

Enabling in Kubernetes

ozone.client.failover.resolve-needed = true   # master switch, default false
hdds.heartbeat.address.refresh.threshold = 3  # consecutive failures before re-resolve (default)

Replaces the earlier stacked attempt (szetszwo#11) and supersedes the closed #10488, rebuilt directly on master after #10612 merged.

What is the link to the Apache JIRA?

https://issues.apache.org/jira/browse/HDDS-15533

How was this patch tested?

  • TestHeartbeatEndpointTaskDnsRefresh (4): a connection-class failure at the threshold triggers the refresh; flag-off, application-level errors, and below-threshold do not.
  • TestSCMConnectionManager (3 new): successful rebuild on IP change; build failure leaves the endpoint and cached address unchanged (guards the stuck-until-restart regression); a concurrent removeSCMServer during the unlocked DNS-resolve window abandons the refresh without committing.
  • TestHostAndPort (3): resolveLatest() no-op on unchanged IP; setAddress rejects null; address mutation does not change map-key identity.
  • mvn checkstyle:check passes on hadoop-hdds/common and hadoop-hdds/container-service; full build and test suites via CI.

Generated-by: Claude Code (Fable 5)

Resolves the HDDS-15682 TODO in HostAndPort: makes the cached address threadsafe (volatile) and adds
refresh() to re-resolve host:port on demand. On a connection-class heartbeat failure past a threshold
(gated by ozone.client.failover.resolve-needed), HeartbeatEndpointTask asks
SCMConnectionManager.refreshSCMServer to re-resolve the SCM peer and, on an IP change, rebuild the
endpoint under the same host:port key. The stable key means no re-keying or StateContext queue
migration is needed.

New knob: hdds.heartbeat.address.refresh.threshold (default 3).

Generated-by: Claude Code (Opus 4.8)
…eshed address

Review follow-up: HostAndPort.refresh() committed the new IP before SCMConnectionManager rebuilt the
endpoint, so a buildScmEndpoint failure or a lost race left the cached address ahead of the live
proxy -- and the next "IP unchanged" check then blocked recovery until DN restart.

Split refresh() into resolveLatest() (no mutation) + setAddress(); refreshSCMServer now builds the
replacement with the candidate address and commits it only on success. Adds TestSCMConnectionManager
cases for the successful swap and for build-failure-leaves-state-unchanged.

Generated-by: Claude Code (Opus 4.8)
…ak-safe

Re-review fixes:
- refreshSCMServer: log the old -> new address at INFO (the swap was invisible to operators), and
  treat a RuntimeException from closing the stale proxy as cleanup-only -- the swap is already
  committed, and the exception would otherwise escape the heartbeat task.
- EndpointStateMachine.close(): shut the executor down in a finally block so a proxy close failure
  cannot leak the endpoint thread; refreshSCMServer makes close() a recurring runtime path.
- HostAndPort.setAddress: reject null.

Generated-by: Claude Code (Fable 5)
…refresh test

- TestHostAndPort: resolveLatest() no-op on unchanged IP, setAddress null rejection, and map-key
  identity unchanged by address mutation.
- TestSCMConnectionManager: removing the endpoint during the unlocked DNS-resolve window abandons
  the refresh without committing the re-resolved address.

Generated-by: Claude Code (Fable 5)

@szetszwo szetszwo 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.

@kerneltime , thanks for the update! Just have a comment about the conf key.

"2s";
/** Missed heartbeats against one SCM before the DN re-resolves its hostname (HDDS-15533). */
public static final String HDDS_HEARTBEAT_ADDRESS_REFRESH_THRESHOLD =
"hdds.heartbeat.address.refresh.threshold";

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.

Let's add "missed-count", i.e.

  •   hdds.heartbeat.address.refresh.missed-count-threshold
    

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.

2 participants