Skip to content

HADOOP-19987. ObserverReadProxyProvider's probing pool blocks when saturated instead of rejecting - #8724

Open
joseluisll wants to merge 1 commit into
apache:trunkfrom
joseluisll:HADOOP-19987
Open

joseluisll wants to merge 1 commit into
apache:trunkfrom
joseluisll:HADOOP-19987

Conversation

@joseluisll

Copy link
Copy Markdown
Contributor

Description of PR

https://issues.apache.org/jira/browse/HADOOP-19987

getHAServiceStateWithTimeout catches RejectedExecutionException to fall back to the active NameNode, but that fallback is unreachable: the probing pool was a BlockingThreadPoolExecutorService, whose submit() blocks on a semaphore instead of rejecting. A caller that should have been shed onto the active NN is parked instead — while holding the provider's monitor, since the probe runs inside the synchronized changeProxy().

Latent today: the only production caller is inside changeProxy(), so there is at most one probe in flight per provider. Reaching saturation needs dfs.client.failover.namenode.ha-state.probe.timeout set above its 0 default plus probes that don't return on interrupt — but when it happens the monitor is held forever.

Fix: use a plain ThreadPoolExecutor with the same shape (4 threads, 128-deep queue, 10s idle timeout, daemon threads) and the default AbortPolicy. Capacity is unchanged at 132; the field type widens to ExecutorService. Thread names keep the nn-ha-state-probing-pool<N>-t<M> shape, with the per-thread counter now starting at t0 and <N> counting probing pools alone.

How was this patch tested?

Two new tests in TestObserverReadProxyProvider, each running the call under test on its own thread with an explicit deadline so a hang names the stuck call:

  • testProbingPoolRejectsOnceSaturatedsubmit() past capacity throws instead of blocking.
  • testFallsBackToActiveWhenProbingPoolSaturated — the HA-state probe returns and falls back to the active NN.

Both fail against the old pool, blocking for the full 20s deadline.

Full CI green on the fork (common, hdfs - other, hdfs - slow, hdfs-rbf, mr, other, yarn-server-rm on Java 17; build-only on Java 21 and 25): https://github.com/joseluisll/hadoop/actions/runs/34147776162

common needed a rerun for TestZKDelegationTokenSecretManager#testNodesLoadedAfterRestart, an unrelated pre-existing flake in hadoop-common; this patch only touches hadoop-hdfs-client.

For code changes:

  • Does the title of this PR start with the corresponding JIRA issue id (e.g. 'HADOOP-17799. Your PR title ...')?
  • Object storage: Have the integration tests been executed and the endpoint declared according to the connector-specific documentation?
  • If adding new dependencies to the code, are these dependencies licensed in a way that is compatible for inclusion under ASF 2.0?
  • If applicable, have you updated the LICENSE, LICENSE-binary, NOTICE-binary files?

AI Tooling

Contains content generated by Claude Code.

…turated instead of rejecting

getHAServiceStateWithTimeout catches RejectedExecutionException and returns
null so the caller falls back to the active NameNode. That fallback cannot
be reached.

The pool was a BlockingThreadPoolExecutorService, which wraps the real
executor in a SemaphoredDelegatingExecutor whose submit() blocks on
queueingPermits.acquire() rather than rejecting, so it never throws
RejectedExecutionException. A caller that should have been shed onto the
active NN is instead parked -- while holding this provider's monitor, since
the probe is reached through the synchronized changeProxy(), so every other
thread needing that monitor waits behind it.

This is latent rather than something users hit today. The one production
caller runs inside the synchronized changeProxy(), so a provider has at most
one probe in flight, and dfs.client.failover.namenode.ha-state.probe.timeout
defaults to 0, which makes task.get() wait indefinitely. Saturation needs
that timeout set above 0, a NameNode whose RPC does not return on interrupt
so that cancelled probes keep occupying the four threads, and enough further
probes to fill the queue behind them. The consequence when it does happen is
severe and permanent: the provider's monitor is held by a thread that will
never make progress.

Use a plain ThreadPoolExecutor with the same shape (4 threads, 128-deep
queue, 10s idle timeout via allowCoreThreadTimeOut, daemon threads) and the
default AbortPolicy, so saturation reaches the fallback written for it.
Capacity is unchanged: the semaphore permitted activeTasks + waitingTasks,
the same 132 as four threads plus a 128-deep queue. The field type widens to
ExecutorService. Under saturation the provider now sheds load onto the
active NN rather than applying backpressure, which is what the fallback was
written to do.

Thread names keep their nn-ha-state-probing-pool<N>-t<M> shape, so a dump
still says which provider a thread belongs to in a JVM holding one per
nameservice. Two details shift: the per-thread counter starts at t0 rather
than t1, and <N> now counts probing pools alone, where before it came from a
counter BlockingThreadPoolExecutorService shares among all of its users.

Adds two tests. Against the old pool both fail, blocking for the full 20s
deadline:

  submit past capacity did not return within 20000ms: it blocked instead of
  failing fast
  HA state probe on a saturated pool did not return within 20000ms: it
  blocked instead of failing fast

Each runs the call under test on its own thread with an explicit deadline: a
bare @timeout would catch the hang but report only "timed out after N
seconds" without naming the stuck call. The pool and its sizing are exposed
@VisibleForTesting so the test can fill it exactly, which also removes the
magic numbers the constructor previously hardcoded.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hadoop-yetus

Copy link
Copy Markdown

🎊 +1 overall

Vote Subsystem Runtime Logfile Comment
+0 🆗 reexec 1m 20s Docker mode activated.
_ Prechecks _
+1 💚 dupname 0m 0s No case conflicting files found.
+0 🆗 codespell 0m 0s codespell was not available.
+0 🆗 detsecrets 0m 0s detect-secrets was not available.
+1 💚 @author 0m 0s The patch does not contain any @author tags.
+1 💚 test4tests 0m 0s The patch appears to include 1 new or modified test files.
_ trunk Compile Tests _
+0 🆗 mvndep 2m 12s Maven dependency ordering for branch
+1 💚 mvninstall 53m 28s trunk passed
+1 💚 compile 6m 5s trunk passed with JDK Ubuntu-21.0.12+8-1-24.04-Ubuntu
+1 💚 compile 6m 32s trunk passed with JDK Ubuntu-17.0.20+8-1-24.04-Ubuntu
+1 💚 checkstyle 2m 15s trunk passed
+1 💚 mvnsite 3m 20s trunk passed
+1 💚 javadoc 2m 36s trunk passed with JDK Ubuntu-21.0.12+8-1-24.04-Ubuntu
+1 💚 javadoc 2m 33s trunk passed with JDK Ubuntu-17.0.20+8-1-24.04-Ubuntu
+1 💚 spotbugs 8m 9s trunk passed
+1 💚 shadedclient 37m 17s branch has no errors when building and testing our client artifacts.
_ Patch Compile Tests _
+0 🆗 mvndep 0m 47s Maven dependency ordering for patch
+1 💚 mvninstall 2m 19s the patch passed
+1 💚 compile 5m 30s the patch passed with JDK Ubuntu-21.0.12+8-1-24.04-Ubuntu
+1 💚 javac 5m 30s the patch passed
+1 💚 compile 6m 6s the patch passed with JDK Ubuntu-17.0.20+8-1-24.04-Ubuntu
+1 💚 javac 6m 6s the patch passed
+1 💚 blanks 0m 0s The patch has no blanks issues.
+1 💚 checkstyle 1m 48s the patch passed
+1 💚 mvnsite 2m 27s the patch passed
+1 💚 javadoc 1m 39s the patch passed with JDK Ubuntu-21.0.12+8-1-24.04-Ubuntu
+1 💚 javadoc 1m 43s the patch passed with JDK Ubuntu-17.0.20+8-1-24.04-Ubuntu
+1 💚 spotbugs 7m 38s the patch passed
+1 💚 shadedclient 36m 45s patch has no errors when building and testing our client artifacts.
_ Other Tests _
+1 💚 unit 2m 40s hadoop-hdfs-client in the patch passed.
+1 💚 unit 260m 10s hadoop-hdfs in the patch passed.
+1 💚 asflicense 0m 52s The patch does not generate ASF License warnings.
454m 58s
Subsystem Report/Notes
Docker ClientAPI=1.56 ServerAPI=1.56 base: https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8724/1/artifact/out/Dockerfile
GITHUB PR #8724
Optional Tests dupname asflicense compile javac javadoc mvninstall mvnsite unit shadedclient spotbugs checkstyle codespell detsecrets
uname Linux 00d302e278df 5.15.0-185-generic #195-Ubuntu SMP Fri Jun 19 17:11:50 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
Build tool maven
Personality dev-support/bin/hadoop.sh
git revision trunk / 82eefaa
Default Java Ubuntu-17.0.20+8-1-24.04-Ubuntu
Multi-JDK versions /usr/lib/jvm/java-21-openjdk-amd64:Ubuntu-21.0.12+8-1-24.04-Ubuntu /usr/lib/jvm/java-17-openjdk-amd64:Ubuntu-17.0.20+8-1-24.04-Ubuntu
Test Results https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8724/1/testReport/
Max. process+thread count 2656 (vs. ulimit of 10000)
modules C: hadoop-hdfs-project/hadoop-hdfs-client hadoop-hdfs-project/hadoop-hdfs U: hadoop-hdfs-project
Console output https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8724/1/console
versions git=2.43.0 maven=3.9.15 spotbugs=4.9.7
Powered by Apache Yetus 0.14.1 https://yetus.apache.org

This message was automatically generated.

@joseluisll

joseluisll commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

@pan3793 @slfan1989 @Hexiaoqiao This PR is Green and ready to be reviewed.

It is a prerrequisite for HADOOP-19979, that will fix 4 flaky conditions so that we reduce later the GHA exclude list, as explained on HADOOP-19981 [umbrella for all GHA reactivation candidates from excluded-tests.txt] and its subtasks [specific identified candidates for reactivation].

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