Skip to content

Conversation

@codelipenghui
Copy link
Contributor

@codelipenghui codelipenghui commented Jul 30, 2025

Summary

  • Fixed NullPointerException in namespace deletion when redirecting to geo-replicated clusters
  • Changed TLS URL selection logic to use remote cluster's TLS config instead of local broker's config
  • Added comprehensive test coverage for all TLS configuration scenarios

Problem

When deleting a namespace that needs to be redirected to a remote cluster in geo-replication scenarios, the code was incorrectly using the local broker's TLS configuration (config().isTlsEnabled() || \!isRequestHttps()) to determine whether to use HTTP or HTTPS URLs for the redirect. This caused issues when:

  1. Local broker had different TLS settings than the remote cluster
  2. Remote cluster's serviceUrlTls was null but TLS logic tried to access it
  3. Resulted in NullPointerException: Cannot invoke "String.length()" because "<parameter2>" is null at NamespacesBase.java:440
bin/pulsar-admin namespaces delete xxx/yyy
2025-07-30T18:09:05,422+0000 [AsyncHttpClient-9-1] WARN  org.apache.pulsar.client.admin.internal.BaseResource - [http://aws-uat-use1-broker.ssi.svc.cluster.local:8080/admin/v2/namespaces/xxx/yyy?force=false] Failed to perform http delete request: javax.ws.rs.InternalServerErrorException: HTTP 500 {"reason":"\n --- An unexpected error occurred in the server ---\n\nMessage: Cannot invoke \"String.length()\" because \"<parameter2>\" is null\n\nStacktrace:\n\njava.net.MalformedURLException: Cannot invoke \"String.length()\" because \"<parameter2>\" is null\n\tat java.base/java.net.URL.<init>(Unknown Source)\n\tat java.base/java.net.URL.<init>(Unknown Source)\n\tat java.base/java.net.URL.<init>(Unknown Source)\n\tat org.apache.pulsar.broker.admin.impl.NamespacesBase.lambda$precheckWhenDeleteNamespace$33(NamespacesBase.java:440)\n\tat java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(Unknown Source)\n\tat java.base/java.util.concurrent.CompletableFuture.postComplete(Unknown Source)\n\tat java.base/java.util.concurrent.CompletableFuture.complete(Unknown Source)\n\tat org.apache.pulsar.metadata.impl.ZKMetadataStore.handleGetResult(ZKMetadataStore.java:300)\n\tat org.apache.pulsar.metadata.impl.ZKMetadataStore.lambda$batchOperation$9(ZKMetadataStore.java:244)\n\tat java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)\n\tat java.base/java.util.concurrent.FutureTask.run(Unknown Source)\n\tat java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)\n\tat io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)\n\tat java.base/java.lang.Thread.run(Unknown Source)\nCaused by: java.lang.NullPointerException: Cannot invoke \"String.length()\" because \"<parameter2>\" is null\n\t... 16 more\n"}

 --- An unexpected error occurred in the server ---

Message: Cannot invoke "String.length()" because "<parameter2>" is null

Stacktrace:

java.net.MalformedURLException: Cannot invoke "String.length()" because "<parameter2>" is null
	at java.base/java.net.URL.<init>(Unknown Source)
	at java.base/java.net.URL.<init>(Unknown Source)
	at java.base/java.net.URL.<init>(Unknown Source)
	at org.apache.pulsar.broker.admin.impl.NamespacesBase.lambda$precheckWhenDeleteNamespace$33(NamespacesBase.java:440)
	at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(Unknown Source)
	at java.base/java.util.concurrent.CompletableFuture.postComplete(Unknown Source)
	at java.base/java.util.concurrent.CompletableFuture.complete(Unknown Source)
	at org.apache.pulsar.metadata.impl.ZKMetadataStore.handleGetResult(ZKMetadataStore.java:300)
	at org.apache.pulsar.metadata.impl.ZKMetadataStore.lambda$batchOperation$9(ZKMetadataStore.java:244)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
	at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException: Cannot invoke "String.length()" because "<parameter2>" is null
	... 16 more


Reason:
 --- An unexpected error occurred in the server ---

Message: Cannot invoke "String.length()" because "<parameter2>" is null

Stacktrace:

java.net.MalformedURLException: Cannot invoke "String.length()" because "<parameter2>" is null
	at java.base/java.net.URL.<init>(Unknown Source)
	at java.base/java.net.URL.<init>(Unknown Source)
	at java.base/java.net.URL.<init>(Unknown Source)
	at org.apache.pulsar.broker.admin.impl.NamespacesBase.lambda$precheckWhenDeleteNamespace$33(NamespacesBase.java:440)
	at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(Unknown Source)
	at java.base/java.util.concurrent.CompletableFuture.postComplete(Unknown Source)
	at java.base/java.util.concurrent.CompletableFuture.complete(Unknown Source)
	at org.apache.pulsar.metadata.impl.ZKMetadataStore.handleGetResult(ZKMetadataStore.java:300)
	at org.apache.pulsar.metadata.impl.ZKMetadataStore.lambda$batchOperation$9(ZKMetadataStore.java:244)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
	at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException: Cannot invoke "String.length()" because "<parameter2>" is null
	... 16 more

Solution

Changed the logic in NamespacesBase.java:439 from:

if (\!config().isTlsEnabled() || \!isRequestHttps()) {

to:

if (\!replClusterData.isBrokerClientTlsEnabled()) {

This ensures that the TLS URL selection is based on the remote cluster's TLS configuration, not the local broker's configuration.

Test Coverage

Added testDeleteNamespaceUsesRemoteClusterTlsConfig() which validates three scenarios:

  1. Remote cluster with TLS disabled: Should use HTTP serviceUrl (port 8080)
  2. Remote cluster with TLS enabled: Should use HTTPS serviceUrlTls (port 8443)
  3. Remote cluster with TLS enabled but no TLS URL: Should properly handle error case

Test Plan

  • Existing tests pass
  • New test validates correct TLS URL selection logic
  • Checkstyle violations resolved
  • Test covers edge cases (missing TLS URLs)

Documentation

  • doc
  • doc-required
  • doc-not-needed
  • doc-complete

…ation

When deleting a namespace that needs to be redirected to a remote cluster,
the code was incorrectly using the local broker's TLS configuration to
determine whether to use HTTP or HTTPS URLs. This caused NullPointerException
when the remote cluster had different TLS settings.

Changed the logic to use the remote cluster's brokerClientTlsEnabled setting
instead of the local broker's TLS configuration, ensuring proper URL selection
based on the target cluster's capabilities.

Added comprehensive test coverage for three scenarios:
- Remote cluster with TLS disabled (uses HTTP)
- Remote cluster with TLS enabled (uses HTTPS)
- Remote cluster with TLS enabled but no TLS URL (proper error handling)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions github-actions bot added doc-label-missing doc-not-needed Your PR changes do not impact docs and removed doc-label-missing labels Jul 30, 2025
@apache apache deleted a comment from github-actions bot Jul 30, 2025
@codelipenghui codelipenghui changed the title Fix namespace deletion TLS URL selection for geo-replication [fix][broker] fix namespace deletion TLS URL selection for geo-replication Jul 30, 2025
@codelipenghui codelipenghui self-assigned this Jul 30, 2025
@codelipenghui codelipenghui added the type/bug The PR fixed a bug or issue reported a bug label Jul 30, 2025
@codelipenghui codelipenghui added this to the 4.1.0 milestone Jul 30, 2025
Copy link
Member

@lhotari lhotari left a comment

Choose a reason for hiding this comment

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

LGTM

@lhotari
Copy link
Member

lhotari commented Jul 30, 2025

Not directly related to this PR, but noticed that the test was using "global" clusters, it seems that it's a TopicName/NamespaceName v1 syntax concept which was deprecated by "PIP-10: Remove cluster for namespace and topic names".

All topics in v2 format TopicName.isGlobal() and namespaces in v2 format return true. And after PIP-10 there is no separation of local and global topics.

There's a discussion thread on dev mailing list related to Topic / Namespace v1 formats used in tests:
https://lists.apache.org/thread/60ysbhrgxfltyjwqybgzct1t5csztn2s

@codelipenghui @poorbarcode Are we going to deprecate TopicName / NamespaceName v1 (as was done in PIP-10) or is there a need for v1 format in the future?

@codelipenghui
Copy link
Contributor Author

@lhotari Let me change the test first. Since there are objections in the community to remove V1 format support, I think it's fine. But we can continue to mark V1 as deprecated to avoid new users to continue use V1 format topic.

@Technoboy- Technoboy- merged commit 2ba02e2 into apache:master Jul 31, 2025
51 checks passed
@codelipenghui codelipenghui deleted the fix-namespace-deletion-tls-url-selection branch July 31, 2025 06:11
gaozhangmin pushed a commit to gaozhangmin/pulsar that referenced this pull request Aug 13, 2025
poorbarcode pushed a commit to poorbarcode/pulsar that referenced this pull request Aug 14, 2025
manas-ctds pushed a commit to datastax/pulsar that referenced this pull request Aug 20, 2025
srinath-ctds pushed a commit to datastax/pulsar that referenced this pull request Aug 26, 2025
KannarFr pushed a commit to CleverCloud/pulsar that referenced this pull request Sep 22, 2025
walkinggo pushed a commit to walkinggo/pulsar that referenced this pull request Oct 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cherry-picked/branch-4.0 doc-not-needed Your PR changes do not impact docs ready-to-test release/4.0.7 type/bug The PR fixed a bug or issue reported a bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants