Skip to content

HADOOP-19966. ZKDelegationTokenSecretManager may fail to load tokens and keys from ZooKeeper on startup - #8688

Open
pan3793 wants to merge 3 commits into
apache:trunkfrom
pan3793:HADOOP-19966
Open

HADOOP-19966. ZKDelegationTokenSecretManager may fail to load tokens and keys from ZooKeeper on startup#8688
pan3793 wants to merge 3 commits into
apache:trunkfrom
pan3793:HADOOP-19966

Conversation

@pan3793

@pan3793 pan3793 commented Aug 17, 2026

Copy link
Copy Markdown
Member

Description of PR

ZKDelegationTokenSecretManager reads the CuratorCache immediately after
starting it, but CuratorCache populates asynchronously, so the cache can
still be empty and existing tokens/keys are silently not loaded into memory
on startup. The root container znodes also carry Curator's default data (the
local address), and since HADOOP-17835 (3.4.0) CuratorCache includes the root
node itself, so that data was fed to the key/token parsers.

This change:

  • Waits for CuratorCache initialization (forInitialized) before loading each cache.
  • Skips the root container znode (/ZKDTSMMasterKeyRoot, /ZKDTSMTokensRoot)
    when processing cache events and when streaming the cache, since it is a
    container rather than a key/token.
  • Creates the root container znodes with explicit empty data instead of
    Curator's default (the local address). Existing roots keep their old
    payload, so the root skip above is the actual fix.
  • Adds zk-dt-secret-manager.zkCacheInitTimeout (ms) to bound the wait for
    cache initialization; default 0 waits indefinitely, the tests set 10s.
  • Calls stopThreads() when startThreads() fails so a failed start does not
    leak the caches, counters, or the Curator client.
  • Restores the thread-local curator and closes the client in the tests that
    use setCurator.

Contains content generated by deepseek-v4-pro and Claude Fable 5.1

How was this patch tested?

mvn -pl hadoop-common-project/hadoop-common -am
-Dtest=TestZKDelegationTokenSecretManager test

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? Note: Automated CI
    testing doesn't cover all cases so manual testing with cloud storage is still
    required.
  • 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

If an AI tool was used:

@hadoop-yetus

Copy link
Copy Markdown

💔 -1 overall

Vote Subsystem Runtime Logfile Comment
+0 🆗 reexec 0m 0s Docker mode activated.
-1 ❌ patch 0m 20s #8688 does not apply to trunk. Rebase required? Wrong Branch? See https://cwiki.apache.org/confluence/display/HADOOP/How+To+Contribute for help.
Subsystem Report/Notes
Console output https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8688/1/console
versions git=2.34.1
Powered by Apache Yetus 0.14.1 https://yetus.apache.org

This message was automatically generated.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a ZooKeeper-backed delegation token startup issue in ZKDelegationTokenSecretManager where tokens/keys might not be loaded because CuratorCache initializes asynchronously, and where root container znodes created without explicit data can contain unexpected default payloads under Curator 5.2.0+.

Changes:

  • Wait for CuratorCache initialization before loading tokens/keys from the cache.
  • Skip processing the root container znodes when consuming cache events and when streaming the cache contents.
  • Create root znodes with explicit empty data, and adjust tests to better isolate/cleanup curator usage and reduce restart flakiness.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/ZKDelegationTokenSecretManager.java Ensures cache initialization completes before loading and avoids parsing container-root nodes; creates roots with explicit empty data.
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/token/delegation/TestZKDelegationTokenSecretManager.java Improves test reliability around restart loading and curator lifecycle handling.
Suppressed comments (1)

hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/token/delegation/TestZKDelegationTokenSecretManager.java:649

  • ExecutorService shutdown and tm1/tm2.destroy() are inside the main try-block, so an exception from invokeAll()/future.get() can leak threads and running secret managers. Ensure the executor is always shut down and both token managers are destroyed via a finally.
    try {
      // When the init method is called,
      // the ZKDelegationTokenSecretManager#startThread method will be called,
      // and the creatingParentContainersIfNeeded will be called to create the nameSpace.
      ExecutorService executorService = Executors.newFixedThreadPool(2);

      Callable<Boolean> tm1Callable = () -> {
        tm1.init();
        return true;
      };
      Callable<Boolean> tm2Callable = () -> {
        tm2.init();
        return true;
      };
      List<Future<Boolean>> futures = executorService.invokeAll(
          Arrays.asList(tm1Callable, tm2Callable));
      for (Future<Boolean> future : futures) {
        assertTrue(future.get());
      }

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +561 to +567
try {
DelegationTokenManager tm1 = new DelegationTokenManager(conf, new Text("foo"));

// When the init method is called,
// the ZKDelegationTokenSecretManager#startThread method will be called,
// and the creatingParentContainersIfNeeded will be called to create the nameSpace.
tm1.init();
// When the init method is called,
// the ZKDelegationTokenSecretManager#startThread method will be called,
// and the creatingParentContainersIfNeeded will be called to create the nameSpace.
tm1.init();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed: destroy() and executor shutdown/await moved into finally in both tests.

…and keys from ZooKeeper on startup

Wait for CuratorCache initialization before loading each cache, skip the
root container znode when processing cache events, and create the container
znodes with explicit empty data.

Assisted-by: deepseek-v4-pro
Move token manager destruction and executor shutdown into finally blocks in
testCreatingParentContainersIfNeeded and testMultipleInit.

Assisted-by: deepseek-v4-pro
@hadoop-yetus

Copy link
Copy Markdown

🎊 +1 overall

Vote Subsystem Runtime Logfile Comment
+0 🆗 reexec 6m 35s 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 _
+1 💚 mvninstall 26m 25s trunk passed
+1 💚 compile 8m 47s trunk passed with JDK Ubuntu-21.0.11+10-1-24.04.2-Ubuntu
+1 💚 compile 8m 51s trunk passed with JDK Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
+1 💚 checkstyle 0m 57s trunk passed
+1 💚 mvnsite 1m 20s trunk passed
+1 💚 javadoc 0m 45s trunk passed with JDK Ubuntu-21.0.11+10-1-24.04.2-Ubuntu
+1 💚 javadoc 0m 50s trunk passed with JDK Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
+1 💚 spotbugs 1m 50s trunk passed
+1 💚 shadedclient 19m 56s branch has no errors when building and testing our client artifacts.
_ Patch Compile Tests _
+1 💚 mvninstall 0m 41s the patch passed
+1 💚 compile 8m 57s the patch passed with JDK Ubuntu-21.0.11+10-1-24.04.2-Ubuntu
+1 💚 javac 8m 57s the patch passed
+1 💚 compile 9m 4s the patch passed with JDK Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
+1 💚 javac 9m 4s the patch passed
+1 💚 blanks 0m 0s The patch has no blanks issues.
+1 💚 checkstyle 0m 46s the patch passed
+1 💚 mvnsite 1m 11s the patch passed
+1 💚 javadoc 0m 51s the patch passed with JDK Ubuntu-21.0.11+10-1-24.04.2-Ubuntu
+1 💚 javadoc 0m 54s the patch passed with JDK Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
+1 💚 spotbugs 1m 52s the patch passed
+1 💚 shadedclient 17m 12s patch has no errors when building and testing our client artifacts.
_ Other Tests _
+1 💚 unit 18m 16s hadoop-common in the patch passed.
+1 💚 asflicense 0m 47s The patch does not generate ASF License warnings.
138m 2s
Subsystem Report/Notes
Docker ClientAPI=1.55 ServerAPI=1.55 base: https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8688/2/artifact/out/Dockerfile
GITHUB PR #8688
Optional Tests dupname asflicense compile javac javadoc mvninstall mvnsite unit shadedclient spotbugs checkstyle codespell detsecrets
uname Linux 409f60a6e195 5.15.0-179-generic #189-Ubuntu SMP Tue May 5 18:20:56 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
Build tool maven
Personality dev-support/bin/hadoop.sh
git revision trunk / d2bddcc
Default Java Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
Multi-JDK versions /usr/lib/jvm/java-21-openjdk-amd64:Ubuntu-21.0.11+10-1-24.04.2-Ubuntu /usr/lib/jvm/java-17-openjdk-amd64:Ubuntu-17.0.19+10-1-24.04.2-Ubuntu
Test Results https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8688/2/testReport/
Max. process+thread count 3158 (vs. ulimit of 10000)
modules C: hadoop-common-project/hadoop-common U: hadoop-common-project/hadoop-common
Console output https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8688/2/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.

@pan3793
pan3793 requested a review from slfan1989 August 19, 2026 06:08
@slfan1989 slfan1989 self-assigned this Aug 19, 2026
@pan3793

pan3793 commented Aug 21, 2026

Copy link
Copy Markdown
Member Author

@slfan1989, mind taking another look? still see such testing failure occasionly

https://github.com/apache/hadoop/actions/runs/32465896350/job/96725649556

@slfan1989

Copy link
Copy Markdown
Contributor

@slfan1989, mind taking another look? still see such testing failure occasionly

https://github.com/apache/hadoop/actions/runs/32465896350/job/96725649556

@pan3793 Thank you very much for your contribution! I will take a closer look at this PR today.

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

Thanks for working on this. The overall direction looks correct, and both Yetus and the targeted test suite pass.

However, I have two concerns before approving:

  1. The fixed 10-second cache initialization timeout may cause startup failures for large token/key trees or slower ZooKeeper environments.

  2. The added eventual wait in testNodesLoadedAfterRestart weakens the regression test, because the previous asynchronous implementation could also pass once the listener catches up. Since the production change guarantees initialization before init() returns, the test should retain an immediate assertion.

I also suggest cleaning up partially started caches and counters when initialization fails. For now, I think these points should be addressed before the PR is merged.

private void awaitCacheInitialized(CountDownLatch initialized,
String cacheName) throws IOException {
try {
if (!initialized.await(CACHE_INITIALIZED_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {

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.

initialized() is emitted only after all initial nodes have been loaded into the cache, so the required time depends on the size of the token/key trees and ZooKeeper latency. A fixed 10-second timeout can make an otherwise healthy service fail to start when the cache is large or the configured ZooKeeper timeout is longer.
Could this timeout be configurable, or derived from an existing ZooKeeper timeout setting, with an appropriate default?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Made configurable via zk-dt-secret-manager.zkCacheInitTimeout (ms) in f35a83a. Default 0 waits indefinitely; the tests set 10s.

// The good token should be loaded on startup, and removed after expiry.
id = smNew.decodeTokenIdentifier(token);
final AbstractDelegationTokenIdentifier idGood = id;
GenericTestUtils.waitFor(new Supplier<Boolean>() {

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.

The production change is intended to guarantee that the cache is fully loaded before tm.init() returns. Adding an eventual 5-second wait here weakens that regression guarantee: the old asynchronous implementation could also pass once its listener catches up.

Could we keep the immediate assertion after tm.init() ? Ideally, the test should fail without the awaitCacheInitialized() production change and pass with it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Restored the immediate assertion after tm.init() in f35a83a. The trunk failure linked above fails on exactly this assertion without the production change.

.build();
keyCache.listenable().addListener(keyCacheListener);
keyCache.start();
awaitCacheInitialized(keyCacheInitialized, "key");

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.

When cache initialization times out or is interrupted, startThreads() throws after the cache and shared counters have already been started. The caller does not automatically invoke stopThreads() after a failed initialization, so these resources may remain active. Could the partially started resources be closed before propagating the failure?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

startThreads() now calls stopThreads() before rethrowing on any failure, with an isRunning() guard so the cleanup never stops a running instance, in f35a83a.

.build();
tokenCache.listenable().addListener(tokenCacheListener);
tokenCache.start();
awaitCacheInitialized(tokenCacheInitialized, "token");

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.

Same Line#317

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Same as above, fixed in f35a83a.

…ces on failed start

Add zk-dt-secret-manager.zkCacheInitTimeout (ms, default 0 waits
indefinitely), call stopThreads() when startThreads() fails, and restore the
immediate assertion in testNodesLoadedAfterRestart.

Assisted-by: Claude Fable 5.1
@hadoop-yetus

Copy link
Copy Markdown

🎊 +1 overall

Vote Subsystem Runtime Logfile Comment
+0 🆗 reexec 0m 23s Docker mode activated.
_ Prechecks _
+1 💚 dupname 0m 1s 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 _
+1 💚 mvninstall 27m 49s trunk passed
+1 💚 compile 8m 32s trunk passed with JDK Ubuntu-21.0.12+8-1-24.04-Ubuntu
+1 💚 compile 8m 50s trunk passed with JDK Ubuntu-17.0.20+8-1-24.04-Ubuntu
+1 💚 checkstyle 0m 55s trunk passed
+1 💚 mvnsite 1m 11s trunk passed
+1 💚 javadoc 0m 54s trunk passed with JDK Ubuntu-21.0.12+8-1-24.04-Ubuntu
+1 💚 javadoc 0m 53s trunk passed with JDK Ubuntu-17.0.20+8-1-24.04-Ubuntu
+1 💚 spotbugs 1m 45s trunk passed
+1 💚 shadedclient 16m 57s branch has no errors when building and testing our client artifacts.
_ Patch Compile Tests _
+1 💚 mvninstall 0m 43s the patch passed
+1 💚 compile 8m 9s the patch passed with JDK Ubuntu-21.0.12+8-1-24.04-Ubuntu
+1 💚 javac 8m 9s the patch passed
+1 💚 compile 8m 44s the patch passed with JDK Ubuntu-17.0.20+8-1-24.04-Ubuntu
+1 💚 javac 8m 44s the patch passed
+1 💚 blanks 0m 0s The patch has no blanks issues.
-0 ⚠️ checkstyle 0m 50s /results-checkstyle-hadoop-common-project_hadoop-common.txt hadoop-common-project/hadoop-common: The patch generated 1 new + 22 unchanged - 0 fixed = 23 total (was 22)
+1 💚 mvnsite 1m 13s the patch passed
+1 💚 javadoc 0m 51s the patch passed with JDK Ubuntu-21.0.12+8-1-24.04-Ubuntu
+1 💚 javadoc 0m 53s the patch passed with JDK Ubuntu-17.0.20+8-1-24.04-Ubuntu
+1 💚 spotbugs 1m 52s the patch passed
+1 💚 shadedclient 16m 40s patch has no errors when building and testing our client artifacts.
_ Other Tests _
+1 💚 unit 18m 21s hadoop-common in the patch passed.
+1 💚 asflicense 0m 47s The patch does not generate ASF License warnings.
128m 17s
Subsystem Report/Notes
Docker ClientAPI=1.55 ServerAPI=1.55 base: https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8688/3/artifact/out/Dockerfile
GITHUB PR #8688
Optional Tests dupname asflicense compile javac javadoc mvninstall mvnsite unit shadedclient spotbugs checkstyle codespell detsecrets
uname Linux 4cd208ebe904 5.15.0-190-generic #200-Ubuntu SMP Fri Aug 7 15:06:04 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
Build tool maven
Personality dev-support/bin/hadoop.sh
git revision trunk / f35a83a
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-8688/3/testReport/
Max. process+thread count 3159 (vs. ulimit of 10000)
modules C: hadoop-common-project/hadoop-common U: hadoop-common-project/hadoop-common
Console output https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8688/3/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.

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.

4 participants