diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 29e5254a1d41b..40d9fc2e26866 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,16 +1,16 @@
-
+
-
+
-
\ No newline at end of file
+
diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml
index de4bd7de54d9a..2ac1ab79d0d7b 100644
--- a/.mvn/extensions.xml
+++ b/.mvn/extensions.xml
@@ -1,4 +1,4 @@
-
+
-
+
com.gradle
gradle-enterprise-maven-extension
diff --git a/.mvn/gradle-enterprise.xml b/.mvn/gradle-enterprise.xml
index effb5bb221325..eec67bc7edad0 100644
--- a/.mvn/gradle-enterprise.xml
+++ b/.mvn/gradle-enterprise.xml
@@ -1,4 +1,4 @@
-
+
-
- #{env['GRADLE_ENTERPRISE_ACCESS_KEY']?.trim() > ''}
+
+ #{env['GRADLE_ENTERPRISE_ACCESS_KEY']?.trim() > ''}
https://ge.apache.org
false
diff --git a/bouncy-castle/bc/pom.xml b/bouncy-castle/bc/pom.xml
index cf83a68be7f75..7b8efd37941c4 100644
--- a/bouncy-castle/bc/pom.xml
+++ b/bouncy-castle/bc/pom.xml
@@ -1,3 +1,4 @@
+
-
+
4.0.0
-
- org.apache.pulsar
+ io.streamnative
bouncy-castle-parent
3.2.0-SNAPSHOT
..
-
bouncy-castle-bc
Apache Pulsar :: Bouncy Castle :: BC
-
${project.groupId}
@@ -39,23 +36,19 @@
${project.version}
provided
-
org.bouncycastle
bcpkix-jdk18on
${bouncycastle.version}
-
org.bouncycastle
bcprov-ext-jdk18on
${bouncycastle.version}
-
-
de.ntcomputer
@@ -73,7 +66,6 @@
-
org.apache.maven.plugins
maven-checkstyle-plugin
diff --git a/bouncy-castle/bcfips-include-test/pom.xml b/bouncy-castle/bcfips-include-test/pom.xml
index 56639ff43144e..5548fb92cedf8 100644
--- a/bouncy-castle/bcfips-include-test/pom.xml
+++ b/bouncy-castle/bcfips-include-test/pom.xml
@@ -1,3 +1,4 @@
+
-
+
4.0.0
- org.apache.pulsar
+ io.streamnative
bouncy-castle-parent
3.2.0-SNAPSHOT
..
-
bcfips-include-test
Pulsar Bouncy Castle FIPS Test
Broker and client runs auth include BC FIPS verison
-
${project.groupId}
@@ -39,7 +37,6 @@
${project.version}
test
-
${project.groupId}
pulsar-broker
@@ -53,7 +50,6 @@
test-jar
test
-
${project.groupId}
pulsar-broker
@@ -66,7 +62,6 @@
test
-
${project.groupId}
@@ -74,7 +69,6 @@
${project.version}
pkg
-
diff --git a/bouncy-castle/bcfips/pom.xml b/bouncy-castle/bcfips/pom.xml
index f38a8e0d2afdb..ec33cdd91fc7b 100644
--- a/bouncy-castle/bcfips/pom.xml
+++ b/bouncy-castle/bcfips/pom.xml
@@ -1,3 +1,4 @@
+
-
+
4.0.0
-
- org.apache.pulsar
+ io.streamnative
bouncy-castle-parent
3.2.0-SNAPSHOT
..
-
bouncy-castle-bcfips
Apache Pulsar :: Bouncy Castle :: BC-FIPS
-
${project.groupId}
@@ -39,20 +36,17 @@
${project.version}
provided
-
org.bouncycastle
bc-fips
${bouncycastle.bc-fips.version}
-
org.bouncycastle
bcpkix-fips
${bouncycastle.bcpkix-fips.version}
-
diff --git a/bouncy-castle/pom.xml b/bouncy-castle/pom.xml
index 3b46eb4a90c9e..c97c0061dc4f2 100644
--- a/bouncy-castle/pom.xml
+++ b/bouncy-castle/pom.xml
@@ -1,3 +1,4 @@
+
-
+
4.0.0
pom
- org.apache.pulsar
+ io.streamnative
pulsar
3.2.0-SNAPSHOT
..
-
@@ -42,16 +41,13 @@
-
bouncy-castle-parent
Apache Pulsar :: Bouncy Castle :: Parent
-
bc
bcfips
-
bcfips-include-test
diff --git a/buildtools/pom.xml b/buildtools/pom.xml
index c92d68ccc8022..b1cf5e0263e3c 100644
--- a/buildtools/pom.xml
+++ b/buildtools/pom.xml
@@ -1,3 +1,4 @@
+
-
+
4.0.0
-
org.apache
apache
29
-
+
-
- org.apache.pulsar
+ io.streamnative
buildtools
3.2.0-SNAPSHOT
jar
Pulsar Build Tools
-
- 2023-09-08T03:29:19Z
+ 2024-03-04T21:08:59Z
1.8
1.8
3.1.0
@@ -60,7 +57,6 @@
--add-opens java.base/jdk.internal.platform=ALL-UNNAMED
-
@@ -72,9 +68,7 @@
-
-
org.yaml
snakeyaml
@@ -95,7 +89,6 @@
guice
${guice.version}
-
org.testng
testng
@@ -142,7 +135,6 @@
${mockito.version}
-
@@ -251,6 +243,55 @@
**/proto/*
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.0
+
+
+ default-jar
+ package
+
+ jar
+
+
+
+ javadoc-jar
+ package
+
+ jar
+
+
+ javadoc
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.2.0
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ maven-gpg-plugin
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
@@ -260,4 +301,14 @@
+
+
+ ossrh
+ https://s01.oss.sonatype.org/content/repositories/snapshots
+
+
+ ossrh
+ https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
diff --git a/buildtools/src/main/resources/log4j2.xml b/buildtools/src/main/resources/log4j2.xml
index 184f58487eaf0..9e12d754fe7b5 100644
--- a/buildtools/src/main/resources/log4j2.xml
+++ b/buildtools/src/main/resources/log4j2.xml
@@ -1,4 +1,4 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/buildtools/src/main/resources/pulsar/checkstyle.xml b/buildtools/src/main/resources/pulsar/checkstyle.xml
index c63c8993408de..7a32584cc4dd6 100644
--- a/buildtools/src/main/resources/pulsar/checkstyle.xml
+++ b/buildtools/src/main/resources/pulsar/checkstyle.xml
@@ -1,4 +1,4 @@
-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
+ IMPORT CHECKS
-
-
-
-
+ -->
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
} else
-->
-
else
-->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
diff --git a/buildtools/src/main/resources/pulsar/suppressions.xml b/buildtools/src/main/resources/pulsar/suppressions.xml
index 57a01c60f6a27..e558a8a0618fd 100644
--- a/buildtools/src/main/resources/pulsar/suppressions.xml
+++ b/buildtools/src/main/resources/pulsar/suppressions.xml
@@ -1,4 +1,4 @@
-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conf/filesystem_offload_core_site.xml b/conf/filesystem_offload_core_site.xml
index d26cec2cc60f0..3c7e6d94bbe50 100644
--- a/conf/filesystem_offload_core_site.xml
+++ b/conf/filesystem_offload_core_site.xml
@@ -1,3 +1,4 @@
+
-
-
- fs.defaultFS
-
-
-
- hadoop.tmp.dir
- pulsar
-
-
- io.file.buffer.size
- 4096
-
-
- io.seqfile.compress.blocksize
- 1000000
-
-
- io.seqfile.compression.type
- BLOCK
-
-
- io.map.index.interval
- 128
-
-
+
+
+ fs.defaultFS
+
+
+
+ hadoop.tmp.dir
+ pulsar
+
+
+ io.file.buffer.size
+ 4096
+
+
+ io.seqfile.compress.blocksize
+ 1000000
+
+
+ io.seqfile.compression.type
+ BLOCK
+
+
+ io.map.index.interval
+ 128
+
diff --git a/conf/functions_log4j2.xml b/conf/functions_log4j2.xml
index fd4042e82e82f..a1dcc6a1f654a 100644
--- a/conf/functions_log4j2.xml
+++ b/conf/functions_log4j2.xml
@@ -1,3 +1,4 @@
+
- pulsar-functions-instance
- 30
-
-
- pulsar.log.appender
- RollingFile
-
-
- pulsar.log.level
- info
-
-
- bk.log.level
- info
-
-
-
-
- Console
- SYSTEM_OUT
-
- %d{ISO8601_OFFSET_DATE_TIME_HHMM} [%t] %-5level %logger{36} - %msg%n
-
-
-
- RollingFile
- ${sys:pulsar.function.log.dir}/${sys:pulsar.function.log.file}.log
- ${sys:pulsar.function.log.dir}/${sys:pulsar.function.log.file}-%d{MM-dd-yyyy}-%i.log.gz
- true
-
- %d{ISO8601_OFFSET_DATE_TIME_HHMM} [%t] %-5level %logger{36} - %msg%n
-
-
-
- 1
- true
-
-
- 1 GB
-
-
- 0 0 0 * * ?
-
-
-
-
- ${sys:pulsar.function.log.dir}
- 2
-
- ${sys:pulsar.function.log.file}*log.gz
-
-
- 30d
-
-
-
-
-
- BkRollingFile
- ${sys:pulsar.function.log.dir}/${sys:pulsar.function.log.file}.bk
- ${sys:pulsar.function.log.dir}/${sys:pulsar.function.log.file}.bk-%d{MM-dd-yyyy}-%i.log.gz
- true
-
- %d{ISO8601_OFFSET_DATE_TIME_HHMM} [%t] %-5level %logger{36} - %msg%n
-
-
-
- 1
- true
-
-
- 1 GB
-
-
- 0 0 0 * * ?
-
-
-
-
- ${sys:pulsar.function.log.dir}
- 2
-
- ${sys:pulsar.function.log.file}.bk*log.gz
-
-
- 30d
-
-
-
-
-
-
-
- org.apache.pulsar.functions.runtime.shaded.org.apache.bookkeeper
- ${sys:bk.log.level}
- false
-
- [BkRollingFile]
-
-
-
- ${sys:pulsar.log.level}
-
- [${sys:pulsar.log.appender}]
- ${sys:pulsar.log.level}
-
-
-
+ pulsar-functions-instance
+ 30
+
+
+ pulsar.log.appender
+ RollingFile
+
+
+ pulsar.log.level
+ info
+
+
+ bk.log.level
+ info
+
+
+
+
+ Console
+ SYSTEM_OUT
+
+ %d{ISO8601_OFFSET_DATE_TIME_HHMM} [%t] %-5level %logger{36} - %msg%n
+
+
+
+ RollingFile
+ ${sys:pulsar.function.log.dir}/${sys:pulsar.function.log.file}.log
+ ${sys:pulsar.function.log.dir}/${sys:pulsar.function.log.file}-%d{MM-dd-yyyy}-%i.log.gz
+ true
+
+ %d{ISO8601_OFFSET_DATE_TIME_HHMM} [%t] %-5level %logger{36} - %msg%n
+
+
+
+ 1
+ true
+
+
+ 1 GB
+
+
+ 0 0 0 * * ?
+
+
+
+
+ ${sys:pulsar.function.log.dir}
+ 2
+
+ ${sys:pulsar.function.log.file}*log.gz
+
+
+ 30d
+
+
+
+
+
+ BkRollingFile
+ ${sys:pulsar.function.log.dir}/${sys:pulsar.function.log.file}.bk
+ ${sys:pulsar.function.log.dir}/${sys:pulsar.function.log.file}.bk-%d{MM-dd-yyyy}-%i.log.gz
+ true
+
+ %d{ISO8601_OFFSET_DATE_TIME_HHMM} [%t] %-5level %logger{36} - %msg%n
+
+
+
+ 1
+ true
+
+
+ 1 GB
+
+
+ 0 0 0 * * ?
+
+
+
+
+ ${sys:pulsar.function.log.dir}
+ 2
+
+ ${sys:pulsar.function.log.file}.bk*log.gz
+
+
+ 30d
+
+
+
+
+
+
+
+ org.apache.pulsar.functions.runtime.shaded.org.apache.bookkeeper
+ ${sys:bk.log.level}
+ false
+
+ [BkRollingFile]
+
+
+
+ ${sys:pulsar.log.level}
+
+ [${sys:pulsar.log.appender}]
+ ${sys:pulsar.log.level}
+
+
+
diff --git a/conf/functions_worker.yml b/conf/functions_worker.yml
index 4c5b6aab1b7f4..3871c74a88778 100644
--- a/conf/functions_worker.yml
+++ b/conf/functions_worker.yml
@@ -43,6 +43,16 @@ metadataStoreOperationTimeoutSeconds: 30
# Metadata store cache expiry time in seconds
metadataStoreCacheExpirySeconds: 300
+# Specifies if the function worker should use classloading for validating submissions for built-in
+# connectors and functions. This is required for validateConnectorConfig to take effect.
+# Default is false.
+enableClassloadingOfBuiltinFiles: false
+
+# Specifies if the function worker should use classloading for validating submissions for external
+# connectors and functions. This is required for validateConnectorConfig to take effect.
+# Default is false.
+enableClassloadingOfExternalFiles: false
+
################################
# Function package management
################################
@@ -398,9 +408,20 @@ saslJaasServerRoleTokenSignerSecretPath:
########################
connectorsDirectory: ./connectors
+# Whether to enable referencing connectors directory files by file url in connector (sink/source) creation
+enableReferencingConnectorDirectoryFiles: true
+# Regex patterns for enabling creation of connectors by referencing packages in matching http/https urls
+additionalEnabledConnectorUrlPatterns: []
functionsDirectory: ./functions
-
-# Should connector config be validated during submission
+# Whether to enable referencing functions directory files by file url in functions creation
+enableReferencingFunctionsDirectoryFiles: true
+# Regex patterns for enabling creation of functions by referencing packages in matching http/https urls
+additionalEnabledFunctionsUrlPatterns: []
+
+# Enables extended validation for connector config with fine-grain annotation based validation
+# during submission. Classloading with either enableClassloadingOfExternalFiles or
+# enableClassloadingOfBuiltinFiles must be enabled on the worker for this to take effect.
+# Default is false.
validateConnectorConfig: false
# Whether to initialize distributed log metadata by runtime.
diff --git a/conf/proxy.conf b/conf/proxy.conf
index 4194bf7621985..8285e1cb75320 100644
--- a/conf/proxy.conf
+++ b/conf/proxy.conf
@@ -28,17 +28,19 @@ metadataStoreUrl=
# The metadata store URL for the configuration data. If empty, we fall back to use metadataStoreUrl
configurationMetadataStoreUrl=
-# If Service Discovery is Disabled this url should point to the discovery service provider.
+# If does not set metadataStoreUrl or configurationMetadataStoreUrl, this url should point to the discovery service
+# provider, and does not support multi urls yet.
# The URL must begin with pulsar:// for plaintext or with pulsar+ssl:// for TLS.
brokerServiceURL=
brokerServiceURLTLS=
-# These settings are unnecessary if `zookeeperServers` is specified
+# If does not set metadataStoreUrl or configurationMetadataStoreUrl, this url should point to the discovery service
+# provider, and does not support multi urls yet.
brokerWebServiceURL=
brokerWebServiceURLTLS=
-# If function workers are setup in a separate cluster, configure the following 2 settings
-# to point to the function workers cluster
+# If function workers are setup in a separate cluster, configure the following 2 settings. This url should point to
+# the discovery service provider of the function workers cluster, and does not support multi urls yet.
functionWorkerWebServiceURL=
functionWorkerWebServiceURLTLS=
diff --git a/distribution/io/pom.xml b/distribution/io/pom.xml
index df97efcca18a4..bf238fb43920e 100644
--- a/distribution/io/pom.xml
+++ b/distribution/io/pom.xml
@@ -1,3 +1,4 @@
+
-
+
4.0.0
-
- org.apache.pulsar
+ io.streamnative
distribution
3.2.0-SNAPSHOT
..
-
pulsar-io-distribution
pom
Pulsar :: Distribution :: IO
-
${project.groupId}
@@ -46,7 +43,6 @@
provided
-
@@ -69,9 +65,32 @@
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.0
+
+
+ default-jar
+ package
+
+ jar
+
+
+
+ javadoc-jar
+ package
+
+ jar
+
+
+ javadoc
+
+
+
+
-
generator-connector-config
@@ -151,5 +170,4 @@
-
diff --git a/distribution/io/src/assemble/io.xml b/distribution/io/src/assemble/io.xml
index 5b652170fdbb5..71d5aaf69f76a 100644
--- a/distribution/io/src/assemble/io.xml
+++ b/distribution/io/src/assemble/io.xml
@@ -1,3 +1,4 @@
+
-
+
bin
dir
@@ -38,48 +37,120 @@
.
644
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/distribution/offloaders/pom.xml b/distribution/offloaders/pom.xml
index 0fd0d1dc2bc79..fba25b4b846b4 100644
--- a/distribution/offloaders/pom.xml
+++ b/distribution/offloaders/pom.xml
@@ -1,3 +1,4 @@
+
-
+
4.0.0
-
- org.apache.pulsar
+ io.streamnative
distribution
3.2.0-SNAPSHOT
..
-
pulsar-offloader-distribution
pom
Pulsar :: Distribution :: Offloader
-
${project.groupId}
@@ -66,7 +63,6 @@
-
@@ -90,6 +86,30 @@
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.0
+
+
+ default-jar
+ package
+
+ jar
+
+
+
+ javadoc-jar
+ package
+
+ jar
+
+
+ javadoc
+
+
+
+
diff --git a/distribution/offloaders/src/assemble/offloaders.xml b/distribution/offloaders/src/assemble/offloaders.xml
index 38f7eee906064..19c60f68c21d4 100644
--- a/distribution/offloaders/src/assemble/offloaders.xml
+++ b/distribution/offloaders/src/assemble/offloaders.xml
@@ -1,3 +1,4 @@
+
-
+
bin
tar.gz
@@ -38,13 +37,11 @@
.
644
-
offloaders
644
-
offloaders
diff --git a/distribution/pom.xml b/distribution/pom.xml
index 3f52a30bb5237..2023cd71de6a8 100644
--- a/distribution/pom.xml
+++ b/distribution/pom.xml
@@ -1,3 +1,4 @@
+
-
+
4.0.0
-
- org.apache.pulsar
+ io.streamnative
pulsar
3.2.0-SNAPSHOT
..
-
distribution
pom
Pulsar :: Distribution
-
-
main
@@ -47,7 +43,6 @@
shell
-
core-modules
@@ -55,7 +50,6 @@
-
@@ -65,6 +59,30 @@
${skipBuildDistribution}
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.0
+
+
+ default-jar
+ package
+
+ jar
+
+
+
+ javadoc-jar
+ package
+
+ jar
+
+
+ javadoc
+
+
+
+
diff --git a/distribution/server/pom.xml b/distribution/server/pom.xml
index 647de84bf07f3..34e111c7f9c48 100644
--- a/distribution/server/pom.xml
+++ b/distribution/server/pom.xml
@@ -1,3 +1,4 @@
+
-
+
4.0.0
-
- org.apache.pulsar
+ io.streamnative
distribution
3.2.0-SNAPSHOT
..
-
pulsar-server-distribution
pom
Pulsar :: Distribution :: Server
-
${project.groupId}
pulsar-broker
${project.version}
-
${project.groupId}
pulsar-docs-tools
${project.version}
-
${project.groupId}
pulsar-proxy
${project.version}
-
${project.groupId}
pulsar-broker-auth-oidc
${project.version}
-
${project.groupId}
pulsar-broker-auth-sasl
${project.version}
-
${project.groupId}
pulsar-client-auth-sasl
${project.version}
-
jline
jline
${jline.version}
-
org.apache.zookeeper
zookeeper-prometheus-metrics
@@ -95,7 +85,6 @@
-
${project.groupId}
pulsar-package-bookkeeper-storage
@@ -107,13 +96,11 @@
-
${project.groupId}
pulsar-package-filesystem-storage
${project.version}
-
${project.groupId}
pulsar-client-tools
@@ -125,33 +112,27 @@
-
${project.groupId}
pulsar-testclient
${project.version}
-
org.apache.logging.log4j
log4j-api
-
org.apache.logging.log4j
log4j-core
-
org.apache.logging.log4j
log4j-web
-
io.dropwizard.metrics
metrics-core
-
io.dropwizard.metrics
metrics-graphite
@@ -162,44 +143,36 @@
-
io.dropwizard.metrics
metrics-jvm
-
org.xerial.snappy
snappy-java
-
com.fasterxml.jackson.dataformat
jackson-dataformat-yaml
-
org.apache.logging.log4j
log4j-slf4j-impl
-
org.apache.bookkeeper.stats
prometheus-metrics-provider
-
io.prometheus
simpleclient_log4j2
-
${project.groupId}
bouncy-castle-bc
${project.version}
pkg
-
${project.groupId}
@@ -214,7 +187,6 @@
-
${project.groupId}
pulsar-functions-worker
@@ -231,7 +203,6 @@
-
${project.groupId}
@@ -246,7 +217,6 @@
-
${project.groupId}
@@ -259,7 +229,6 @@
-
io.grpc
@@ -269,13 +238,11 @@
org.bouncycastle
bcpkix-jdk18on
-
io.perfmark
perfmark-api
compile
-
org.apache.bookkeeper.http
@@ -297,7 +264,6 @@
vertx-web
-
@@ -338,6 +304,30 @@
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.0
+
+
+ default-jar
+ package
+
+ jar
+
+
+
+ javadoc-jar
+ package
+
+ jar
+
+
+ javadoc
+
+
+
+
diff --git a/distribution/server/src/assemble/LICENSE.bin.txt b/distribution/server/src/assemble/LICENSE.bin.txt
index db66fc4ced547..b3cecd215f3ce 100644
--- a/distribution/server/src/assemble/LICENSE.bin.txt
+++ b/distribution/server/src/assemble/LICENSE.bin.txt
@@ -285,7 +285,7 @@ The Apache Software License, Version 2.0
- commons-lang-commons-lang-2.6.jar
- commons-logging-commons-logging-1.1.1.jar
- org.apache.commons-commons-collections4-4.4.jar
- - org.apache.commons-commons-compress-1.21.jar
+ - org.apache.commons-commons-compress-1.26.0.jar
- org.apache.commons-commons-lang3-3.11.jar
- org.apache.commons-commons-text-1.10.0.jar
* Netty
@@ -345,34 +345,34 @@ The Apache Software License, Version 2.0
- net.java.dev.jna-jna-jpms-5.12.1.jar
- net.java.dev.jna-jna-platform-jpms-5.12.1.jar
* BookKeeper
- - org.apache.bookkeeper-bookkeeper-common-4.16.3.jar
- - org.apache.bookkeeper-bookkeeper-common-allocator-4.16.3.jar
- - org.apache.bookkeeper-bookkeeper-proto-4.16.3.jar
- - org.apache.bookkeeper-bookkeeper-server-4.16.3.jar
- - org.apache.bookkeeper-bookkeeper-tools-framework-4.16.3.jar
- - org.apache.bookkeeper-circe-checksum-4.16.3.jar
- - org.apache.bookkeeper-cpu-affinity-4.16.3.jar
- - org.apache.bookkeeper-statelib-4.16.3.jar
- - org.apache.bookkeeper-stream-storage-api-4.16.3.jar
- - org.apache.bookkeeper-stream-storage-common-4.16.3.jar
- - org.apache.bookkeeper-stream-storage-java-client-4.16.3.jar
- - org.apache.bookkeeper-stream-storage-java-client-base-4.16.3.jar
- - org.apache.bookkeeper-stream-storage-proto-4.16.3.jar
- - org.apache.bookkeeper-stream-storage-server-4.16.3.jar
- - org.apache.bookkeeper-stream-storage-service-api-4.16.3.jar
- - org.apache.bookkeeper-stream-storage-service-impl-4.16.3.jar
- - org.apache.bookkeeper.http-http-server-4.16.3.jar
- - org.apache.bookkeeper.http-vertx-http-server-4.16.3.jar
- - org.apache.bookkeeper.stats-bookkeeper-stats-api-4.16.3.jar
- - org.apache.bookkeeper.stats-prometheus-metrics-provider-4.16.3.jar
- - org.apache.distributedlog-distributedlog-common-4.16.3.jar
- - org.apache.distributedlog-distributedlog-core-4.16.3-tests.jar
- - org.apache.distributedlog-distributedlog-core-4.16.3.jar
- - org.apache.distributedlog-distributedlog-protocol-4.16.3.jar
- - org.apache.bookkeeper.stats-codahale-metrics-provider-4.16.3.jar
- - org.apache.bookkeeper-bookkeeper-slogger-api-4.16.3.jar
- - org.apache.bookkeeper-bookkeeper-slogger-slf4j-4.16.3.jar
- - org.apache.bookkeeper-native-io-4.16.3.jar
+ - org.apache.bookkeeper-bookkeeper-common-4.16.4.jar
+ - org.apache.bookkeeper-bookkeeper-common-allocator-4.16.4.jar
+ - org.apache.bookkeeper-bookkeeper-proto-4.16.4.jar
+ - org.apache.bookkeeper-bookkeeper-server-4.16.4.jar
+ - org.apache.bookkeeper-bookkeeper-tools-framework-4.16.4.jar
+ - org.apache.bookkeeper-circe-checksum-4.16.4.jar
+ - org.apache.bookkeeper-cpu-affinity-4.16.4.jar
+ - org.apache.bookkeeper-statelib-4.16.4.jar
+ - org.apache.bookkeeper-stream-storage-api-4.16.4.jar
+ - org.apache.bookkeeper-stream-storage-common-4.16.4.jar
+ - org.apache.bookkeeper-stream-storage-java-client-4.16.4.jar
+ - org.apache.bookkeeper-stream-storage-java-client-base-4.16.4.jar
+ - org.apache.bookkeeper-stream-storage-proto-4.16.4.jar
+ - org.apache.bookkeeper-stream-storage-server-4.16.4.jar
+ - org.apache.bookkeeper-stream-storage-service-api-4.16.4.jar
+ - org.apache.bookkeeper-stream-storage-service-impl-4.16.4.jar
+ - org.apache.bookkeeper.http-http-server-4.16.4.jar
+ - org.apache.bookkeeper.http-vertx-http-server-4.16.4.jar
+ - org.apache.bookkeeper.stats-bookkeeper-stats-api-4.16.4.jar
+ - org.apache.bookkeeper.stats-prometheus-metrics-provider-4.16.4.jar
+ - org.apache.distributedlog-distributedlog-common-4.16.4.jar
+ - org.apache.distributedlog-distributedlog-core-4.16.4-tests.jar
+ - org.apache.distributedlog-distributedlog-core-4.16.4.jar
+ - org.apache.distributedlog-distributedlog-protocol-4.16.4.jar
+ - org.apache.bookkeeper.stats-codahale-metrics-provider-4.16.4.jar
+ - org.apache.bookkeeper-bookkeeper-slogger-api-4.16.4.jar
+ - org.apache.bookkeeper-bookkeeper-slogger-slf4j-4.16.4.jar
+ - org.apache.bookkeeper-native-io-4.16.4.jar
* Apache HTTP Client
- org.apache.httpcomponents-httpclient-4.5.13.jar
- org.apache.httpcomponents-httpcore-4.4.15.jar
@@ -382,25 +382,25 @@ The Apache Software License, Version 2.0
- org.asynchttpclient-async-http-client-2.12.1.jar
- org.asynchttpclient-async-http-client-netty-utils-2.12.1.jar
* Jetty
- - org.eclipse.jetty-jetty-client-9.4.53.v20231009.jar
- - org.eclipse.jetty-jetty-continuation-9.4.53.v20231009.jar
- - org.eclipse.jetty-jetty-http-9.4.53.v20231009.jar
- - org.eclipse.jetty-jetty-io-9.4.53.v20231009.jar
- - org.eclipse.jetty-jetty-proxy-9.4.53.v20231009.jar
- - org.eclipse.jetty-jetty-security-9.4.53.v20231009.jar
- - org.eclipse.jetty-jetty-server-9.4.53.v20231009.jar
- - org.eclipse.jetty-jetty-servlet-9.4.53.v20231009.jar
- - org.eclipse.jetty-jetty-servlets-9.4.53.v20231009.jar
- - org.eclipse.jetty-jetty-util-9.4.53.v20231009.jar
- - org.eclipse.jetty-jetty-util-ajax-9.4.53.v20231009.jar
- - org.eclipse.jetty.websocket-javax-websocket-client-impl-9.4.53.v20231009.jar
- - org.eclipse.jetty.websocket-websocket-api-9.4.53.v20231009.jar
- - org.eclipse.jetty.websocket-websocket-client-9.4.53.v20231009.jar
- - org.eclipse.jetty.websocket-websocket-common-9.4.53.v20231009.jar
- - org.eclipse.jetty.websocket-websocket-server-9.4.53.v20231009.jar
- - org.eclipse.jetty.websocket-websocket-servlet-9.4.53.v20231009.jar
- - org.eclipse.jetty-jetty-alpn-conscrypt-server-9.4.53.v20231009.jar
- - org.eclipse.jetty-jetty-alpn-server-9.4.53.v20231009.jar
+ - org.eclipse.jetty-jetty-client-9.4.54.v20240208.jar
+ - org.eclipse.jetty-jetty-continuation-9.4.54.v20240208.jar
+ - org.eclipse.jetty-jetty-http-9.4.54.v20240208.jar
+ - org.eclipse.jetty-jetty-io-9.4.54.v20240208.jar
+ - org.eclipse.jetty-jetty-proxy-9.4.54.v20240208.jar
+ - org.eclipse.jetty-jetty-security-9.4.54.v20240208.jar
+ - org.eclipse.jetty-jetty-server-9.4.54.v20240208.jar
+ - org.eclipse.jetty-jetty-servlet-9.4.54.v20240208.jar
+ - org.eclipse.jetty-jetty-servlets-9.4.54.v20240208.jar
+ - org.eclipse.jetty-jetty-util-9.4.54.v20240208.jar
+ - org.eclipse.jetty-jetty-util-ajax-9.4.54.v20240208.jar
+ - org.eclipse.jetty.websocket-javax-websocket-client-impl-9.4.54.v20240208.jar
+ - org.eclipse.jetty.websocket-websocket-api-9.4.54.v20240208.jar
+ - org.eclipse.jetty.websocket-websocket-client-9.4.54.v20240208.jar
+ - org.eclipse.jetty.websocket-websocket-common-9.4.54.v20240208.jar
+ - org.eclipse.jetty.websocket-websocket-server-9.4.54.v20240208.jar
+ - org.eclipse.jetty.websocket-websocket-servlet-9.4.54.v20240208.jar
+ - org.eclipse.jetty-jetty-alpn-conscrypt-server-9.4.54.v20240208.jar
+ - org.eclipse.jetty-jetty-alpn-server-9.4.54.v20240208.jar
* SnakeYaml -- org.yaml-snakeyaml-2.0.jar
* RocksDB - org.rocksdb-rocksdbjni-7.9.2.jar
* Google Error Prone Annotations - com.google.errorprone-error_prone_annotations-2.5.1.jar
@@ -445,6 +445,10 @@ The Apache Software License, Version 2.0
* Jodah
- net.jodah-typetools-0.5.0.jar
- net.jodah-failsafe-2.4.4.jar
+ * Byte Buddy
+ - net.bytebuddy-byte-buddy-1.14.12.jar
+ * zt-zip
+ - org.zeroturnaround-zt-zip-1.17.jar
* Apache Avro
- org.apache.avro-avro-1.11.3.jar
- org.apache.avro-avro-protobuf-1.11.3.jar
diff --git a/distribution/server/src/assemble/bin.xml b/distribution/server/src/assemble/bin.xml
index 949c265706929..31b17eacda1b3 100644
--- a/distribution/server/src/assemble/bin.xml
+++ b/distribution/server/src/assemble/bin.xml
@@ -1,3 +1,4 @@
+
-
+
bin
tar.gz
@@ -115,14 +114,11 @@
${artifact.groupId}-${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}
-
- org.apache.pulsar:pulsar-functions-runtime-all
-
+ io.streamnative:pulsar-functions-runtime-all
org.projectlombok:lombok
-
- org.apache.pulsar:pulsar-functions-api-examples
+ io.streamnative:pulsar-functions-api-examples
*:tar.gz
diff --git a/distribution/shell/pom.xml b/distribution/shell/pom.xml
index a1146811ff23f..4f5cf8f97c3f5 100644
--- a/distribution/shell/pom.xml
+++ b/distribution/shell/pom.xml
@@ -1,3 +1,4 @@
+
-
+
4.0.0
-
- org.apache.pulsar
+ io.streamnative
distribution
3.2.0-SNAPSHOT
..
-
pulsar-shell-distribution
pom
Pulsar :: Distribution :: Shell
-
${project.groupId}
pulsar-client-tools
${project.version}
-
org.apache.logging.log4j
log4j-core
-
org.apache.logging.log4j
log4j-web
@@ -53,14 +48,11 @@
org.apache.logging.log4j
log4j-slf4j-impl
-
io.prometheus
simpleclient_log4j2
-
-
@@ -101,6 +93,30 @@
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.0
+
+
+ default-jar
+ package
+
+ jar
+
+
+
+ javadoc-jar
+ package
+
+ jar
+
+
+ javadoc
+
+
+
+
diff --git a/distribution/shell/src/assemble/LICENSE.bin.txt b/distribution/shell/src/assemble/LICENSE.bin.txt
index bdd1b18ce0c3d..8047501b15661 100644
--- a/distribution/shell/src/assemble/LICENSE.bin.txt
+++ b/distribution/shell/src/assemble/LICENSE.bin.txt
@@ -342,7 +342,7 @@ The Apache Software License, Version 2.0
- commons-logging-1.2.jar
- commons-lang3-3.11.jar
- commons-text-1.10.0.jar
- - commons-compress-1.21.jar
+ - commons-compress-1.26.0.jar
* Netty
- netty-buffer-4.1.104.Final.jar
- netty-codec-4.1.104.Final.jar
@@ -386,23 +386,23 @@ The Apache Software License, Version 2.0
- log4j-web-2.18.0.jar
* BookKeeper
- - bookkeeper-common-allocator-4.16.3.jar
- - cpu-affinity-4.16.3.jar
- - circe-checksum-4.16.3.jar
+ - bookkeeper-common-allocator-4.16.4.jar
+ - cpu-affinity-4.16.4.jar
+ - circe-checksum-4.16.4.jar
* AirCompressor
- aircompressor-0.20.jar
* AsyncHttpClient
- async-http-client-2.12.1.jar
- async-http-client-netty-utils-2.12.1.jar
* Jetty
- - jetty-client-9.4.53.v20231009.jar
- - jetty-http-9.4.53.v20231009.jar
- - jetty-io-9.4.53.v20231009.jar
- - jetty-util-9.4.53.v20231009.jar
- - javax-websocket-client-impl-9.4.53.v20231009.jar
- - websocket-api-9.4.53.v20231009.jar
- - websocket-client-9.4.53.v20231009.jar
- - websocket-common-9.4.53.v20231009.jar
+ - jetty-client-9.4.54.v20240208.jar
+ - jetty-http-9.4.54.v20240208.jar
+ - jetty-io-9.4.54.v20240208.jar
+ - jetty-util-9.4.54.v20240208.jar
+ - javax-websocket-client-impl-9.4.54.v20240208.jar
+ - websocket-api-9.4.54.v20240208.jar
+ - websocket-client-9.4.54.v20240208.jar
+ - websocket-common-9.4.54.v20240208.jar
* SnakeYaml -- snakeyaml-2.0.jar
* Google Error Prone Annotations - error_prone_annotations-2.5.1.jar
* Javassist -- javassist-3.25.0-GA.jar
diff --git a/distribution/shell/src/assemble/shell.xml b/distribution/shell/src/assemble/shell.xml
index f823e0258b231..cd11bd8a1ef57 100644
--- a/distribution/shell/src/assemble/shell.xml
+++ b/distribution/shell/src/assemble/shell.xml
@@ -1,3 +1,4 @@
+
-
+
bin
tar.gz
@@ -46,7 +45,6 @@
.
644
-
bin
@@ -76,7 +74,6 @@
-
lib
diff --git a/docker/pom.xml b/docker/pom.xml
index 0df3c066babd4..779b58e642000 100644
--- a/docker/pom.xml
+++ b/docker/pom.xml
@@ -1,4 +1,4 @@
-
+
-
+
pom
4.0.0
- org.apache.pulsar
+ io.streamnative
pulsar
3.2.0-SNAPSHOT
@@ -39,6 +38,30 @@
${skipBuildDistribution}
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.0
+
+
+ default-jar
+ package
+
+ jar
+
+
+
+ javadoc-jar
+ package
+
+ jar
+
+
+ javadoc
+
+
+
+
diff --git a/docker/pulsar-all/pom.xml b/docker/pulsar-all/pom.xml
index 269a2e4f98f62..6b4765327e319 100644
--- a/docker/pulsar-all/pom.xml
+++ b/docker/pulsar-all/pom.xml
@@ -1,3 +1,4 @@
+
-
+
- org.apache.pulsar
+ io.streamnative
docker-images
3.2.0-SNAPSHOT
@@ -29,7 +29,6 @@
pulsar-all-docker-image
Apache Pulsar :: Docker Images :: Pulsar Latest Version (Include All Components)
pom
-
${project.groupId}
@@ -64,7 +63,6 @@
-
git-commit-id-no-git
@@ -177,7 +175,6 @@
-
docker-push
@@ -200,6 +197,5 @@
-
diff --git a/docker/pulsar/Dockerfile b/docker/pulsar/Dockerfile
index 4e5885ce55d17..6a0dc0100e7fd 100644
--- a/docker/pulsar/Dockerfile
+++ b/docker/pulsar/Dockerfile
@@ -36,10 +36,14 @@ COPY scripts/install-pulsar-client.sh /pulsar/bin
# The final image needs to give the root group sufficient permission for Pulsar components
# to write to specific directories within /pulsar
+# The ownership is changed to uid 10000 to allow using a different root group. This is necessary when running the
+# container when gid=0 is prohibited. In that case, the container must be run with uid 10000 with
+# any group id != 0 (for example 10001).
# The file permissions are preserved when copying files from this builder image to the target image.
RUN for SUBDIRECTORY in conf data download logs; do \
[ -d /pulsar/$SUBDIRECTORY ] || mkdir /pulsar/$SUBDIRECTORY; \
- chmod -R g+w /pulsar/$SUBDIRECTORY; \
+ chmod -R ug+w /pulsar/$SUBDIRECTORY; \
+ chown -R 10000:0 /pulsar/$SUBDIRECTORY; \
done
### Create 2nd stage from Ubuntu image
diff --git a/docker/pulsar/pom.xml b/docker/pulsar/pom.xml
index 1c29fa3f00c9c..743e649c880aa 100644
--- a/docker/pulsar/pom.xml
+++ b/docker/pulsar/pom.xml
@@ -1,3 +1,4 @@
+
-
+
- org.apache.pulsar
+ io.streamnative
docker-images
3.2.0-SNAPSHOT
@@ -29,7 +29,6 @@
pulsar-docker-image
Apache Pulsar :: Docker Images :: Pulsar Latest Version
pom
-
${project.groupId}
@@ -46,13 +45,11 @@
-
http://archive.ubuntu.com/ubuntu/
http://security.ubuntu.com/ubuntu/
17
-
git-commit-id-no-git
@@ -130,7 +127,6 @@
-
docker-push
@@ -153,6 +149,5 @@
-
diff --git a/docker/pulsar/scripts/apply-config-from-env-with-prefix.py b/docker/pulsar/scripts/apply-config-from-env-with-prefix.py
index 58f6c98975005..9943b283a9f89 100755
--- a/docker/pulsar/scripts/apply-config-from-env-with-prefix.py
+++ b/docker/pulsar/scripts/apply-config-from-env-with-prefix.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env bash
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
@@ -32,83 +32,8 @@
# update if they exist and ignored if they don't.
############################################################
-import os
-import sys
-
-if len(sys.argv) < 3:
- print('Usage: %s [...]' % (sys.argv[0]))
- sys.exit(1)
-
-# Always apply env config to env scripts as well
-prefix = sys.argv[1]
-conf_files = sys.argv[2:]
-
-PF_ENV_DEBUG = (os.environ.get('PF_ENV_DEBUG','0') == '1')
-
-for conf_filename in conf_files:
- lines = [] # List of config file lines
- keys = {} # Map a key to its line number in the file
-
- # Load conf file
- for line in open(conf_filename):
- lines.append(line)
- line = line.strip()
- if not line or line.startswith('#'):
- continue
-
- try:
- k,v = line.split('=', 1)
- keys[k] = len(lines) - 1
- except:
- if PF_ENV_DEBUG:
- print("[%s] skip Processing %s" % (conf_filename, line))
-
- # Update values from Env
- for k in sorted(os.environ.keys()):
- v = os.environ[k].strip()
-
- # Hide the value in logs if is password.
- if "password" in k.lower():
- displayValue = "********"
- else:
- displayValue = v
-
- if k.startswith(prefix):
- k = k[len(prefix):]
- if k in keys:
- print('[%s] Applying config %s = %s' % (conf_filename, k, displayValue))
- idx = keys[k]
- lines[idx] = '%s=%s\n' % (k, v)
-
-
- # Ensure we have a new-line at the end of the file, to avoid issue
- # when appending more lines to the config
- lines.append('\n')
-
- # Add new keys from Env
- for k in sorted(os.environ.keys()):
- v = os.environ[k]
- if not k.startswith(prefix):
- continue
-
- # Hide the value in logs if is password.
- if "password" in k.lower():
- displayValue = "********"
- else:
- displayValue = v
-
- k = k[len(prefix):]
- if k not in keys:
- print('[%s] Adding config %s = %s' % (conf_filename, k, displayValue))
- lines.append('%s=%s\n' % (k, v))
- else:
- print('[%s] Updating config %s = %s' % (conf_filename, k, displayValue))
- lines[keys[k]] = '%s=%s\n' % (k, v)
-
-
- # Store back the updated config in the same file
- f = open(conf_filename, 'w')
- for line in lines:
- f.write(line)
- f.close()
+# DEPRECATED: Use "apply-config-from-env.py --prefix MY_PREFIX_ conf_file" instead
+# this is not a python script, but a bash script. Call apply-config-from-env.py with the prefix argument
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
+"${SCRIPT_DIR}/apply-config-from-env.py" --prefix "$1" "${@:2}"
diff --git a/docker/pulsar/scripts/apply-config-from-env.py b/docker/pulsar/scripts/apply-config-from-env.py
index b8b479fc15b85..da51f05f8be66 100755
--- a/docker/pulsar/scripts/apply-config-from-env.py
+++ b/docker/pulsar/scripts/apply-config-from-env.py
@@ -25,18 +25,29 @@
## ./apply-config-from-env file.conf
##
-import os, sys
-
-if len(sys.argv) < 2:
- print('Usage: %s' % (sys.argv[0]))
+import os, sys, argparse
+
+parser = argparse.ArgumentParser(description='Pulsar configuration file customizer based on environment variables')
+parser.add_argument('--prefix', default='PULSAR_PREFIX_', help='Prefix for environment variables, default is PULSAR_PREFIX_')
+parser.add_argument('conf_files', nargs='*', help='Configuration files')
+args = parser.parse_args()
+if not args.conf_files:
+ parser.print_help()
sys.exit(1)
-# Always apply env config to env scripts as well
-conf_files = sys.argv[1:]
+env_prefix = args.prefix
+conf_files = args.conf_files
-PF_ENV_PREFIX = 'PULSAR_PREFIX_'
PF_ENV_DEBUG = (os.environ.get('PF_ENV_DEBUG','0') == '1')
+# List of keys where the value should not be displayed in logs
+sensitive_keys = ["brokerClientAuthenticationParameters", "bookkeeperClientAuthenticationParameters", "tokenSecretKey"]
+
+def sanitize_display_value(k, v):
+ if "password" in k.lower() or k in sensitive_keys or (k == "tokenSecretKey" and v.startswith("data:")):
+ return "********"
+ return v
+
for conf_filename in conf_files:
lines = [] # List of config file lines
keys = {} # Map a key to its line number in the file
@@ -47,7 +58,6 @@
line = line.strip()
if not line:
continue
-
try:
k,v = line.split('=', 1)
if k.startswith('#'):
@@ -61,37 +71,26 @@
for k in sorted(os.environ.keys()):
v = os.environ[k].strip()
- # Hide the value in logs if is password.
- if "password" in k.lower():
- displayValue = "********"
- else:
- displayValue = v
-
- if k.startswith(PF_ENV_PREFIX):
- k = k[len(PF_ENV_PREFIX):]
if k in keys:
+ displayValue = sanitize_display_value(k, v)
print('[%s] Applying config %s = %s' % (conf_filename, k, displayValue))
idx = keys[k]
lines[idx] = '%s=%s\n' % (k, v)
-
# Ensure we have a new-line at the end of the file, to avoid issue
# when appending more lines to the config
lines.append('\n')
-
- # Add new keys from Env
+
+ # Add new keys from Env
for k in sorted(os.environ.keys()):
- v = os.environ[k]
- if not k.startswith(PF_ENV_PREFIX):
+ if not k.startswith(env_prefix):
continue
- # Hide the value in logs if is password.
- if "password" in k.lower():
- displayValue = "********"
- else:
- displayValue = v
+ v = os.environ[k].strip()
+ k = k[len(env_prefix):]
+
+ displayValue = sanitize_display_value(k, v)
- k = k[len(PF_ENV_PREFIX):]
if k not in keys:
print('[%s] Adding config %s = %s' % (conf_filename, k, displayValue))
lines.append('%s=%s\n' % (k, v))
@@ -99,10 +98,8 @@
print('[%s] Updating config %s = %s' % (conf_filename, k, displayValue))
lines[keys[k]] = '%s=%s\n' % (k, v)
-
# Store back the updated config in the same file
f = open(conf_filename, 'w')
for line in lines:
f.write(line)
- f.close()
-
+ f.close()
\ No newline at end of file
diff --git a/jclouds-shaded/pom.xml b/jclouds-shaded/pom.xml
index 19bbe37d99dbb..e7934969b32ee 100644
--- a/jclouds-shaded/pom.xml
+++ b/jclouds-shaded/pom.xml
@@ -1,4 +1,4 @@
-
+
-
+
4.0.0
- org.apache.pulsar
+ io.streamnative
pulsar
3.2.0-SNAPSHOT
..
-
jclouds-shaded
Apache Pulsar :: Jclouds shaded
-
-
org.apache.jclouds
jclouds-allblobstore
@@ -66,7 +61,6 @@
javax.annotation-api
-
@@ -82,7 +76,6 @@
true
true
false
-
com.google.guava:guava
@@ -106,7 +99,6 @@
com.google.errorprone:*
-
com.google
@@ -140,21 +132,20 @@
com.google.errorprone
org.apache.pulsar.jcloud.shade.com.google.errorprone
-
-
- org.apache.jclouds:jclouds-core
-
-
- lib/gson*jar
-
-
-
+
+ org.apache.jclouds:jclouds-core
+
+
+ lib/gson*jar
+
+
+
diff --git a/managed-ledger/pom.xml b/managed-ledger/pom.xml
index 318a5377d14fa..f0cadc45ecdd1 100644
--- a/managed-ledger/pom.xml
+++ b/managed-ledger/pom.xml
@@ -1,3 +1,4 @@
+
-
+
4.0.0
-
- org.apache.pulsar
+ io.streamnative
pulsar
3.2.0-SNAPSHOT
..
-
managed-ledger
Managed Ledger
-
org.apache.bookkeeper
bookkeeper-server
-
org.apache.bookkeeper.stats
prometheus-metrics-provider
-
org.apache.bookkeeper.stats
codahale-metrics-provider
@@ -54,36 +49,30 @@
-
com.google.protobuf
protobuf-java
-
${project.groupId}
pulsar-common
${project.version}
-
${project.groupId}
pulsar-metadata
${project.version}
-
com.google.guava
guava
-
${project.groupId}
testmocks
${project.version}
test
-
org.apache.zookeeper
zookeeper
@@ -105,29 +94,25 @@
- io.dropwizard.metrics
- metrics-core
- test
+ io.dropwizard.metrics
+ metrics-core
+ test
- org.xerial.snappy
- snappy-java
- test
+ org.xerial.snappy
+ snappy-java
+ test
-
org.awaitility
awaitility
test
-
org.slf4j
slf4j-api
-
-
@@ -147,7 +132,6 @@
-
org.apache.maven.plugins
maven-jar-plugin
diff --git a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/ManagedCursor.java b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/ManagedCursor.java
index d1ffdf6d2d763..bc6a1e9a782d6 100644
--- a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/ManagedCursor.java
+++ b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/ManagedCursor.java
@@ -517,6 +517,10 @@ void markDelete(Position position, Map properties)
*/
void rewind();
+ default void rewind(boolean readCompacted) {
+ rewind();
+ }
+
/**
* Move the cursor to a different read position.
*
diff --git a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/ManagedLedgerConfig.java b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/ManagedLedgerConfig.java
index 0c93a5b642cf6..6ee9c2f949243 100644
--- a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/ManagedLedgerConfig.java
+++ b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/ManagedLedgerConfig.java
@@ -170,6 +170,7 @@ public int getMinimumRolloverTimeMs() {
* the time unit
*/
public void setMinimumRolloverTime(int minimumRolloverTime, TimeUnit unit) {
+ checkArgument(minimumRolloverTime >= 0);
this.minimumRolloverTimeMs = (int) unit.toMillis(minimumRolloverTime);
checkArgument(maximumRolloverTimeMs >= minimumRolloverTimeMs,
"Minimum rollover time needs to be less than maximum rollover time");
@@ -195,6 +196,7 @@ public long getMaximumRolloverTimeMs() {
* the time unit
*/
public void setMaximumRolloverTime(int maximumRolloverTime, TimeUnit unit) {
+ checkArgument(maximumRolloverTime >= 0);
this.maximumRolloverTimeMs = unit.toMillis(maximumRolloverTime);
checkArgument(maximumRolloverTimeMs >= minimumRolloverTimeMs,
"Maximum rollover time needs to be greater than minimum rollover time");
@@ -411,7 +413,8 @@ public ManagedLedgerConfig setThrottleMarkDelete(double throttleMarkDelete) {
* time unit for retention time
*/
public ManagedLedgerConfig setRetentionTime(int retentionTime, TimeUnit unit) {
- this.retentionTimeMs = unit.toMillis(retentionTime);
+ checkArgument(retentionTime >= -1, "The retention time should be -1, 0 or value > 0");
+ this.retentionTimeMs = retentionTime != -1 ? unit.toMillis(retentionTime) : -1;
return this;
}
diff --git a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedCursorImpl.java b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedCursorImpl.java
index 4b65d62f0eee8..da013c0731307 100644
--- a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedCursorImpl.java
+++ b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedCursorImpl.java
@@ -59,6 +59,7 @@
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.stream.LongStream;
import org.apache.bookkeeper.client.AsyncCallback.CloseCallback;
import org.apache.bookkeeper.client.AsyncCallback.OpenCallback;
import org.apache.bookkeeper.client.BKException;
@@ -195,11 +196,11 @@ public class ManagedCursorImpl implements ManagedCursor {
position.ackSet = null;
return position;
};
- private final RangeSetWrapper individualDeletedMessages;
+ protected final RangeSetWrapper individualDeletedMessages;
// Maintain the deletion status for batch messages
// (ledgerId, entryId) -> deletion indexes
- private final ConcurrentSkipListMap batchDeletedIndexes;
+ protected final ConcurrentSkipListMap batchDeletedIndexes;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private RateLimiter markDeleteLimiter;
@@ -676,7 +677,7 @@ private void recoveredCursor(PositionImpl position, Map properties
LedgerHandle recoveredFromCursorLedger) {
// if the position was at a ledger that didn't exist (since it will be deleted if it was previously empty),
// we need to move to the next existing ledger
- if (!ledger.ledgerExists(position.getLedgerId())) {
+ if (position.getEntryId() == -1L && !ledger.ledgerExists(position.getLedgerId())) {
Long nextExistingLedger = ledger.getNextValidLedger(position.getLedgerId());
if (nextExistingLedger == null) {
log.info("[{}] [{}] Couldn't find next next valid ledger for recovery {}", ledger.getName(), name,
@@ -786,6 +787,8 @@ public void asyncReadEntriesWithSkip(int numberOfEntriesToRead, long maxSizeByte
int numOfEntriesToRead = applyMaxSizeCap(numberOfEntriesToRead, maxSizeBytes);
PENDING_READ_OPS_UPDATER.incrementAndGet(this);
+ // Skip deleted entries.
+ skipCondition = skipCondition == null ? this::isMessageDeleted : skipCondition.or(this::isMessageDeleted);
OpReadEntry op =
OpReadEntry.create(this, readPosition, numOfEntriesToRead, callback, ctx, maxPosition, skipCondition);
ledger.asyncReadEntries(op);
@@ -944,6 +947,8 @@ public void asyncReadEntriesWithSkipOrWait(int maxEntries, long maxSizeBytes, Re
asyncReadEntriesWithSkip(numberOfEntriesToRead, NO_MAX_SIZE_LIMIT, callback, ctx,
maxPosition, skipCondition);
} else {
+ // Skip deleted entries.
+ skipCondition = skipCondition == null ? this::isMessageDeleted : skipCondition.or(this::isMessageDeleted);
OpReadEntry op = OpReadEntry.create(this, readPosition, numberOfEntriesToRead, callback,
ctx, maxPosition, skipCondition);
@@ -2512,9 +2517,15 @@ public Position getPersistentMarkDeletedPosition() {
@Override
public void rewind() {
+ rewind(false);
+ }
+
+ @Override
+ public void rewind(boolean readCompacted) {
lock.writeLock().lock();
try {
- PositionImpl newReadPosition = ledger.getNextValidPosition(markDeletePosition);
+ PositionImpl newReadPosition =
+ readCompacted ? markDeletePosition.getNext() : ledger.getNextValidPosition(markDeletePosition);
PositionImpl oldReadPosition = readPosition;
log.info("[{}-{}] Rewind from {} to {}", ledger.getName(), name, oldReadPosition, newReadPosition);
@@ -2784,30 +2795,23 @@ public void skipNonRecoverableLedger(final long ledgerId){
if (ledgerInfo == null) {
return;
}
- lock.writeLock().lock();
log.warn("[{}] [{}] Since the ledger [{}] is lost and the autoSkipNonRecoverableData is true, this ledger will"
+ " be auto acknowledge in subscription", ledger.getName(), name, ledgerId);
- try {
- for (int i = 0; i < ledgerInfo.getEntries(); i++) {
- if (!individualDeletedMessages.contains(ledgerId, i)) {
- asyncDelete(PositionImpl.get(ledgerId, i), new AsyncCallbacks.DeleteCallback() {
- @Override
- public void deleteComplete(Object ctx) {
- // ignore.
- }
+ asyncDelete(() -> LongStream.range(0, ledgerInfo.getEntries())
+ .mapToObj(i -> (Position) PositionImpl.get(ledgerId, i)).iterator(),
+ new AsyncCallbacks.DeleteCallback() {
+ @Override
+ public void deleteComplete(Object ctx) {
+ // ignore.
+ }
- @Override
- public void deleteFailed(ManagedLedgerException ex, Object ctx) {
- // The method internalMarkDelete already handled the failure operation. We only need to
- // make sure the memory state is updated.
- // If the broker crashed, the non-recoverable ledger will be detected again.
- }
- }, null);
- }
- }
- } finally {
- lock.writeLock().unlock();
- }
+ @Override
+ public void deleteFailed(ManagedLedgerException ex, Object ctx) {
+ // The method internalMarkDelete already handled the failure operation. We only need to
+ // make sure the memory state is updated.
+ // If the broker crashed, the non-recoverable ledger will be detected again.
+ }
+ }, null);
}
// //////////////////////////////////////////////////
@@ -3613,4 +3617,29 @@ public boolean isCacheReadEntry() {
public ManagedLedgerConfig getConfig() {
return config;
}
+
+ /***
+ * Create a non-durable cursor and copy the ack stats.
+ */
+ public ManagedCursor duplicateNonDurableCursor(String nonDurableCursorName) throws ManagedLedgerException {
+ NonDurableCursorImpl newNonDurableCursor =
+ (NonDurableCursorImpl) ledger.newNonDurableCursor(getMarkDeletedPosition(), nonDurableCursorName);
+ if (individualDeletedMessages != null) {
+ this.individualDeletedMessages.forEach(range -> {
+ newNonDurableCursor.individualDeletedMessages.addOpenClosed(
+ range.lowerEndpoint().getLedgerId(),
+ range.lowerEndpoint().getEntryId(),
+ range.upperEndpoint().getLedgerId(),
+ range.upperEndpoint().getEntryId());
+ return true;
+ });
+ }
+ if (batchDeletedIndexes != null) {
+ for (Map.Entry entry : this.batchDeletedIndexes.entrySet()) {
+ BitSetRecyclable copiedBitSet = BitSetRecyclable.valueOf(entry.getValue());
+ newNonDurableCursor.batchDeletedIndexes.put(entry.getKey(), copiedBitSet);
+ }
+ }
+ return newNonDurableCursor;
+ }
}
diff --git a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerFactoryMBeanImpl.java b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerFactoryMBeanImpl.java
index cf3d7142d617e..5a6bc8017b7e0 100644
--- a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerFactoryMBeanImpl.java
+++ b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerFactoryMBeanImpl.java
@@ -18,6 +18,7 @@
*/
package org.apache.bookkeeper.mledger.impl;
+import static com.google.common.base.Preconditions.checkArgument;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;
import org.apache.bookkeeper.mledger.ManagedLedgerFactoryMXBean;
@@ -41,6 +42,7 @@ public ManagedLedgerFactoryMBeanImpl(ManagedLedgerFactoryImpl factory) throws Ex
}
public void refreshStats(long period, TimeUnit unit) {
+ checkArgument(period >= 0);
double seconds = unit.toMillis(period) / 1000.0;
if (seconds <= 0.0) {
diff --git a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerImpl.java b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerImpl.java
index 8ce2a6924ebed..75ac4dd4c0a44 100644
--- a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerImpl.java
+++ b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerImpl.java
@@ -3242,7 +3242,7 @@ void offloadLoop(CompletableFuture promise, Queue ledg
.thenCompose(readHandle -> config.getLedgerOffloader().offload(readHandle, uuid, extraMetadata))
.thenCompose((ignore) -> {
return Retries.run(Backoff.exponentialJittered(TimeUnit.SECONDS.toMillis(1),
- TimeUnit.SECONDS.toHours(1)).limit(10),
+ TimeUnit.HOURS.toMillis(1)).limit(10),
FAIL_ON_CONFLICT,
() -> completeLedgerInfoForOffloaded(ledgerId, uuid),
scheduledExecutor, name)
@@ -4459,7 +4459,8 @@ private void cancelScheduledTasks() {
@Override
public boolean checkInactiveLedgerAndRollOver() {
long currentTimeMs = System.currentTimeMillis();
- if (inactiveLedgerRollOverTimeMs > 0 && currentTimeMs > (lastAddEntryTimeMs + inactiveLedgerRollOverTimeMs)) {
+ if (currentLedgerEntries > 0 && inactiveLedgerRollOverTimeMs > 0 && currentTimeMs > (lastAddEntryTimeMs
+ + inactiveLedgerRollOverTimeMs)) {
log.info("[{}] Closing inactive ledger, last-add entry {}", name, lastAddEntryTimeMs);
if (STATE_UPDATER.compareAndSet(this, State.LedgerOpened, State.ClosingLedger)) {
LedgerHandle currentLedger = this.currentLedger;
@@ -4537,4 +4538,4 @@ public Position getTheSlowestNonDurationReadPosition() {
}
return theSlowestNonDurableReadPosition;
}
-}
+}
\ No newline at end of file
diff --git a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerMBeanImpl.java b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerMBeanImpl.java
index e057dee99538e..7884add95526c 100644
--- a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerMBeanImpl.java
+++ b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerMBeanImpl.java
@@ -18,6 +18,7 @@
*/
package org.apache.bookkeeper.mledger.impl;
+import static com.google.common.base.Preconditions.checkArgument;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;
import org.apache.bookkeeper.mledger.ManagedCursor;
@@ -63,6 +64,7 @@ public ManagedLedgerMBeanImpl(ManagedLedgerImpl managedLedger) {
}
public void refreshStats(long period, TimeUnit unit) {
+ checkArgument(period >= 0);
double seconds = unit.toMillis(period) / 1000.0;
if (seconds <= 0.0) {
// skip refreshing stats
diff --git a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ReadOnlyCursorImpl.java b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ReadOnlyCursorImpl.java
index 9102339b2904e..1661613f07d7d 100644
--- a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ReadOnlyCursorImpl.java
+++ b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ReadOnlyCursorImpl.java
@@ -23,6 +23,7 @@
import org.apache.bookkeeper.client.BookKeeper;
import org.apache.bookkeeper.mledger.AsyncCallbacks;
import org.apache.bookkeeper.mledger.ManagedLedgerConfig;
+import org.apache.bookkeeper.mledger.Position;
import org.apache.bookkeeper.mledger.ReadOnlyCursor;
import org.apache.bookkeeper.mledger.impl.ManagedLedgerImpl.PositionBound;
import org.apache.bookkeeper.mledger.proto.MLDataFormats;
@@ -70,4 +71,9 @@ public MLDataFormats.ManagedLedgerInfo.LedgerInfo getCurrentLedgerInfo() {
public long getNumberOfEntries(Range range) {
return this.ledger.getNumberOfEntries(range);
}
+
+ @Override
+ public boolean isMessageDeleted(Position position) {
+ return false;
+ }
}
diff --git a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/offload/OffloadUtils.java b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/offload/OffloadUtils.java
index 550626f76c000..9c9feb2aa7f7c 100644
--- a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/offload/OffloadUtils.java
+++ b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/offload/OffloadUtils.java
@@ -198,7 +198,7 @@ public static CompletableFuture cleanupOffloaded(long ledgerId, UUID uuid,
metadataMap.put("ManagedLedgerName", name);
return Retries.run(Backoff.exponentialJittered(TimeUnit.SECONDS.toMillis(1),
- TimeUnit.SECONDS.toHours(1)).limit(10),
+ TimeUnit.HOURS.toMillis(1)).limit(10),
Retries.NonFatalPredicate,
() -> mlConfig.getLedgerOffloader().deleteOffloaded(ledgerId, uuid, metadataMap),
executor, name).whenComplete((ignored, exception) -> {
diff --git a/managed-ledger/src/test/java/org/apache/bookkeeper/mledger/impl/ManagedCursorTest.java b/managed-ledger/src/test/java/org/apache/bookkeeper/mledger/impl/ManagedCursorTest.java
index 627ae73d928bd..644f53c3a522d 100644
--- a/managed-ledger/src/test/java/org/apache/bookkeeper/mledger/impl/ManagedCursorTest.java
+++ b/managed-ledger/src/test/java/org/apache/bookkeeper/mledger/impl/ManagedCursorTest.java
@@ -43,6 +43,7 @@
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -65,6 +66,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import lombok.Cleanup;
@@ -72,6 +74,7 @@
import org.apache.bookkeeper.client.BookKeeper;
import org.apache.bookkeeper.client.BookKeeper.DigestType;
import org.apache.bookkeeper.client.LedgerEntry;
+import org.apache.bookkeeper.client.api.ReadHandle;
import org.apache.bookkeeper.mledger.AsyncCallbacks;
import org.apache.bookkeeper.mledger.AsyncCallbacks.AddEntryCallback;
import org.apache.bookkeeper.mledger.AsyncCallbacks.DeleteCallback;
@@ -766,7 +769,7 @@ void testResetCursor() throws Exception {
@Test(timeOut = 20000)
void testResetCursor1() throws Exception {
ManagedLedger ledger = factory.open("my_test_move_cursor_ledger",
- new ManagedLedgerConfig().setMaxEntriesPerLedger(2));
+ new ManagedLedgerConfig().setMaxEntriesPerLedger(2));
ManagedCursor cursor = ledger.openCursor("trc1");
PositionImpl actualEarliest = (PositionImpl) ledger.addEntry("dummy-entry-1".getBytes(Encoding));
ledger.addEntry("dummy-entry-2".getBytes(Encoding));
@@ -2286,7 +2289,7 @@ void testFindNewestMatchingEdgeCase1() throws Exception {
ManagedCursorImpl c1 = (ManagedCursorImpl) ledger.openCursor("c1");
assertNull(c1.findNewestMatching(
- entry -> Arrays.equals(entry.getDataAndRelease(), "expired".getBytes(Encoding))));
+ entry -> Arrays.equals(entry.getDataAndRelease(), "expired".getBytes(Encoding))));
}
@Test(timeOut = 20000)
@@ -2595,7 +2598,7 @@ public void findEntryComplete(Position position, Object ctx) {
@Override
public void findEntryFailed(ManagedLedgerException exception, Optional failedReadPosition,
- Object ctx) {
+ Object ctx) {
result.exception = exception;
counter.countDown();
}
@@ -2621,7 +2624,7 @@ public void findEntryFailed(ManagedLedgerException exception, Optional
}
void internalTestFindNewestMatchingAllEntries(final String name, final int entriesPerLedger,
- final int expectedEntryId) throws Exception {
+ final int expectedEntryId) throws Exception {
final String ledgerAndCursorName = name;
ManagedLedgerConfig config = new ManagedLedgerConfig();
config.setRetentionSizeInMB(10);
@@ -2715,7 +2718,7 @@ void testReplayEntries() throws Exception {
assertTrue((Arrays.equals(entries.get(0).getData(), "entry1".getBytes(Encoding))
&& Arrays.equals(entries.get(1).getData(), "entry3".getBytes(Encoding)))
|| (Arrays.equals(entries.get(0).getData(), "entry3".getBytes(Encoding))
- && Arrays.equals(entries.get(1).getData(), "entry1".getBytes(Encoding))));
+ && Arrays.equals(entries.get(1).getData(), "entry1".getBytes(Encoding))));
entries.forEach(Entry::release);
// 3. Fail on reading non-existing position
@@ -3142,7 +3145,7 @@ public void operationFailed(ManagedLedgerException exception) {
try {
bkc.openLedgerNoRecovery(ledgerId, DigestType.fromApiDigestType(mlConfig.getDigestType()),
- mlConfig.getPassword());
+ mlConfig.getPassword());
fail("ledger should have deleted due to update-cursor failure");
} catch (BKException e) {
// ok
@@ -3761,17 +3764,17 @@ private void deleteBatchIndex(ManagedCursor cursor, Position position, int batch
pos.ackSet = bitSet.toLongArray();
cursor.asyncDelete(pos,
- new DeleteCallback() {
- @Override
- public void deleteComplete(Object ctx) {
- latch.countDown();
- }
+ new DeleteCallback() {
+ @Override
+ public void deleteComplete(Object ctx) {
+ latch.countDown();
+ }
- @Override
- public void deleteFailed(ManagedLedgerException exception, Object ctx) {
- latch.countDown();
- }
- }, null);
+ @Override
+ public void deleteFailed(ManagedLedgerException exception, Object ctx) {
+ latch.countDown();
+ }
+ }, null);
latch.await();
pos.ackSet = null;
}
@@ -4484,5 +4487,202 @@ public void readEntriesFailed(ManagedLedgerException exception, Object ctx) {
ledger.close();
}
+
+ @Test
+ public void testReadEntriesWithSkipDeletedEntries() throws Exception {
+ @Cleanup
+ ManagedLedgerImpl ledger = (ManagedLedgerImpl) factory.open("testReadEntriesWithSkipDeletedEntries");
+ ledger = Mockito.spy(ledger);
+ List actualReadEntryIds = new ArrayList<>();
+ Mockito.doAnswer(inv -> {
+ long start = inv.getArgument(1);
+ long end = inv.getArgument(2);
+ for (long i = start; i <= end; i++) {
+ actualReadEntryIds.add(i);
+ }
+ return inv.callRealMethod();
+ })
+ .when(ledger)
+ .asyncReadEntry(Mockito.any(ReadHandle.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.any(), Mockito.any());
+ @Cleanup
+ ManagedCursor cursor = ledger.openCursor("c");
+
+ int entries = 20;
+ Position maxReadPosition = null;
+ Map map = new HashMap<>();
+ for (int i = 0; i < entries; i++) {
+ maxReadPosition = ledger.addEntry(new byte[1024]);
+ map.put(i, maxReadPosition);
+ }
+
+
+ Set deletedPositions = new HashSet<>();
+ deletedPositions.add(map.get(1));
+ deletedPositions.add(map.get(4));
+ deletedPositions.add(map.get(5));
+ deletedPositions.add(map.get(8));
+ deletedPositions.add(map.get(9));
+ deletedPositions.add(map.get(10));
+ deletedPositions.add(map.get(15));
+ deletedPositions.add(map.get(17));
+ deletedPositions.add(map.get(19));
+ cursor.delete(deletedPositions);
+
+ CompletableFuture f0 = new CompletableFuture<>();
+ List readEntries = new ArrayList<>();
+ cursor.asyncReadEntries(5, -1L, new ReadEntriesCallback() {
+ @Override
+ public void readEntriesComplete(List entries, Object ctx) {
+ readEntries.addAll(entries);
+ f0.complete(null);
+ }
+
+ @Override
+ public void readEntriesFailed(ManagedLedgerException exception, Object ctx) {
+ f0.completeExceptionally(exception);
+ }
+ }, null, PositionImpl.get(maxReadPosition.getLedgerId(), maxReadPosition.getEntryId()).getNext());
+
+ f0.get();
+
+ CompletableFuture f1 = new CompletableFuture<>();
+ cursor.asyncReadEntries(5, -1L, new ReadEntriesCallback() {
+ @Override
+ public void readEntriesComplete(List entries, Object ctx) {
+ readEntries.addAll(entries);
+ f1.complete(null);
+ }
+
+ @Override
+ public void readEntriesFailed(ManagedLedgerException exception, Object ctx) {
+ f1.completeExceptionally(exception);
+ }
+ }, null, PositionImpl.get(maxReadPosition.getLedgerId(), maxReadPosition.getEntryId()).getNext());
+
+
+ f1.get();
+ CompletableFuture f2 = new CompletableFuture<>();
+ cursor.asyncReadEntries(100, -1L, new ReadEntriesCallback() {
+ @Override
+ public void readEntriesComplete(List entries, Object ctx) {
+ readEntries.addAll(entries);
+ f2.complete(null);
+ }
+
+ @Override
+ public void readEntriesFailed(ManagedLedgerException exception, Object ctx) {
+ f2.completeExceptionally(exception);
+ }
+ }, null, PositionImpl.get(maxReadPosition.getLedgerId(), maxReadPosition.getEntryId()).getNext());
+
+ f2.get();
+
+ Position cursorReadPosition = cursor.getReadPosition();
+ Position expectReadPosition = maxReadPosition.getNext();
+ assertTrue(cursorReadPosition.getLedgerId() == expectReadPosition.getLedgerId()
+ && cursorReadPosition.getEntryId() == expectReadPosition.getEntryId());
+
+ assertEquals(readEntries.size(), actualReadEntryIds.size());
+ assertEquals(entries - deletedPositions.size(), actualReadEntryIds.size());
+ for (Entry entry : readEntries) {
+ long entryId = entry.getEntryId();
+ assertTrue(actualReadEntryIds.contains(entryId));
+ }
+ }
+
+
+ @Test
+ public void testReadEntriesWithSkipDeletedEntriesAndWithSkipConditions() throws Exception {
+ @Cleanup
+ ManagedLedgerImpl ledger = (ManagedLedgerImpl)
+ factory.open("testReadEntriesWithSkipDeletedEntriesAndWithSkipConditions");
+ ledger = Mockito.spy(ledger);
+
+ List actualReadEntryIds = new ArrayList<>();
+ Mockito.doAnswer(inv -> {
+ long start = inv.getArgument(1);
+ long end = inv.getArgument(2);
+ for (long i = start; i <= end; i++) {
+ actualReadEntryIds.add(i);
+ }
+ return inv.callRealMethod();
+ })
+ .when(ledger)
+ .asyncReadEntry(Mockito.any(ReadHandle.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.any(), Mockito.any());
+ @Cleanup
+ ManagedCursor cursor = ledger.openCursor("c");
+
+ int entries = 20;
+ Position maxReadPosition0 = null;
+ Map map = new HashMap<>();
+ for (int i = 0; i < entries; i++) {
+ maxReadPosition0 = ledger.addEntry(new byte[1024]);
+ map.put(i, maxReadPosition0);
+ }
+
+ PositionImpl maxReadPosition =
+ PositionImpl.get(maxReadPosition0.getLedgerId(), maxReadPosition0.getEntryId()).getNext();
+
+ Set deletedPositions = new HashSet<>();
+ deletedPositions.add(map.get(1));
+ deletedPositions.add(map.get(3));
+ deletedPositions.add(map.get(5));
+ cursor.delete(deletedPositions);
+
+ Set skippedPositions = new HashSet<>();
+ skippedPositions.add(map.get(6).getEntryId());
+ skippedPositions.add(map.get(7).getEntryId());
+ skippedPositions.add(map.get(8).getEntryId());
+ skippedPositions.add(map.get(11).getEntryId());
+ skippedPositions.add(map.get(15).getEntryId());
+ skippedPositions.add(map.get(16).getEntryId());
+
+ Predicate skipCondition = position -> skippedPositions.contains(position.getEntryId());
+ List readEntries = new ArrayList<>();
+
+ CompletableFuture f0 = new CompletableFuture<>();
+ cursor.asyncReadEntriesWithSkip(10, -1L, new ReadEntriesCallback() {
+ @Override
+ public void readEntriesComplete(List entries, Object ctx) {
+ readEntries.addAll(entries);
+ f0.complete(null);
+ }
+
+ @Override
+ public void readEntriesFailed(ManagedLedgerException exception, Object ctx) {
+ f0.completeExceptionally(exception);
+ }
+ }, null, maxReadPosition, skipCondition);
+
+ f0.get();
+ CompletableFuture f1 = new CompletableFuture<>();
+ cursor.asyncReadEntriesWithSkip(100, -1L, new ReadEntriesCallback() {
+ @Override
+ public void readEntriesComplete(List entries, Object ctx) {
+ readEntries.addAll(entries);
+ f1.complete(null);
+ }
+
+ @Override
+ public void readEntriesFailed(ManagedLedgerException exception, Object ctx) {
+ f1.completeExceptionally(exception);
+ }
+ }, null, maxReadPosition, skipCondition);
+ f1.get();
+
+
+ assertEquals(actualReadEntryIds.size(), readEntries.size());
+ assertEquals(entries - deletedPositions.size() - skippedPositions.size(), actualReadEntryIds.size());
+ for (Entry entry : readEntries) {
+ long entryId = entry.getEntryId();
+ assertTrue(actualReadEntryIds.contains(entryId));
+ }
+
+ Position cursorReadPosition = cursor.getReadPosition();
+ Position expectReadPosition = maxReadPosition;
+ assertTrue(cursorReadPosition.getLedgerId() == expectReadPosition.getLedgerId()
+ && cursorReadPosition.getEntryId() == expectReadPosition.getEntryId());
+ }
+
private static final Logger log = LoggerFactory.getLogger(ManagedCursorTest.class);
}
diff --git a/managed-ledger/src/test/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerTest.java b/managed-ledger/src/test/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerTest.java
index 8430afb4e4f82..4c92911c687f5 100644
--- a/managed-ledger/src/test/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerTest.java
+++ b/managed-ledger/src/test/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerTest.java
@@ -3898,6 +3898,30 @@ public void testInactiveLedgerRollOver() throws Exception {
ledger.close();
}
+ @Test
+ public void testDontRollOverEmptyInactiveLedgers() throws Exception {
+ int inactiveLedgerRollOverTimeMs = 5;
+ ManagedLedgerFactoryConfig factoryConf = new ManagedLedgerFactoryConfig();
+ @Cleanup("shutdown")
+ ManagedLedgerFactory factory = new ManagedLedgerFactoryImpl(metadataStore, bkc);
+ ManagedLedgerConfig config = new ManagedLedgerConfig();
+ config.setInactiveLedgerRollOverTime(inactiveLedgerRollOverTimeMs, TimeUnit.MILLISECONDS);
+ ManagedLedgerImpl ledger = (ManagedLedgerImpl) factory.open("rollover_inactive", config);
+ ManagedCursor cursor = ledger.openCursor("c1");
+
+ long ledgerId = ledger.currentLedger.getId();
+
+ Thread.sleep(inactiveLedgerRollOverTimeMs * 5);
+ ledger.checkInactiveLedgerAndRollOver();
+
+ Thread.sleep(inactiveLedgerRollOverTimeMs * 5);
+ ledger.checkInactiveLedgerAndRollOver();
+
+ assertEquals(ledger.currentLedger.getId(), ledgerId);
+
+ ledger.close();
+ }
+
@Test
public void testOffloadTaskCancelled() throws Exception {
@Cleanup("shutdown")
@@ -4094,6 +4118,7 @@ public void testNonDurableCursorCreateForInactiveLedger() throws Exception {
ManagedLedgerConfig config = new ManagedLedgerConfig();
config.setInactiveLedgerRollOverTime(10, TimeUnit.MILLISECONDS);
ManagedLedgerImpl ml = (ManagedLedgerImpl) factory.open(mlName, config);
+ ml.addEntry("entry".getBytes(UTF_8));
MutableBoolean isRolledOver = new MutableBoolean(false);
retryStrategically((test) -> {
diff --git a/microbench/pom.xml b/microbench/pom.xml
index a62876e8802f7..a175c761bf184 100644
--- a/microbench/pom.xml
+++ b/microbench/pom.xml
@@ -1,3 +1,4 @@
+
-
+
4.0.0
-
- org.apache.pulsar
+ io.streamnative
pulsar
3.2.0-SNAPSHOT
../pom.xml
-
microbench
jar
Pulsar Microbenchmarks
-
1.37
-
microbench
@@ -79,7 +75,6 @@
-
org.openjdk.jmh
@@ -98,7 +93,6 @@
${project.version}
-
@@ -130,4 +124,4 @@
-
\ No newline at end of file
+
diff --git a/microbench/src/main/resources/log4j2.xml b/microbench/src/main/resources/log4j2.xml
index 7ec5ed8169a66..c48941ef785e5 100644
--- a/microbench/src/main/resources/log4j2.xml
+++ b/microbench/src/main/resources/log4j2.xml
@@ -1,4 +1,4 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 6844f20e1aa9e..bc04cd2d09841 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,4 +1,4 @@
-
+
-
+
4.0.0
pom
@@ -28,30 +27,24 @@
apache
29
-
- org.apache.pulsar
+ io.streamnative
pulsar
-
3.2.0-SNAPSHOT
-
Pulsar
Pulsar is a distributed pub-sub messaging platform with a very
flexible messaging model and an intuitive client API.
https://github.com/apache/pulsar
-
Apache Software Foundation
https://www.apache.org/
2017
-
Apache Pulsar developers
https://pulsar.apache.org/
-
Apache License, Version 2.0
@@ -59,47 +52,38 @@ flexible messaging model and an intuitive client API.
repo
-
- https://github.com/apache/pulsar
- scm:git:https://github.com/apache/pulsar.git
- scm:git:ssh://git@github.com:apache/pulsar.git
+ https://github.com/streamnative/pulsar
+ scm:git:https://github.com/streamnative/pulsar.git
+ scm:git:ssh://git@github.com:streamnative/pulsar.git
-
GitHub Actions
https://github.com/apache/pulsar/actions
-
Github
https://github.com/apache/pulsar/issues
-
17
17
${maven.compiler.target}
8
-
- 3.2.0
-
+ 3.4.0
**/Test*.java,**/*Test.java,**/*Tests.java,**/*TestCase.java
quarantine
-
UTF-8
UTF-8
- 2023-09-08T03:29:19Z
+ 2024-03-04T21:08:59Z
true
-
-
-
+
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED
@@ -116,7 +100,7 @@ flexible messaging model and an intuitive client API.
false
1
true
-
+
false
${project.build.directory}
@@ -129,20 +113,20 @@ flexible messaging model and an intuitive client API.
false
package
package
-
- 1.21
-
- 4.16.3
+ 1.26.0
+ 4.16.4
3.9.1
1.5.0
1.10.0
- 1.1.10.5
- 4.1.12.1
+ 1.1.10.5
+
+ 4.1.12.1
+
5.1.0
4.1.104.Final
0.0.24.Final
- 9.4.53.v20231009
+ 9.4.54.v20240208
2.5.2
2.34
1.10.50
@@ -162,6 +146,8 @@ flexible messaging model and an intuitive client API.
0.43.3
true
0.5.0
+ 1.14.12
+ 1.17
3.19.6
${protobuf3.version}
1.55.3
@@ -248,11 +234,9 @@ flexible messaging model and an intuitive client API.
3.4.3
1.5.2-3
2.0.6
-
1.18.3
2.2
-
3.3.0
1.1.1
@@ -260,12 +244,12 @@ flexible messaging model and an intuitive client API.
5.6.0
3.25.0-GA
1.5.0
- 3.3
+ 3.3
+
4.2.0
1.5.4
5.4.0
2.33.2
-
0.6.1
3.0.0
@@ -300,20 +284,16 @@ flexible messaging model and an intuitive client API.
1.6.1
6.4.0
3.33.0
-
rename-netty-native-libs.sh
-
-
org.jline
jline
${jline3.version}
-
org.asynchttpclient
async-http-client
@@ -329,39 +309,34 @@ flexible messaging model and an intuitive client API.
-
org.testng
testng
${testng.version}
-
- org.yaml
- *
-
+
+ org.yaml
+ *
+
-
org.hamcrest
hamcrest
${hamcrest.version}
test
-
org.awaitility
awaitility
${awaitility.version}
test
-
org.mockito
mockito-core
${mockito.version}
-
org.apache.zookeeper
zookeeper
@@ -437,7 +412,6 @@ flexible messaging model and an intuitive client API.
-
org.apache.bookkeeper
bookkeeper-server
@@ -466,13 +440,11 @@ flexible messaging model and an intuitive client API.
-
org.apache.bookkeeper
cpu-affinity
${bookkeeper.version}
-
io.vertx
vertx-core
@@ -483,38 +455,33 @@ flexible messaging model and an intuitive client API.
vertx-web
${vertx.version}
-
- org.apache.curator
- curator-recipes
- ${curator.version}
-
-
- org.apache.zookeeper
- *
-
-
+ org.apache.curator
+ curator-recipes
+ ${curator.version}
+
+
+ org.apache.zookeeper
+ *
+
+
-
org.apache.bookkeeper
bookkeeper-common-allocator
${bookkeeper.version}
-
org.apache.bookkeeper
bookkeeper-tools-framework
${bookkeeper.version}
-
org.reflections
reflections
${reflections.version}
-
org.apache.bookkeeper
@@ -547,7 +514,6 @@ flexible messaging model and an intuitive client API.
-
org.apache.bookkeeper
@@ -580,19 +546,16 @@ flexible messaging model and an intuitive client API.
-
org.apache.bookkeeper
bookkeeper-common
${bookkeeper.version}
-
org.apache.bookkeeper.stats
bookkeeper-stats-api
${bookkeeper.version}
-
org.apache.bookkeeper.stats
datasketches-metrics-provider
@@ -604,37 +567,31 @@ flexible messaging model and an intuitive client API.
-
org.apache.bookkeeper.stats
prometheus-metrics-provider
${bookkeeper.version}
-
org.rocksdb
rocksdbjni
${rocksdb.version}
-
org.eclipse.jetty
jetty-server
${jetty.version}
-
org.eclipse.jetty
jetty-alpn-conscrypt-server
${jetty.version}
-
org.conscrypt
conscrypt-openjdk-uber
${conscrypt.version}
-
org.eclipse.jetty
jetty-bom
@@ -642,7 +599,6 @@ flexible messaging model and an intuitive client API.
pom
import
-
io.netty
netty-bom
@@ -650,7 +606,6 @@ flexible messaging model and an intuitive client API.
pom
import
-
io.netty.incubator
netty-incubator-transport-classes-io_uring
@@ -673,79 +628,66 @@ flexible messaging model and an intuitive client API.
${netty-iouring.version}
linux-aarch_64
-
com.beust
jcommander
${jcommander.version}
-
com.google.guava
guava
${guava.version}
-
com.google.inject
guice
${guice.version}
-
com.google.inject.extensions
guice-assistedinject
${guice.version}
-
org.apache.commons
commons-lang3
${commons-lang3.version}
-
org.apache.commons
commons-compress
${commons-compress.version}
-
commons-configuration
commons-configuration
${commons-configuration.version}
-
commons-io
commons-io
${commons-io.version}
-
org.apache.commons
commons-text
${commons-text.version}
-
org.slf4j
slf4j-api
${slf4j.version}
-
org.slf4j
slf4j-simple
${slf4j.version}
-
org.slf4j
jcl-over-slf4j
${slf4j.version}
-
org.apache.logging.log4j
log4j-bom
@@ -753,49 +695,41 @@ flexible messaging model and an intuitive client API.
pom
import
-
commons-codec
commons-codec
${commons-codec.version}
-
org.glassfish.jersey.core
jersey-server
${jersey.version}
-
org.glassfish.jersey.core
jersey-client
${jersey.version}
-
org.glassfish.jersey.inject
jersey-hk2
${jersey.version}
-
org.glassfish.jersey.containers
jersey-container-servlet-core
${jersey.version}
-
org.glassfish.jersey.containers
jersey-container-servlet
${jersey.version}
-
javax.ws.rs
javax.ws.rs-api
${javax.ws.rs-api.version}
-
org.glassfish.jersey.media
jersey-media-json-jackson
@@ -807,19 +741,16 @@ flexible messaging model and an intuitive client API.
-
org.glassfish.jersey.media
jersey-media-multipart
${jersey.version}
-
net.java.dev.jna
jna
${jna.version}
-
com.github.docker-java
docker-java-core
@@ -841,7 +772,6 @@ flexible messaging model and an intuitive client API.
docker-java-transport-zerodep
${docker-java.version}
-
com.fasterxml.jackson
jackson-bom
@@ -849,56 +779,46 @@ flexible messaging model and an intuitive client API.
pom
import
-
org.codehaus.jettison
jettison
${jettison.version}
-
com.fasterxml.woodstox
woodstox-core
${woodstox.version}
-
-
org.hdrhistogram
HdrHistogram
${hdrHistogram.version}
-
io.swagger
swagger-core
${swagger.version}
-
io.swagger
swagger-annotations
${swagger.version}
-
javax.servlet
javax.servlet-api
${javax.servlet-api}
-
com.github.ben-manes.caffeine
caffeine
${caffeine.version}
-
org.bouncycastle
bcpkix-jdk18on
${bouncycastle.version}
-
com.cronutils
cron-utils
@@ -910,19 +830,16 @@ flexible messaging model and an intuitive client API.
-
com.yahoo.athenz
athenz-zts-java-client-core
${athenz.version}
-
com.yahoo.athenz
athenz-zpe-java-client
${athenz.version}
-
com.yahoo.athenz
athenz-cert-refresher
@@ -934,7 +851,6 @@ flexible messaging model and an intuitive client API.
-
com.yahoo.athenz
athenz-auth-core
@@ -946,67 +862,56 @@ flexible messaging model and an intuitive client API.
-
com.github.zafarkhaja
java-semver
${java-semver.version}
-
io.prometheus
simpleclient
${prometheus.version}
-
io.prometheus
simpleclient_hotspot
${prometheus.version}
-
io.prometheus
simpleclient_log4j2
${prometheus.version}
-
io.prometheus
simpleclient_servlet
${prometheus.version}
-
io.prometheus
simpleclient_jetty
${prometheus.version}
-
io.prometheus
simpleclient_caffeine
${prometheus.version}
-
com.carrotsearch
hppc
${hppc.version}
-
io.etcd
jetcd-core
${jetcd.version}
-
io.etcd
jetcd-test
${jetcd.version}
-
org.apache.spark
spark-streaming_2.10
@@ -1034,7 +939,6 @@ flexible messaging model and an intuitive client API.
-
io.jsonwebtoken
jjwt-api
@@ -1050,13 +954,21 @@ flexible messaging model and an intuitive client API.
jjwt-jackson
${jsonwebtoken.version}
-
net.jodah
typetools
${typetools.version}
-
+
+ net.bytebuddy
+ byte-buddy
+ ${byte-buddy.version}
+
+
+ org.zeroturnaround
+ zt-zip
+ ${zt-zip.version}
+
io.grpc
grpc-bom
@@ -1064,7 +976,6 @@ flexible messaging model and an intuitive client API.
pom
import
-
io.grpc
grpc-all
@@ -1088,7 +999,6 @@ flexible messaging model and an intuitive client API.
-
io.grpc
grpc-xds
@@ -1100,25 +1010,21 @@ flexible messaging model and an intuitive client API.
-
com.google.http-client
google-http-client
${google-http-client.version}
-
com.google.http-client
google-http-client-jackson2
${google-http-client.version}
-
com.google.http-client
google-http-client-gson
${google-http-client.version}
-
io.perfmark
perfmark-api
@@ -1131,7 +1037,6 @@ flexible messaging model and an intuitive client API.
-
com.google.protobuf
protobuf-bom
@@ -1139,19 +1044,16 @@ flexible messaging model and an intuitive client API.
pom
import
-
com.google.code.gson
gson
${gson.version}
-
com.yahoo.datasketches
sketches-core
${sketches.version}
-
com.amazonaws
aws-java-sdk-bom
@@ -1159,7 +1061,6 @@ flexible messaging model and an intuitive client API.
pom
import
-
org.apache.distributedlog
distributedlog-core
@@ -1172,13 +1073,11 @@ flexible messaging model and an intuitive client API.
-
org.apache.commons
commons-collections4
${commons.collections4.version}
-
com.lmax
@@ -1202,103 +1101,86 @@ flexible messaging model and an intuitive client API.
assertj-core
${assertj-core.version}
-
org.projectlombok
lombok
${lombok.version}
-
javax.annotation
javax.annotation-api
${javax.annotation-api.version}
-
javax.xml.bind
jaxb-api
${jaxb-api}
-
jakarta.xml.bind
jakarta.xml.bind-api
${jakarta.xml.bind.version}
-
com.sun.activation
javax.activation
${javax.activation.version}
-
com.sun.activation
jakarta.activation
${jakarta.activation.version}
-
jakarta.activation
jakarta.activation-api
${jakarta.activation.version}
-
jakarta.validation
jakarta.validation-api
${jakarta.validation.version}
-
io.opencensus
opencensus-api
${opencensus.version}
-
io.opencensus
opencensus-contrib-http-util
${opencensus.version}
-
io.opencensus
opencensus-contrib-grpc-metrics
${opencensus.version}
-
org.opensearch.client
opensearch-rest-high-level-client
${opensearch.version}
-
co.elastic.clients
elasticsearch-java
${elasticsearch-java.version}
-
joda-time
joda-time
${joda.version}
-
org.javassist
javassist
${javassist.version}
-
net.jcip
jcip-annotations
${jcip.version}
-
io.airlift
aircompressor
@@ -1310,25 +1192,21 @@ flexible messaging model and an intuitive client API.
-
org.objenesis
objenesis
${objenesis.version}
-
org.apache.httpcomponents
httpclient
${apache-http-client.version}
-
org.apache.httpcomponents
httpcore
${apache-httpcomponents.version}
-
com.github.spotbugs
spotbugs-annotations
@@ -1336,31 +1214,26 @@ flexible messaging model and an intuitive client API.
provided
true
-
com.google.errorprone
error_prone_annotations
${errorprone.version}
-
com.google.j2objc
j2objc-annotations
${j2objc-annotations.version}
-
org.yaml
snakeyaml
${snakeyaml.version}
-
org.apache.ant
ant
${ant.version}
-
com.squareup.okhttp3
okhttp
@@ -1381,7 +1254,6 @@ flexible messaging model and an intuitive client API.
okio
${okio.version}
-
org.jetbrains.kotlin
kotlin-stdlib
@@ -1392,25 +1264,21 @@ flexible messaging model and an intuitive client API.
kotlin-stdlib-common
${kotlin-stdlib.version}
-
org.jetbrains.kotlin
kotlin-stdlib-jdk8
${kotlin-stdlib.version}
-
com.github.luben
zstd-jni
${zstd-jni.version}
-
- com.typesafe.netty
- netty-reactive-streams
- ${netty-reactive-streams.version}
+ com.typesafe.netty
+ netty-reactive-streams
+ ${netty-reactive-streams.version}
-
org.roaringbitmap
RoaringBitmap
@@ -1428,41 +1296,35 @@ flexible messaging model and an intuitive client API.
-
- org.apache.pulsar
+ io.streamnative
buildtools
${project.version}
test
-
org.testng
testng
test
-
org.mockito
mockito-core
test
-
com.github.stefanbirkner
system-lambda
${system-lambda.version}
test
-
org.assertj
assertj-core
test
-
org.projectlombok
lombok
@@ -1473,7 +1335,6 @@ flexible messaging model and an intuitive client API.
javax.annotation-api
provided
-
org.apache.bookkeeper
@@ -1483,8 +1344,8 @@ flexible messaging model and an intuitive client API.
tests
- org.bouncycastle
- *
+ org.bouncycastle
+ *
org.slf4j
@@ -1518,8 +1379,12 @@ flexible messaging model and an intuitive client API.
+
+ org.apache.logging.log4j
+ log4j-layout-template-json
+ 2.20.0
+
-
${project.artifactId}
@@ -1594,7 +1459,6 @@ flexible messaging model and an intuitive client API.
-
org.commonjava.maven.plugins
directory-maven-plugin
@@ -1609,14 +1473,13 @@ flexible messaging model and an intuitive client API.
pulsar.basedir
- org.apache.pulsar
+ io.streamnative
pulsar
-
pl.project13.maven
git-commit-id-plugin
@@ -1641,7 +1504,6 @@ flexible messaging model and an intuitive client API.
-
com.mycila
license-maven-plugin
@@ -1742,33 +1604,26 @@ flexible messaging model and an intuitive client API.
src/assemble/README.bin.txt
src/assemble/LICENSE.bin.txt
src/assemble/NOTICE.bin.txt
-
src/main/java/org/apache/bookkeeper/mledger/proto/MLDataFormats.java
src/main/java/org/apache/pulsar/broker/service/schema/proto/SchemaRegistryFormat.java
bin/proto/MLDataFormats_pb2.py
-
**/avro/generated/*.java
-
**/*.avsc
-
src/main/java/org/apache/pulsar/io/kinesis/fbs/CompressionType.java
src/main/java/org/apache/pulsar/io/kinesis/fbs/EncryptionCtx.java
src/main/java/org/apache/pulsar/io/kinesis/fbs/EncryptionKey.java
src/main/java/org/apache/pulsar/io/kinesis/fbs/KeyValue.java
src/main/java/org/apache/pulsar/io/kinesis/fbs/Message.java
-
src/main/java/org/apache/bookkeeper/mledger/util/AbstractCASReferenceCounted.java
-
dependency-reduced-pom.xml
-
pulsar-client-go/go.mod
pulsar-client-go/go.sum
@@ -1776,14 +1631,11 @@ flexible messaging model and an intuitive client API.
pulsar-function-go/go.sum
pulsar-function-go/examples/go.mod
pulsar-function-go/examples/go.sum
-
**/META-INF/services/com.scurrilous.circe.HashProvider
-
**/django/stats/migrations/*.py
**/conf/uwsgi_params
-
**/*.crt
**/*.key
@@ -1798,21 +1650,16 @@ flexible messaging model and an intuitive client API.
certificate-authority/index.txt
certificate-authority/serial
certificate-authority/README.md
-
**/zk-3.5-test-data/*
-
**/requirements.txt
-
conf/schema_example.json
**/templates/*.tpl
-
**/.helmignore
**/_helpers.tpl
-
**/*.md
.github/**
@@ -1841,6 +1688,7 @@ flexible messaging model and an intuitive client API.
**/*.so.*
**/*.dylib
src/test/resources/*.txt
+ **/dependency-reduced-pom.xml
@@ -1849,27 +1697,78 @@ flexible messaging model and an intuitive client API.
maven-enforcer-plugin
${maven-enforcer-plugin.version}
-
- enforce-maven
-
- enforce
-
-
-
-
- 17
- Java 17+ is required to build Pulsar.
-
-
- 3.6.1
-
-
-
-
+
+ enforce-maven
+
+ enforce
+
+
+
+
+ 17
+ Java 17+ is required to build Pulsar.
+
+
+ 3.6.1
+
+
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.13
+ true
+
+ ossrh
+ https://s01.oss.sonatype.org/
+ 60285aee9d4161
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.2.0
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ attach-javadocs
+
+ jar
+
+
+ none
+ false
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 3.0.0-M1
+
+ true
+ false
+ release
+ deploy
+
+
-
@@ -1890,10 +1789,10 @@ flexible messaging model and an intuitive client API.
maven-surefire-plugin
- ${include}
+ ${include}
- **/*$*,${exclude}
+ **/*$*,${exclude}
${groups}
${excludedGroups}
@@ -1977,13 +1876,13 @@ flexible messaging model and an intuitive client API.
com.github.spotbugs
spotbugs-maven-plugin
${spotbugs-maven-plugin.version}
-
-
- com.github.spotbugs
- spotbugs
- ${spotbugs.version}
-
-
+
+
+ com.github.spotbugs
+ spotbugs
+ ${spotbugs.version}
+
+
org.codehaus.mojo
@@ -1995,6 +1894,16 @@ flexible messaging model and an intuitive client API.
docker-maven-plugin
${docker-maven.version}
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.13
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.2.0
+
@@ -2010,7 +1919,6 @@ flexible messaging model and an intuitive client API.
-
@@ -2119,7 +2027,6 @@ flexible messaging model and an intuitive client API.
tests
-
contrib-check
-
-
-
- org.apache.rat
- apache-rat-plugin
-
-
-
- check
-
- verify
-
-
-
-
- org.apache.maven.plugins
- maven-checkstyle-plugin
-
-
- check-style
- verify
-
- ${pulsar.basedir}/buildtools/src/main/resources/pulsar/checkstyle.xml
- ${pulsar.basedir}/buildtools/src/main/resources/pulsar/suppressions.xml
- UTF-8
- **/proto/*
-
-
- check
-
-
-
-
-
-
+
+
+
+ org.apache.rat
+ apache-rat-plugin
+
+
+
+ check
+
+ verify
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+
+ check-style
+ verify
+
+ ${pulsar.basedir}/buildtools/src/main/resources/pulsar/checkstyle.xml
+ ${pulsar.basedir}/buildtools/src/main/resources/pulsar/suppressions.xml
+ UTF-8
+ **/proto/*
+
+
+ check
+
+
+
+
+
+
-
windows
@@ -2175,7 +2081,6 @@ flexible messaging model and an intuitive client API.
rename-netty-native-libs.cmd
-
@@ -2195,6 +2100,7 @@ flexible messaging model and an intuitive client API.
managed-ledger
tiered-storage
pulsar-common
+ pulsar-bom
pulsar-broker-common
pulsar-broker
pulsar-cli-utils
@@ -2220,39 +2126,28 @@ flexible messaging model and an intuitive client API.
pulsar-broker-auth-sasl
pulsar-client-auth-sasl
pulsar-config-validation
-
structured-event-log
-
pulsar-transaction
-
pulsar-functions
-
pulsar-io
-
bouncy-castle
-
pulsar-client-messagecrypto-bc
-
pulsar-metadata
jclouds-shaded
-
pulsar-package-management
-
distribution
docker
tests
-
microbench
-
core-modules
@@ -2264,6 +2159,7 @@ flexible messaging model and an intuitive client API.
testmocks
managed-ledger
pulsar-common
+ pulsar-bom
pulsar-broker-common
pulsar-broker
pulsar-cli-utils
@@ -2283,31 +2179,23 @@ flexible messaging model and an intuitive client API.
pulsar-broker-auth-sasl
pulsar-client-auth-sasl
pulsar-config-validation
-
pulsar-transaction
-
pulsar-functions
-
pulsar-io
-
bouncy-castle
-
pulsar-client-messagecrypto-bc
-
distribution
pulsar-metadata
-
pulsar-package-management
-
+
+ 4.0.0
+ pom
+
+ org.apache
+ apache
+ 29
+
+
+ io.streamnative
+ pulsar-bom
+ 3.2.0
+ Pulsar BOM
+ Pulsar (Bill of Materials)
+ https://github.com/apache/pulsar
+
+ Apache Software Foundation
+ https://www.apache.org/
+
+ 2017
+
+
+ Apache Pulsar developers
+ https://pulsar.apache.org/
+
+
+
+
+ Apache License, Version 2.0
+ https://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+ https://github.com/apache/pulsar
+ scm:git:https://github.com/apache/pulsar.git
+ scm:git:ssh://git@github.com:apache/pulsar.git
+
+
+ GitHub Actions
+ https://github.com/apache/pulsar/actions
+
+
+ Github
+ https://github.com/apache/pulsar/issues
+
+
+ 17
+ 17
+ UTF-8
+ UTF-8
+ 2024-03-04T21:08:59Z
+ 4.1
+ 3.1.2
+ 3.5.3
+
+
+
+
+ com.mycila
+ license-maven-plugin
+ ${license-maven-plugin.version}
+
+
+
+ ../src/license-header.txt
+
+
+
+
+
+ org.apache.rat
+ apache-rat-plugin
+
+
+
+ dependency-reduced-pom.xml
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ ${maven-checkstyle-plugin.version}
+
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.0
+
+
+ default-jar
+ package
+
+ jar
+
+
+
+ javadoc-jar
+ package
+
+ jar
+
+
+ javadoc
+
+
+
+
+
+
+
+ org.apache.maven.wagon
+ wagon-ssh-external
+ ${wagon-ssh-external.version}
+
+
+
+
+
+
+
+
+
+ io.streamnative
+ bouncy-castle-bc
+ ${project.version}
+
+
+ io.streamnative
+ bouncy-castle-bcfips
+ ${project.version}
+
+
+ io.streamnative
+ bouncy-castle-parent
+ ${project.version}
+
+
+ io.streamnative
+ buildtools
+ ${project.version}
+
+
+ io.streamnative
+ distribution
+ ${project.version}
+
+
+ io.streamnative
+ docker-images
+ ${project.version}
+
+
+ io.streamnative
+ jclouds-shaded
+ ${project.version}
+
+
+ io.streamnative
+ managed-ledger
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-all-docker-image
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-broker-auth-athenz
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-broker-auth-oidc
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-broker-auth-sasl
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-broker-common
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-broker
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-cli-utils
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client-1x-base
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client-1x
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client-2x-shaded
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client-admin-api
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client-admin-original
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client-admin
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client-all
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client-api
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client-auth-athenz
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client-auth-sasl
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client-messagecrypto-bc
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client-original
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client-tools-api
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client-tools
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-client
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-common
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-config-validation
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-docker-image
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-docs-tools
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-functions-api-examples-builtin
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-functions-api-examples
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-functions-api
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-functions-instance
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-functions-local-runner-original
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-functions-local-runner
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-functions-proto
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-functions-runtime-all
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-functions-runtime
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-functions-secrets
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-functions-utils
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-functions-worker
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-functions
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-aerospike
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-alluxio
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-aws
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-batch-data-generator
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-batch-discovery-triggerers
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-canal
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-cassandra
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-common
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-core
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-data-generator
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-debezium-core
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-debezium-mongodb
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-debezium-mssql
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-debezium-mysql
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-debezium-oracle
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-debezium-postgres
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-debezium
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-distribution
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-docs
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-dynamodb
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-elastic-search
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-file
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-flume
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-hbase
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-hdfs2
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-hdfs3
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-http
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-influxdb
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-jdbc-clickhouse
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-jdbc-core
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-jdbc-mariadb
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-jdbc-openmldb
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-jdbc-postgres
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-jdbc-sqlite
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-jdbc
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-kafka-connect-adaptor-nar
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-kafka-connect-adaptor
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-kafka
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-kinesis
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-mongo
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-netty
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-nsq
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-rabbitmq
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-redis
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-solr
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io-twitter
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-io
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-metadata
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-offloader-distribution
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-package-bookkeeper-storage
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-package-core
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-package-filesystem-storage
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-package-management
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-proxy
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-server-distribution
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-shell-distribution
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-testclient
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-transaction-common
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-transaction-coordinator
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-transaction-parent
+ ${project.version}
+
+
+ io.streamnative
+ pulsar-websocket
+ ${project.version}
+
+
+ io.streamnative
+ pulsar
+ ${project.version}
+
+
+ io.streamnative
+ structured-event-log
+ ${project.version}
+
+
+ io.streamnative
+ testmocks
+ ${project.version}
+
+
+ io.streamnative
+ tiered-storage-file-system
+ ${project.version}
+
+
+ io.streamnative
+ tiered-storage-jcloud
+ ${project.version}
+
+
+ io.streamnative
+ tiered-storage-parent
+ ${project.version}
+
+
+
+
+
+ ossrh
+ https://s01.oss.sonatype.org/content/repositories/snapshots
+
+
+ ossrh
+ https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
+
diff --git a/pulsar-broker-auth-athenz/pom.xml b/pulsar-broker-auth-athenz/pom.xml
index 6d1c14444ee9e..8a851f1257b1d 100644
--- a/pulsar-broker-auth-athenz/pom.xml
+++ b/pulsar-broker-auth-athenz/pom.xml
@@ -1,4 +1,4 @@
-
+
-
+
4.0.0
- org.apache.pulsar
+ io.streamnative
pulsar
3.2.0-SNAPSHOT
-
pulsar-broker-auth-athenz
jar
Athenz authentication plugin for broker
-
-
${project.groupId}
pulsar-broker
${project.version}
-
${project.groupId}
testmocks
${project.version}
test
-
com.yahoo.athenz
athenz-zpe-java-client
-
org.bouncycastle
bcpkix-jdk18on
-
-
@@ -79,7 +69,6 @@
-
com.github.spotbugs
spotbugs-maven-plugin
@@ -97,7 +86,6 @@
-
org.apache.maven.plugins
maven-checkstyle-plugin
@@ -111,7 +99,6 @@
-
diff --git a/pulsar-broker-auth-athenz/src/test/resources/findbugsExclude.xml b/pulsar-broker-auth-athenz/src/test/resources/findbugsExclude.xml
index ddde8120ba518..47c3d73af5f06 100644
--- a/pulsar-broker-auth-athenz/src/test/resources/findbugsExclude.xml
+++ b/pulsar-broker-auth-athenz/src/test/resources/findbugsExclude.xml
@@ -1,3 +1,4 @@
+
-
\ No newline at end of file
+
diff --git a/pulsar-broker-auth-oidc/pom.xml b/pulsar-broker-auth-oidc/pom.xml
index 1bc1abc579a38..74b59787c552c 100644
--- a/pulsar-broker-auth-oidc/pom.xml
+++ b/pulsar-broker-auth-oidc/pom.xml
@@ -1,4 +1,4 @@
-
+
-
+
4.0.0
- org.apache.pulsar
+ io.streamnative
pulsar
3.2.0-SNAPSHOT
-
pulsar-broker-auth-oidc
jar
Open ID Connect authentication plugin for broker
-
0.11.5
-
-
${project.groupId}
pulsar-broker-common
@@ -50,29 +44,24 @@
-
com.auth0
java-jwt
4.3.0
-
com.auth0
jwks-rsa
0.22.0
-
com.github.ben-manes.caffeine
caffeine
-
org.asynchttpclient
async-http-client
-
io.kubernetes
client-java
@@ -97,7 +86,6 @@
-
io.jsonwebtoken
jjwt-api
@@ -110,16 +98,13 @@
${jsonwebtoken.version}
test
-
com.github.tomakehurst
wiremock-jre8
${wiremock.version}
test
-
-
@@ -141,8 +126,6 @@
-
-
@@ -162,7 +145,6 @@
-
org.apache.maven.plugins
maven-checkstyle-plugin
diff --git a/pulsar-broker-auth-sasl/pom.xml b/pulsar-broker-auth-sasl/pom.xml
index dc8c3dfafa43c..47833dc38095c 100644
--- a/pulsar-broker-auth-sasl/pom.xml
+++ b/pulsar-broker-auth-sasl/pom.xml
@@ -1,4 +1,4 @@
-
+
-
+
4.0.0
- org.apache.pulsar
+ io.streamnative
pulsar
3.2.0-SNAPSHOT
-
pulsar-broker-auth-sasl
jar
SASL authentication plugin for broker
-
-
${project.groupId}
pulsar-broker
${project.version}
-
org.apache.kerby
kerby-config
@@ -53,7 +47,6 @@
-
org.apache.kerby
kerb-simplekdc
@@ -66,30 +59,25 @@
-
${project.groupId}
pulsar-proxy
${project.version}
test
-
${project.groupId}
testmocks
${project.version}
test
-
${project.groupId}
pulsar-client-auth-sasl
${project.version}
test
-
-
@@ -111,8 +99,6 @@
-
-
@@ -132,7 +118,6 @@
-
org.apache.maven.plugins
maven-checkstyle-plugin
diff --git a/pulsar-broker-common/pom.xml b/pulsar-broker-common/pom.xml
index bf41c63f5f67d..c2c1ceb56f366 100644
--- a/pulsar-broker-common/pom.xml
+++ b/pulsar-broker-common/pom.xml
@@ -1,4 +1,4 @@
-
+
-
+
4.0.0
- org.apache.pulsar
+ io.streamnative
pulsar
3.2.0-SNAPSHOT
-
pulsar-broker-common
Common classes used in multiple broker modules
-
${project.groupId}
pulsar-metadata
${project.version}
-
com.google.guava
guava
-
io.prometheus
simpleclient_jetty
-
javax.servlet
javax.servlet-api
-
javax.ws.rs
javax.ws.rs-api
-
io.jsonwebtoken
jjwt-impl
-
io.jsonwebtoken
jjwt-jackson
-
org.bouncycastle
@@ -76,14 +65,12 @@
${bouncycastle.bc-fips.version}
test
-
org.awaitility
awaitility
test
-
@@ -103,7 +90,6 @@
-
org.apache.maven.plugins
maven-checkstyle-plugin
@@ -117,7 +103,6 @@
-
maven-resources-plugin
diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/resources/TopicResources.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/resources/TopicResources.java
index 840ced0a1c1c4..0963f25c3d31f 100644
--- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/resources/TopicResources.java
+++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/resources/TopicResources.java
@@ -50,6 +50,9 @@ public TopicResources(MetadataStore store) {
store.registerListener(this::handleNotification);
}
+ /***
+ * List persistent topics names under a namespace, the topic name contains the partition suffix.
+ */
public CompletableFuture> listPersistentTopicsAsync(NamespaceName ns) {
String path = MANAGED_LEDGER_PATH + "/" + ns + "/persistent";
diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/web/plugin/servlet/AdditionalServlets.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/web/plugin/servlet/AdditionalServlets.java
index 0046d28afa444..f6fc42ff0fccf 100644
--- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/web/plugin/servlet/AdditionalServlets.java
+++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/web/plugin/servlet/AdditionalServlets.java
@@ -79,12 +79,16 @@ public static AdditionalServlets load(PulsarConfiguration conf) throws IOExcepti
return null;
}
+ String[] additionalServletsList = additionalServlets.split(",");
+ if (additionalServletsList.length == 0) {
+ return null;
+ }
+
AdditionalServletDefinitions definitions =
AdditionalServletUtils.searchForServlets(additionalServletDirectory
, narExtractionDirectory);
ImmutableMap.Builder builder = ImmutableMap.builder();
- String[] additionalServletsList = additionalServlets.split(",");
for (String servletName : additionalServletsList) {
AdditionalServletMetadata definition = definitions.servlets().get(servletName);
if (null == definition) {
@@ -106,7 +110,7 @@ public static AdditionalServlets load(PulsarConfiguration conf) throws IOExcepti
}
Map servlets = builder.build();
- if (servlets != null && !servlets.isEmpty()) {
+ if (!servlets.isEmpty()) {
return new AdditionalServlets(servlets);
}
diff --git a/pulsar-broker/pom.xml b/pulsar-broker/pom.xml
index 6aac8959a1065..c2f808abf7f2b 100644
--- a/pulsar-broker/pom.xml
+++ b/pulsar-broker/pom.xml
@@ -1,3 +1,4 @@
+
-
+
4.0.0
-
- org.apache.pulsar
+ io.streamnative
pulsar
3.2.0-SNAPSHOT
..
-
pulsar-broker
jar
Pulsar Broker
-
commons-codec
commons-codec
-
org.apache.commons
commons-collections4
-
org.apache.commons
commons-lang3
-
org.slf4j
slf4j-api
-
io.netty
netty-transport
-
com.google.protobuf
protobuf-java
-
${project.groupId}
pulsar-client-original
${project.version}
-
${project.groupId}
pulsar-websocket
${project.version}
-
${project.groupId}
pulsar-client-admin-original
${project.version}
-
${project.groupId}
pulsar-cli-utils
${project.version}
-
${project.groupId}
managed-ledger
${project.version}
-
org.apache.curator
curator-recipes
-
org.apache.bookkeeper
stream-storage-server
@@ -125,170 +110,139 @@
-
org.apache.bookkeeper
bookkeeper-tools-framework
-
${project.groupId}
pulsar-broker-common
${project.version}
-
${project.groupId}
pulsar-transaction-common
${project.version}
-
${project.groupId}
pulsar-io-batch-discovery-triggerers
${project.version}
test
-
${project.groupId}
testmocks
${project.version}
test
-
com.github.tomakehurst
wiremock-jre8
${wiremock.version}
test
-
- io.dropwizard.metrics
- metrics-core
+ io.dropwizard.metrics
+ metrics-core
-
- org.xerial.snappy
- snappy-java
+ org.xerial.snappy
+ snappy-java
-
-
${project.groupId}
pulsar-functions-worker
${project.version}
-
${project.groupId}
pulsar-functions-local-runner-original
${project.version}
test
-
${project.groupId}
pulsar-client-messagecrypto-bc
${project.version}
-
org.awaitility
awaitility
test
-
-
org.eclipse.jetty
jetty-server
-
org.eclipse.jetty
jetty-alpn-conscrypt-server
-
org.eclipse.jetty
jetty-servlet
-
org.eclipse.jetty
jetty-servlets
-
org.glassfish.jersey.core
jersey-server
-
org.glassfish.jersey.containers
jersey-container-servlet-core
-
org.glassfish.jersey.containers
jersey-container-servlet
-
org.glassfish.jersey.media
jersey-media-json-jackson
-
org.glassfish.jersey.test-framework
jersey-test-framework-core
test
${jersey.version}
-
org.glassfish.jersey.test-framework.providers
jersey-test-framework-provider-grizzly2
test
${jersey.version}
-
jakarta.activation
jakarta.activation-api
-
com.fasterxml.jackson.jaxrs
jackson-jaxrs-json-provider
-
org.glassfish.jersey.inject
jersey-hk2
-
com.fasterxml.jackson.module
jackson-module-jsonSchema
-
org.slf4j
jcl-over-slf4j
-
com.google.guava
guava
-
${project.groupId}
pulsar-docs-tools
@@ -300,80 +254,65 @@
-
com.beust
jcommander
-
io.swagger
swagger-annotations
provided
-
io.prometheus
simpleclient
-
io.prometheus
simpleclient_jetty
-
io.prometheus
simpleclient_hotspot
-
io.prometheus
simpleclient_caffeine
-
io.swagger
swagger-core
provided
-
org.hdrhistogram
HdrHistogram
-
com.google.code.gson
gson
-
com.github.zafarkhaja
java-semver
-
org.apache.avro
avro
${avro.version}
-
com.carrotsearch
hppc
-
org.roaringbitmap
RoaringBitmap
-
com.github.oshi
oshi-core-java11
-
${project.groupId}
pulsar-functions-api-examples
@@ -381,7 +320,6 @@
pom
test
-
${project.groupId}
pulsar-functions-api-examples-builtin
@@ -389,7 +327,6 @@
pom
test
-
${project.groupId}
pulsar-io-batch-data-generator
@@ -397,7 +334,6 @@
pom
test
-
${project.groupId}
pulsar-io-data-generator
@@ -405,7 +341,6 @@
pom
test
-
${project.groupId}
pulsar-metadata
@@ -413,7 +348,6 @@
test-jar
test
-
javax.xml.bind
jaxb-api
@@ -424,42 +358,33 @@
-
com.sun.activation
javax.activation
-
-
${project.groupId}
pulsar-transaction-coordinator
${project.version}
-
-
${project.groupId}
pulsar-package-core
${project.version}
-
io.etcd
jetcd-test
test
-
${project.groupId}
pulsar-package-filesystem-storage
${project.version}
-
-
@@ -482,7 +407,6 @@
-
org.apache.maven.plugins
maven-checkstyle-plugin
@@ -513,7 +437,6 @@
-
maven-dependency-plugin
@@ -566,7 +489,6 @@
-
org.apache.maven.plugins
maven-surefire-plugin
@@ -582,7 +504,6 @@
-
org.xolstice.maven.plugins
protobuf-maven-plugin
@@ -652,7 +573,6 @@
-
maven-resources-plugin
@@ -683,7 +603,6 @@
-
@@ -702,7 +621,6 @@
test-jar
test
-
${project.groupId}
pulsar-package-core
@@ -710,7 +628,6 @@
test-jar
test
-
${project.groupId}
pulsar-metadata
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java
index a04a4c137ccbc..83a91e6971e83 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java
@@ -275,6 +275,7 @@ public class PulsarService implements AutoCloseable, ShutdownService {
private TransactionPendingAckStoreProvider transactionPendingAckStoreProvider;
private final ExecutorProvider transactionExecutorProvider;
private final DefaultMonotonicSnapshotClock monotonicSnapshotClock;
+ private String brokerId;
public enum State {
Init, Started, Closing, Closed
@@ -307,6 +308,7 @@ public PulsarService(ServiceConfiguration config,
// Validate correctness of configuration
PulsarConfigurationLoader.isComplete(config);
TransactionBatchedWriteValidator.validate(config);
+ this.config = config;
// validate `advertisedAddress`, `advertisedListeners`, `internalListenerName`
this.advertisedListeners = MultipleListenerValidator.validateAndAnalysisAdvertisedListener(config);
@@ -317,7 +319,6 @@ public PulsarService(ServiceConfiguration config,
// use `internalListenerName` listener as `advertisedAddress`
this.bindAddress = ServiceConfigurationUtils.getDefaultOrConfiguredAddress(config.getBindAddress());
this.brokerVersion = PulsarVersion.getVersion();
- this.config = config;
this.processTerminator = processTerminator;
this.loadManagerExecutor = Executors
.newSingleThreadScheduledExecutor(new ExecutorProvider.ExtendedThreadFactory("pulsar-load-manager"));
@@ -828,6 +829,12 @@ public void start() throws PulsarServerException {
this.brokerServiceUrl = brokerUrl(config);
this.brokerServiceUrlTls = brokerUrlTls(config);
+ // the broker id is used in the load manager to identify the broker
+ // it should not be used for making connections to the broker
+ this.brokerId =
+ String.format("%s:%s", advertisedAddress, config.getWebServicePort()
+ .or(config::getWebServicePortTls).orElseThrow());
+
if (this.compactionServiceFactory == null) {
this.compactionServiceFactory = loadCompactionServiceFactory();
}
@@ -1099,7 +1106,7 @@ private void addWebSocketServiceHandler(WebService webService,
}
private void handleDeleteCluster(Notification notification) {
- if (ClusterResources.pathRepresentsClusterName(notification.getPath())
+ if (isRunning() && ClusterResources.pathRepresentsClusterName(notification.getPath())
&& notification.getType() == NotificationType.Deleted) {
final String clusterName = ClusterResources.clusterNameFromPath(notification.getPath());
getBrokerService().closeAndRemoveReplicationClient(clusterName);
@@ -1137,7 +1144,8 @@ protected void startLeaderElectionService() {
LOG.info("The load manager extension is enabled. Skipping PulsarService LeaderElectionService.");
return;
}
- this.leaderElectionService = new LeaderElectionService(coordinationService, getSafeWebServiceAddress(),
+ this.leaderElectionService =
+ new LeaderElectionService(coordinationService, getBrokerId(), getSafeWebServiceAddress(),
state -> {
if (state == LeaderElectionState.Leading) {
LOG.info("This broker was elected leader");
@@ -1185,7 +1193,7 @@ protected void startLeaderElectionService() {
protected void acquireSLANamespace() {
try {
// Namespace not created hence no need to unload it
- NamespaceName nsName = NamespaceService.getSLAMonitorNamespace(getLookupServiceAddress(), config);
+ NamespaceName nsName = NamespaceService.getSLAMonitorNamespace(getBrokerId(), config);
if (!this.pulsarResources.getNamespaceResources().namespaceExists(nsName)) {
LOG.info("SLA Namespace = {} doesn't exist.", nsName);
return;
@@ -1694,10 +1702,16 @@ public String getSafeBrokerServiceUrl() {
return brokerServiceUrlTls != null ? brokerServiceUrlTls : brokerServiceUrl;
}
- public String getLookupServiceAddress() {
- return String.format("%s:%s", advertisedAddress, config.getWebServicePortTls().isPresent()
- ? config.getWebServicePortTls().get()
- : config.getWebServicePort().orElseThrow());
+ /**
+ * Return the broker id. The broker id is used in the load manager to uniquely identify the broker at runtime.
+ * It should not be used for making connections to the broker. The broker id is available after {@link #start()}
+ * has been called.
+ *
+ * @return broker id
+ */
+ public String getBrokerId() {
+ return Objects.requireNonNull(brokerId,
+ "brokerId is not initialized before start has been called");
}
public synchronized void addPrometheusRawMetricsProvider(PrometheusRawMetricsProvider metricsProvider) {
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/AdminResource.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/AdminResource.java
index 1526ae18a9057..2ceec18997538 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/AdminResource.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/AdminResource.java
@@ -834,6 +834,10 @@ protected static boolean isNotFoundException(Throwable ex) {
== Status.NOT_FOUND.getStatusCode();
}
+ protected static boolean isNot307And404Exception(Throwable ex) {
+ return !isRedirectException(ex) && !isNotFoundException(ex);
+ }
+
protected static String getTopicNotFoundErrorMessage(String topic) {
return String.format("Topic %s not found", topic);
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/BrokersBase.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/BrokersBase.java
index 3fb1941b33af5..61b354610ac20 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/BrokersBase.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/BrokersBase.java
@@ -26,6 +26,7 @@
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -34,6 +35,7 @@
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
@@ -80,12 +82,18 @@ public class BrokersBase extends AdminResource {
// log a full thread dump when a deadlock is detected in healthcheck once every 10 minutes
// to prevent excessive logging
private static final long LOG_THREADDUMP_INTERVAL_WHEN_DEADLOCK_DETECTED = 600000L;
+ // there is a timeout of 60 seconds default in the client(readTimeoutMs), so we need to set the timeout
+ // a bit shorter than 60 seconds to avoid the client timeout exception thrown before the server timeout exception.
+ // or we can't propagate the server timeout exception to the client.
+ private static final Duration HEALTH_CHECK_READ_TIMEOUT = Duration.ofSeconds(58);
+ private static final TimeoutException HEALTH_CHECK_TIMEOUT_EXCEPTION =
+ FutureUtil.createTimeoutException("Timeout", BrokersBase.class, "healthCheckRecursiveReadNext(...)");
private volatile long threadDumpLoggedTimestamp;
@GET
@Path("/{cluster}")
@ApiOperation(
- value = "Get the list of active brokers (web service addresses) in the cluster."
+ value = "Get the list of active brokers (broker ids) in the cluster."
+ "If authorization is not enabled, any cluster name is valid.",
response = String.class,
responseContainer = "Set")
@@ -115,7 +123,7 @@ public void getActiveBrokers(@Suspended final AsyncResponse asyncResponse,
@GET
@ApiOperation(
- value = "Get the list of active brokers (web service addresses) in the local cluster."
+ value = "Get the list of active brokers (broker ids) in the local cluster."
+ "If authorization is not enabled",
response = String.class,
responseContainer = "Set")
@@ -141,7 +149,9 @@ public void getLeaderBroker(@Suspended final AsyncResponse asyncResponse) {
validateSuperUserAccessAsync().thenAccept(__ -> {
LeaderBroker leaderBroker = pulsar().getLeaderElectionService().getCurrentLeader()
.orElseThrow(() -> new RestException(Status.NOT_FOUND, "Couldn't find leader broker"));
- BrokerInfo brokerInfo = BrokerInfo.builder().serviceUrl(leaderBroker.getServiceUrl()).build();
+ BrokerInfo brokerInfo = BrokerInfo.builder()
+ .serviceUrl(leaderBroker.getServiceUrl())
+ .brokerId(leaderBroker.getBrokerId()).build();
LOG.info("[{}] Successfully to get the information of the leader broker.", clientAppId());
asyncResponse.resume(brokerInfo);
})
@@ -153,8 +163,8 @@ public void getLeaderBroker(@Suspended final AsyncResponse asyncResponse) {
}
@GET
- @Path("/{clusterName}/{broker-webserviceurl}/ownedNamespaces")
- @ApiOperation(value = "Get the list of namespaces served by the specific broker",
+ @Path("/{clusterName}/{brokerId}/ownedNamespaces")
+ @ApiOperation(value = "Get the list of namespaces served by the specific broker id",
response = NamespaceOwnershipStatus.class, responseContainer = "Map")
@ApiResponses(value = {
@ApiResponse(code = 307, message = "Current broker doesn't serve the cluster"),
@@ -162,9 +172,9 @@ public void getLeaderBroker(@Suspended final AsyncResponse asyncResponse) {
@ApiResponse(code = 404, message = "Cluster doesn't exist") })
public void getOwnedNamespaces(@Suspended final AsyncResponse asyncResponse,
@PathParam("clusterName") String cluster,
- @PathParam("broker-webserviceurl") String broker) {
+ @PathParam("brokerId") String brokerId) {
validateSuperUserAccessAsync()
- .thenAccept(__ -> validateBrokerName(broker))
+ .thenCompose(__ -> maybeRedirectToBroker(brokerId))
.thenCompose(__ -> validateClusterOwnershipAsync(cluster))
.thenCompose(__ -> pulsar().getNamespaceService().getOwnedNameSpacesStatusAsync())
.thenAccept(asyncResponse::resume)
@@ -172,7 +182,7 @@ public void getOwnedNamespaces(@Suspended final AsyncResponse asyncResponse,
// If the exception is not redirect exception we need to log it.
if (!isRedirectException(ex)) {
LOG.error("[{}] Failed to get the namespace ownership status. cluster={}, broker={}",
- clientAppId(), cluster, broker);
+ clientAppId(), cluster, brokerId);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
return null;
@@ -396,10 +406,10 @@ private void checkDeadlockedThreads() {
private CompletableFuture internalRunHealthCheck(TopicVersion topicVersion) {
- String lookupServiceAddress = pulsar().getLookupServiceAddress();
+ String brokerId = pulsar().getBrokerId();
NamespaceName namespaceName = (topicVersion == TopicVersion.V2)
- ? NamespaceService.getHeartbeatNamespaceV2(lookupServiceAddress, pulsar().getConfiguration())
- : NamespaceService.getHeartbeatNamespace(lookupServiceAddress, pulsar().getConfiguration());
+ ? NamespaceService.getHeartbeatNamespaceV2(brokerId, pulsar().getConfiguration())
+ : NamespaceService.getHeartbeatNamespace(brokerId, pulsar().getConfiguration());
final String topicName = String.format("persistent://%s/%s", namespaceName, HEALTH_CHECK_TOPIC_SUFFIX);
LOG.info("[{}] Running healthCheck with topic={}", clientAppId(), topicName);
final String messageStr = UUID.randomUUID().toString();
@@ -432,7 +442,10 @@ private CompletableFuture internalRunHealthCheck(TopicVersion topicVersion
});
throw FutureUtil.wrapToCompletionException(createException);
}).thenCompose(reader -> producer.sendAsync(messageStr)
- .thenCompose(__ -> healthCheckRecursiveReadNext(reader, messageStr))
+ .thenCompose(__ -> FutureUtil.addTimeoutHandling(
+ healthCheckRecursiveReadNext(reader, messageStr),
+ HEALTH_CHECK_READ_TIMEOUT, pulsar().getBrokerService().executor(),
+ () -> HEALTH_CHECK_TIMEOUT_EXCEPTION))
.whenComplete((__, ex) -> {
closeAndReCheck(producer, reader, topicOptional.get(), subscriptionName)
.whenComplete((unused, innerEx) -> {
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java
index caaff010439d9..f274cffa46baf 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java
@@ -923,9 +923,9 @@ private CompletableFuture validateLeaderBrokerAsync() {
return FutureUtil.failedFuture(new RestException(Response.Status.PRECONDITION_FAILED, errorStr));
}
LeaderBroker leaderBroker = pulsar().getLeaderElectionService().getCurrentLeader().get();
- String leaderBrokerUrl = leaderBroker.getServiceUrl();
+ String leaderBrokerId = leaderBroker.getBrokerId();
return pulsar().getNamespaceService()
- .createLookupResult(leaderBrokerUrl, false, null)
+ .createLookupResult(leaderBrokerId, false, null)
.thenCompose(lookupResult -> {
String redirectUrl = isRequestHttps() ? lookupResult.getLookupData().getHttpUrlTls()
: lookupResult.getLookupData().getHttpUrl();
@@ -948,7 +948,7 @@ private CompletableFuture validateLeaderBrokerAsync() {
return FutureUtil.failedFuture((
new WebApplicationException(Response.temporaryRedirect(redirect).build())));
} catch (MalformedURLException exception) {
- log.error("The leader broker url is malformed - {}", leaderBrokerUrl);
+ log.error("The redirect url is malformed - {}", redirectUrl);
return FutureUtil.failedFuture(new RestException(exception));
}
});
@@ -984,8 +984,11 @@ public CompletableFuture setNamespaceBundleAffinityAsync(String bundleRang
}
public CompletableFuture internalUnloadNamespaceBundleAsync(String bundleRange,
- String destinationBroker,
+ String destinationBrokerParam,
boolean authoritative) {
+ String destinationBroker = StringUtils.isBlank(destinationBrokerParam) ? null :
+ // ensure backward compatibility: strip the possible http:// or https:// prefix
+ destinationBrokerParam.replaceFirst("http[s]?://", "");
return validateSuperUserAccessAsync()
.thenCompose(__ -> setNamespaceBundleAffinityAsync(bundleRange, destinationBroker))
.thenAccept(__ -> {
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java
index 787ffe397de05..0cdb140c7c3d7 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java
@@ -148,6 +148,7 @@
import org.apache.pulsar.common.util.DateFormatter;
import org.apache.pulsar.common.util.FutureUtil;
import org.apache.pulsar.common.util.collections.BitSetRecyclable;
+import org.apache.pulsar.compaction.Compactor;
import org.apache.pulsar.metadata.api.MetadataStoreException;
import org.apache.pulsar.transaction.coordinator.TransactionCoordinatorID;
import org.slf4j.Logger;
@@ -873,7 +874,7 @@ protected void internalUnloadTopic(AsyncResponse asyncResponse, boolean authorit
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get partitioned metadata while unloading topic {}",
clientAppId(), topicName, ex);
}
@@ -883,7 +884,7 @@ protected void internalUnloadTopic(AsyncResponse asyncResponse, boolean authorit
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to validate the global namespace ownership while unloading topic {}",
clientAppId(), topicName, ex);
}
@@ -1051,7 +1052,7 @@ private void internalUnloadNonPartitionedTopicAsync(AsyncResponse asyncResponse,
}))
.exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to unload topic {}, {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -1073,7 +1074,7 @@ private void internalUnloadTransactionCoordinatorAsync(AsyncResponse asyncRespon
}))
.exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to unload tc {},{}", clientAppId(),
topicName.getPartitionIndex(), ex);
}
@@ -1175,7 +1176,7 @@ protected void internalGetSubscriptions(AsyncResponse asyncResponse, boolean aut
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get partitioned topic metadata while get"
+ " subscriptions for topic {}", clientAppId(), topicName, ex);
}
@@ -1185,7 +1186,7 @@ protected void internalGetSubscriptions(AsyncResponse asyncResponse, boolean aut
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to validate the global namespace/topic ownership while get subscriptions"
+ " for topic {}", clientAppId(), topicName, ex);
}
@@ -1194,7 +1195,7 @@ protected void internalGetSubscriptions(AsyncResponse asyncResponse, boolean aut
})
).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get subscriptions for {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -1233,7 +1234,7 @@ private void internalGetSubscriptionsForNonPartitionedTopic(AsyncResponse asyncR
.thenAccept(topic -> asyncResponse.resume(new ArrayList<>(topic.getSubscriptions().keys())))
.exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get list of subscriptions for {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -1342,7 +1343,7 @@ protected void internalGetManagedLedgerInfo(AsyncResponse asyncResponse, boolean
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get partitioned metadata while get managed info for {}",
clientAppId(), topicName, ex);
}
@@ -1352,7 +1353,7 @@ protected void internalGetManagedLedgerInfo(AsyncResponse asyncResponse, boolean
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to validate the global namespace ownership while get managed info for {}",
clientAppId(), topicName, ex);
}
@@ -1471,7 +1472,7 @@ protected void internalGetPartitionedStats(AsyncResponse asyncResponse, boolean
});
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get partitioned internal stats for {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -1526,7 +1527,7 @@ protected void internalGetPartitionedStatsInternal(AsyncResponse asyncResponse,
});
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get partitioned internal stats for {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -1654,7 +1655,7 @@ private void internalAnalyzeSubscriptionBacklogForNonPartitionedTopic(AsyncRespo
}).exceptionally(ex -> {
Throwable cause = ex.getCause();
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to analyze subscription backlog {} {}",
clientAppId(), topicName, subName, cause);
}
@@ -1681,7 +1682,7 @@ private void internalUpdateSubscriptionPropertiesForNonPartitionedTopic(AsyncRes
}).exceptionally(ex -> {
Throwable cause = ex.getCause();
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to update subscription {} {}", clientAppId(), topicName, subName, cause);
}
asyncResponse.resume(new RestException(cause));
@@ -1710,7 +1711,7 @@ private void internalGetSubscriptionPropertiesForNonPartitionedTopic(AsyncRespon
}).exceptionally(ex -> {
Throwable cause = ex.getCause();
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to update subscription {} {}", clientAppId(), topicName, subName, cause);
}
asyncResponse.resume(new RestException(cause));
@@ -1879,7 +1880,7 @@ protected void internalSkipAllMessages(AsyncResponse asyncResponse, String subNa
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to skip all messages for subscription {} on topic {}",
clientAppId(), subName, topicName, ex);
}
@@ -1923,7 +1924,7 @@ private CompletableFuture internalSkipAllMessagesForNonPartitionedTopicAsy
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to skip all messages for subscription {} on topic {}",
clientAppId(), subName, topicName, ex);
}
@@ -1987,7 +1988,7 @@ protected void internalSkipMessages(AsyncResponse asyncResponse, String subName,
})
).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to skip {} messages {} {}", clientAppId(), numMessages, topicName,
subName, ex);
}
@@ -2057,7 +2058,7 @@ protected void internalExpireMessagesForAllSubscriptions(AsyncResponse asyncResp
)
).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to expire messages for all subscription on topic {}", clientAppId(), topicName,
ex);
}
@@ -2090,10 +2091,9 @@ private void internalExpireMessagesForAllSubscriptionsForNonPartitionedTopic(Asy
final List> futures =
new ArrayList<>((int) topic.getReplicators().size());
List subNames =
- new ArrayList<>((int) topic.getReplicators().size()
- + (int) topic.getSubscriptions().size());
- subNames.addAll(topic.getReplicators().keys());
- subNames.addAll(topic.getSubscriptions().keys());
+ new ArrayList<>((int) topic.getSubscriptions().size());
+ subNames.addAll(topic.getSubscriptions().keys().stream().filter(
+ subName -> !subName.equals(Compactor.COMPACTION_SUBSCRIPTION)).toList());
for (int i = 0; i < subNames.size(); i++) {
try {
futures.add(internalExpireMessagesByTimestampForSinglePartitionAsync(partitionMetadata,
@@ -2125,7 +2125,7 @@ private void internalExpireMessagesForAllSubscriptionsForNonPartitionedTopic(Asy
})
).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to expire messages for all subscription up to {} on {}", clientAppId(),
expireTimeInSeconds, topicName, ex);
}
@@ -2332,7 +2332,7 @@ protected void internalCreateSubscription(AsyncResponse asyncResponse, String su
})).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to create subscription {} on topic {}",
clientAppId(), subscriptionName, topicName, ex);
}
@@ -2342,7 +2342,7 @@ protected void internalCreateSubscription(AsyncResponse asyncResponse, String su
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to create subscription {} on topic {}",
clientAppId(), subscriptionName, topicName, ex);
}
@@ -2473,7 +2473,7 @@ protected void internalUpdateSubscriptionProperties(AsyncResponse asyncResponse,
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to update subscription {} from topic {}",
clientAppId(), subName, topicName, ex);
}
@@ -2513,7 +2513,7 @@ protected void internalAnalyzeSubscriptionBacklog(AsyncResponse asyncResponse, S
})
.exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to analyze back log of subscription {} from topic {}",
clientAppId(), subName, topicName, ex);
}
@@ -2598,7 +2598,7 @@ protected void internalGetSubscriptionProperties(AsyncResponse asyncResponse, St
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to update subscription {} from topic {}",
clientAppId(), subName, topicName, ex);
}
@@ -2684,7 +2684,7 @@ protected void internalResetCursorOnPosition(AsyncResponse asyncResponse, String
});
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.warn("[{}][{}] Failed to reset cursor on subscription {} to position {}",
clientAppId(), topicName, subName, messageId, ex.getCause());
}
@@ -2693,7 +2693,7 @@ protected void internalResetCursorOnPosition(AsyncResponse asyncResponse, String
});
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.warn("[{}][{}] Failed to reset cursor on subscription {} to position {}",
clientAppId(), topicName, subName, messageId, ex.getCause());
}
@@ -2826,6 +2826,9 @@ protected CompletableFuture internalGetMessageById(long ledgerId, long
@Override
public void readEntryFailed(ManagedLedgerException exception,
Object ctx) {
+ if (exception instanceof ManagedLedgerException.LedgerNotExistException) {
+ throw new RestException(Status.NOT_FOUND, "Message id not found");
+ }
throw new RestException(exception);
}
@@ -2882,28 +2885,62 @@ protected CompletableFuture internalGetMessageIdByTimestampAsync(long
throw new RestException(Status.METHOD_NOT_ALLOWED,
"Get message ID by timestamp on a non-persistent topic is not allowed");
}
- ManagedLedger ledger = ((PersistentTopic) topic).getManagedLedger();
- return ledger.asyncFindPosition(entry -> {
+ final PersistentTopic persistentTopic = (PersistentTopic) topic;
+
+ return persistentTopic.getTopicCompactionService().readLastCompactedEntry().thenCompose(lastEntry -> {
+ if (lastEntry == null) {
+ return findMessageIdByPublishTime(timestamp, persistentTopic.getManagedLedger());
+ }
+ MessageMetadata metadata;
+ Position position = lastEntry.getPosition();
try {
- long entryTimestamp = Commands.getEntryTimestamp(entry.getDataBuffer());
- return MessageImpl.isEntryPublishedEarlierThan(entryTimestamp, timestamp);
- } catch (Exception e) {
- log.error("[{}] Error deserializing message for message position find", topicName, e);
+ metadata = Commands.parseMessageMetadata(lastEntry.getDataBuffer());
} finally {
- entry.release();
+ lastEntry.release();
}
- return false;
- }).thenApply(position -> {
- if (position == null) {
- return null;
+ if (timestamp == metadata.getPublishTime()) {
+ return CompletableFuture.completedFuture(new MessageIdImpl(position.getLedgerId(),
+ position.getEntryId(), topicName.getPartitionIndex()));
+ } else if (timestamp < metadata.getPublishTime()) {
+ return persistentTopic.getTopicCompactionService().findEntryByPublishTime(timestamp)
+ .thenApply(compactedEntry -> {
+ try {
+ return new MessageIdImpl(compactedEntry.getLedgerId(),
+ compactedEntry.getEntryId(), topicName.getPartitionIndex());
+ } finally {
+ compactedEntry.release();
+ }
+ });
} else {
- return new MessageIdImpl(position.getLedgerId(), position.getEntryId(),
- topicName.getPartitionIndex());
+ return findMessageIdByPublishTime(timestamp, persistentTopic.getManagedLedger());
}
});
});
}
+ private CompletableFuture findMessageIdByPublishTime(long timestamp, ManagedLedger managedLedger) {
+ return managedLedger.asyncFindPosition(entry -> {
+ try {
+ long entryTimestamp = Commands.getEntryTimestamp(entry.getDataBuffer());
+ return MessageImpl.isEntryPublishedEarlierThan(entryTimestamp, timestamp);
+ } catch (Exception e) {
+ log.error("[{}] Error deserializing message for message position find",
+ topicName,
+ e);
+ } finally {
+ entry.release();
+ }
+ return false;
+ }).thenApply(position -> {
+ if (position == null) {
+ return null;
+ } else {
+ return new MessageIdImpl(position.getLedgerId(), position.getEntryId(),
+ topicName.getPartitionIndex());
+ }
+ });
+ }
+
protected CompletableFuture internalPeekNthMessageAsync(String subName, int messagePosition,
boolean authoritative) {
CompletableFuture ret;
@@ -3292,7 +3329,7 @@ protected void internalGetBacklogSizeByMessageId(AsyncResponse asyncResponse,
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get backlog size for topic {}", clientAppId(),
topicName, ex);
}
@@ -3300,7 +3337,7 @@ protected void internalGetBacklogSizeByMessageId(AsyncResponse asyncResponse,
return null;
})).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to validate global namespace ownership "
+ "to get backlog size for topic {}", clientAppId(), topicName, ex);
}
@@ -3838,7 +3875,7 @@ protected void internalTerminatePartitionedTopic(AsyncResponse asyncResponse, bo
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to terminate topic {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -3846,7 +3883,7 @@ protected void internalTerminatePartitionedTopic(AsyncResponse asyncResponse, bo
})
).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to terminate topic {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -3936,7 +3973,7 @@ protected void internalExpireMessagesByTimestamp(AsyncResponse asyncResponse, St
).exceptionally(ex -> {
Throwable cause = FutureUtil.unwrapCompletionException(ex);
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(cause)) {
+ if (!isNot307And404Exception(cause)) {
if (cause instanceof RestException) {
log.warn("[{}] Failed to expire messages up to {} on {}: {}", clientAppId(), expireTimeInSeconds,
topicName, cause.toString());
@@ -4051,7 +4088,7 @@ protected void internalExpireMessagesByPosition(AsyncResponse asyncResponse, Str
messageId, isExcluded, batchIndex);
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to expire messages up to {} on subscription {} to position {}",
clientAppId(), topicName, subName, messageId, ex);
}
@@ -4201,7 +4238,7 @@ protected void internalTriggerCompaction(AsyncResponse asyncResponse, boolean au
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to trigger compaction on topic {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -4210,7 +4247,7 @@ protected void internalTriggerCompaction(AsyncResponse asyncResponse, boolean au
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to validate global namespace ownership to trigger compaction on topic {}",
clientAppId(), topicName, ex);
}
@@ -4239,7 +4276,7 @@ protected void internalTriggerCompactionNonPartitionedTopic(AsyncResponse asyncR
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to trigger compaction for {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -4275,7 +4312,7 @@ protected void internalTriggerOffload(AsyncResponse asyncResponse,
}
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to trigger offload for {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -4292,7 +4329,7 @@ protected void internalOffloadStatus(AsyncResponse asyncResponse, boolean author
asyncResponse.resume(offloadProcessStatus);
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to offload status on topic {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -4569,7 +4606,7 @@ protected void internalGetLastMessageId(AsyncResponse asyncResponse, boolean aut
});
}).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get last messageId {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -4947,9 +4984,7 @@ protected CompletableFuture internalRemoveSubscribeRate(boolean isGlobal)
protected void handleTopicPolicyException(String methodName, Throwable thr, AsyncResponse asyncResponse) {
Throwable cause = thr.getCause();
- if (!(cause instanceof WebApplicationException) || !(
- ((WebApplicationException) cause).getResponse().getStatus() == 307
- || ((WebApplicationException) cause).getResponse().getStatus() == 404)) {
+ if (isNot307And404Exception(cause)) {
log.error("[{}] Failed to perform {} on topic {}",
clientAppId(), methodName, topicName, cause);
}
@@ -5075,7 +5110,7 @@ protected void internalSetReplicatedSubscriptionStatus(AsyncResponse asyncRespon
resultFuture.exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.warn("[{}] Failed to change replicated subscription status to {} - {} {}", clientAppId(), enabled,
topicName, subName, ex);
}
@@ -5122,7 +5157,7 @@ private void internalSetReplicatedSubscriptionStatusForNonPartitionedTopic(
}
).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to set replicated subscription status on {} {}", clientAppId(),
topicName, subName, ex);
}
@@ -5223,7 +5258,7 @@ protected void internalGetReplicatedSubscriptionStatus(AsyncResponse asyncRespon
}
resultFuture.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get replicated subscription status on {} {}", clientAppId(),
topicName, subName, ex);
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/SchemasResourceBase.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/SchemasResourceBase.java
index 454b8f0fac82c..286366c8b5834 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/SchemasResourceBase.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/SchemasResourceBase.java
@@ -238,7 +238,7 @@ private CompletableFuture validateOwnershipAndOperationAsync(boolean autho
protected boolean shouldPrintErrorLog(Throwable ex) {
- return !isRedirectException(ex) && !isNotFoundException(ex);
+ return isNot307And404Exception(ex);
}
private static final Logger log = LoggerFactory.getLogger(SchemasResourceBase.class);
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/NonPersistentTopics.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/NonPersistentTopics.java
index d4795393f9b03..a22ad4b242f57 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/NonPersistentTopics.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/NonPersistentTopics.java
@@ -132,7 +132,7 @@ public void getInternalStats(
})
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get internal stats for topic {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -479,7 +479,7 @@ public void getListFromBundle(
}
asyncResponse.resume(topicList);
}).exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to list topics on namespace bundle {}/{}", clientAppId(),
namespaceName, bundleRange, ex);
}
@@ -488,7 +488,7 @@ public void getListFromBundle(
});
}
}).exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to list topics on namespace bundle {}/{}", clientAppId(),
namespaceName, bundleRange, ex);
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/PersistentTopics.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/PersistentTopics.java
index 9ccbc0ecba171..9094a4642a0cb 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/PersistentTopics.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/PersistentTopics.java
@@ -119,7 +119,7 @@ public void getList(
internalGetListAsync(Optional.ofNullable(bundle))
.thenAccept(topicList -> asyncResponse.resume(filterSystemTopic(topicList, includeSystemTopic)))
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to get topic list {}", clientAppId(), namespaceName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -150,7 +150,7 @@ public void getPartitionedTopicList(
.thenAccept(partitionedTopicList -> asyncResponse.resume(
filterSystemTopic(partitionedTopicList, includeSystemTopic)))
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to get partitioned topic list {}", clientAppId(), namespaceName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -335,7 +335,7 @@ public void createNonPartitionedTopic(
internalCreateNonPartitionedTopicAsync(authoritative, properties)
.thenAccept(__ -> asyncResponse.resume(Response.noContent().build()))
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to create non-partitioned topic {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -825,7 +825,7 @@ public void updatePartitionedTopic(
asyncResponse.resume(Response.noContent().build());
})
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}][{}] Failed to update partition to {}",
clientAppId(), topicName, numPartitions, ex);
}
@@ -934,7 +934,7 @@ public void getProperties(
internalGetPropertiesAsync(authoritative)
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to get topic {} properties", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -970,7 +970,7 @@ public void updateProperties(
internalUpdatePropertiesAsync(authoritative, properties)
.thenAccept(__ -> asyncResponse.resume(Response.noContent().build()))
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to update topic {} properties", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -1004,7 +1004,7 @@ public void removeProperties(
internalRemovePropertiesAsync(authoritative, key)
.thenAccept(__ -> asyncResponse.resume(Response.noContent().build()))
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to remove key {} in properties on topic {}",
clientAppId(), key, topicName, ex);
}
@@ -1125,7 +1125,7 @@ public void deleteTopic(
} else if (isManagedLedgerNotFoundException(t)) {
ex = new RestException(Response.Status.NOT_FOUND,
getTopicNotFoundErrorMessage(topicName.toString()));
- } else if (!isRedirectException(ex)) {
+ } else if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to delete topic {}", clientAppId(), topicName, t);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -1209,7 +1209,7 @@ public void getStats(
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to get stats for {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -1243,7 +1243,7 @@ public void getInternalStats(
internalGetInternalStatsAsync(authoritative, metadata)
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to get internal stats for topic {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -1892,7 +1892,7 @@ public void peekNthMessage(
internalPeekNthMessageAsync(decode(encodedSubName), messagePosition, authoritative)
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to get peek nth message for topic {} subscription {}", clientAppId(),
topicName, decode(encodedSubName), ex);
}
@@ -1934,7 +1934,7 @@ public void examineMessage(
internalExamineMessageAsync(initialPosition, messagePosition, authoritative)
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to examine a specific message on the topic {}", clientAppId(), topicName,
ex);
}
@@ -1976,7 +1976,7 @@ public void getMessageById(
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to get message with ledgerId {} entryId {} from {}",
clientAppId(), ledgerId, entryId, topicName, ex);
}
@@ -2020,7 +2020,7 @@ public void getMessageIdByTimestamp(
}
})
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to get message ID by timestamp {} from {}",
clientAppId(), timestamp, topicName, ex);
}
@@ -2055,7 +2055,7 @@ public void getBacklog(
log.warn("[{}] Failed to get topic backlog {}: Namespace does not exist", clientAppId(),
namespaceName);
ex = new RestException(Response.Status.NOT_FOUND, "Namespace does not exist");
- } else if (!isRedirectException(ex)) {
+ } else if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to get estimated backlog for topic {}", clientAppId(), encodedTopic, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -2435,7 +2435,8 @@ public void getRetention(@Suspended final AsyncResponse asyncResponse,
@ApiParam(value = "Whether leader broker redirected this call to this broker. For internal use.")
@QueryParam("authoritative") @DefaultValue("false") boolean authoritative) {
validateTopicName(tenant, namespace, encodedTopic);
- preValidation(authoritative)
+ validateTopicPolicyOperationAsync(topicName, PolicyName.RETENTION, PolicyOperation.READ)
+ .thenCompose(__ -> preValidation(authoritative))
.thenCompose(__ -> internalGetRetention(applied, isGlobal))
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
@@ -2462,7 +2463,8 @@ public void setRetention(@Suspended final AsyncResponse asyncResponse,
@QueryParam("isGlobal") @DefaultValue("false") boolean isGlobal,
@ApiParam(value = "Retention policies for the specified topic") RetentionPolicies retention) {
validateTopicName(tenant, namespace, encodedTopic);
- preValidation(authoritative)
+ validateTopicPolicyOperationAsync(topicName, PolicyName.RETENTION, PolicyOperation.WRITE)
+ .thenCompose(__ -> preValidation(authoritative))
.thenCompose(__ -> internalSetRetention(retention, isGlobal))
.thenRun(() -> {
try {
@@ -2498,7 +2500,8 @@ public void removeRetention(@Suspended final AsyncResponse asyncResponse,
@ApiParam(value = "Whether leader broker redirected this call to this broker. For internal use.")
@QueryParam("authoritative") @DefaultValue("false") boolean authoritative) {
validateTopicName(tenant, namespace, encodedTopic);
- preValidation(authoritative)
+ validateTopicPolicyOperationAsync(topicName, PolicyName.RETENTION, PolicyOperation.WRITE)
+ .thenCompose(__ -> preValidation(authoritative))
.thenCompose(__ -> internalRemoveRetention(isGlobal))
.thenRun(() -> {
log.info("[{}] Successfully remove retention: namespace={}, topic={}",
@@ -3092,7 +3095,7 @@ public void terminate(
internalTerminateAsync(authoritative)
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to terminated topic {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
@@ -3188,7 +3191,7 @@ public void compactionStatus(
internalCompactionStatusAsync(authoritative)
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to get the status of a compaction operation for the topic {}",
clientAppId(), topicName, ex);
}
@@ -3327,7 +3330,7 @@ public void trimTopic(
validateTopicName(tenant, namespace, encodedTopic);
internalTrimTopic(asyncResponse, authoritative).exceptionally(ex -> {
// If the exception is not redirect exception we need to log it.
- if (!isRedirectException(ex)) {
+ if (isNot307And404Exception(ex)) {
log.error("[{}] Failed to trim topic {}", clientAppId(), topicName, ex);
}
resumeAsyncResponseExceptionally(asyncResponse, ex);
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v3/Transactions.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v3/Transactions.java
index 1bdc2255085f1..667d8ce581ece 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v3/Transactions.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v3/Transactions.java
@@ -105,7 +105,7 @@ public void getTransactionInBufferStats(@Suspended final AsyncResponse asyncResp
Long.parseLong(leastSigBits))
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get transaction state in transaction buffer {}",
clientAppId(), topicName, ex);
}
@@ -143,7 +143,7 @@ public void getTransactionInPendingAckStats(@Suspended final AsyncResponse async
Long.parseLong(leastSigBits), subName)
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get transaction state in pending ack {}",
clientAppId(), topicName, ex);
}
@@ -181,7 +181,7 @@ public void getTransactionBufferStats(@Suspended final AsyncResponse asyncRespon
internalGetTransactionBufferStats(authoritative, lowWaterMarks, segmentStats)
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get transaction buffer stats in topic {}",
clientAppId(), topicName, ex);
}
@@ -217,7 +217,7 @@ public void getPendingAckStats(@Suspended final AsyncResponse asyncResponse,
internalGetPendingAckStats(authoritative, subName, lowWaterMarks)
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get transaction pending ack stats in topic {}",
clientAppId(), topicName, ex);
}
@@ -314,7 +314,7 @@ public void getPendingAckInternalStats(@Suspended final AsyncResponse asyncRespo
internalGetPendingAckInternalStats(authoritative, subName, metadata)
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get pending ack internal stats {}",
clientAppId(), topicName, ex);
}
@@ -365,7 +365,7 @@ public void getTransactionBufferInternalStats(@Suspended final AsyncResponse asy
internalGetTransactionBufferInternalStats(authoritative, metadata)
.thenAccept(asyncResponse::resume)
.exceptionally(ex -> {
- if (!isRedirectException(ex)) {
+ if (!isNot307And404Exception(ex)) {
log.error("[{}] Failed to get transaction buffer internal stats {}",
clientAppId(), topicName, ex);
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/intercept/BrokerInterceptors.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/intercept/BrokerInterceptors.java
index cef3f0eb609a1..30d1874a97299 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/intercept/BrokerInterceptors.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/intercept/BrokerInterceptors.java
@@ -59,6 +59,9 @@ public BrokerInterceptors(Map intercep
* @return the collection of broker event interceptor
*/
public static BrokerInterceptor load(ServiceConfiguration conf) throws IOException {
+ if (conf.getBrokerInterceptors().isEmpty()) {
+ return null;
+ }
BrokerInterceptorDefinitions definitions =
BrokerInterceptorUtils.searchForInterceptors(conf.getBrokerInterceptorsDirectory(),
conf.getNarExtractionDirectory());
@@ -87,7 +90,7 @@ public static BrokerInterceptor load(ServiceConfiguration conf) throws IOExcepti
});
Map interceptors = builder.build();
- if (interceptors != null && !interceptors.isEmpty()) {
+ if (!interceptors.isEmpty()) {
return new BrokerInterceptors(interceptors);
} else {
return null;
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/LeaderBroker.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/LeaderBroker.java
index acd34e151ed2a..d7c21de5ea1fa 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/LeaderBroker.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/LeaderBroker.java
@@ -30,5 +30,23 @@
@AllArgsConstructor
@NoArgsConstructor
public class LeaderBroker {
+ private String brokerId;
private String serviceUrl;
+
+ public String getBrokerId() {
+ if (brokerId != null) {
+ return brokerId;
+ } else {
+ // for backward compatibility at runtime with older versions of Pulsar
+ return parseHostAndPort(serviceUrl);
+ }
+ }
+
+ private static String parseHostAndPort(String serviceUrl) {
+ int uriSeparatorPos = serviceUrl.indexOf("://");
+ if (uriSeparatorPos == -1) {
+ throw new IllegalArgumentException("'" + serviceUrl + "' isn't an URI.");
+ }
+ return serviceUrl.substring(uriSeparatorPos + 3);
+ }
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/LeaderElectionService.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/LeaderElectionService.java
index 05fe4353f3e76..2e53b54e98f61 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/LeaderElectionService.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/LeaderElectionService.java
@@ -35,17 +35,17 @@ public class LeaderElectionService implements AutoCloseable {
private final LeaderElection leaderElection;
private final LeaderBroker localValue;
- public LeaderElectionService(CoordinationService cs, String localWebServiceAddress,
- Consumer listener) {
- this(cs, localWebServiceAddress, ELECTION_ROOT, listener);
+ public LeaderElectionService(CoordinationService cs, String brokerId,
+ String serviceUrl, Consumer listener) {
+ this(cs, brokerId, serviceUrl, ELECTION_ROOT, listener);
}
public LeaderElectionService(CoordinationService cs,
- String localWebServiceAddress,
- String electionRoot,
+ String brokerId,
+ String serviceUrl, String electionRoot,
Consumer listener) {
this.leaderElection = cs.getLeaderElection(LeaderBroker.class, electionRoot, listener);
- this.localValue = new LeaderBroker(localWebServiceAddress);
+ this.localValue = new LeaderBroker(brokerId, serviceUrl);
}
public void start() {
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/NoopLoadManager.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/NoopLoadManager.java
index 80f887d394dd9..f9f36b705d4c4 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/NoopLoadManager.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/NoopLoadManager.java
@@ -43,7 +43,7 @@
public class NoopLoadManager implements LoadManager {
private PulsarService pulsar;
- private String lookupServiceAddress;
+ private String brokerId;
private ResourceUnit localResourceUnit;
private LockManager lockManager;
private Map bundleBrokerAffinityMap;
@@ -57,16 +57,15 @@ public void initialize(PulsarService pulsar) {
@Override
public void start() throws PulsarServerException {
- lookupServiceAddress = pulsar.getLookupServiceAddress();
- localResourceUnit = new SimpleResourceUnit(String.format("http://%s", lookupServiceAddress),
- new PulsarResourceDescription());
+ brokerId = pulsar.getBrokerId();
+ localResourceUnit = new SimpleResourceUnit(brokerId, new PulsarResourceDescription());
LocalBrokerData localData = new LocalBrokerData(pulsar.getWebServiceAddress(),
pulsar.getWebServiceAddressTls(),
pulsar.getBrokerServiceUrl(), pulsar.getBrokerServiceUrlTls(), pulsar.getAdvertisedListeners());
localData.setProtocols(pulsar.getProtocolDataToAdvertise());
localData.setLoadManagerClassName(this.pulsar.getConfig().getLoadManagerClassName());
- String brokerReportPath = LoadManager.LOADBALANCE_BROKERS_ROOT + "/" + lookupServiceAddress;
+ String brokerReportPath = LoadManager.LOADBALANCE_BROKERS_ROOT + "/" + brokerId;
try {
log.info("Acquiring broker resource lock on {}", brokerReportPath);
@@ -129,12 +128,12 @@ public void disableBroker() throws Exception {
@Override
public Set getAvailableBrokers() throws Exception {
- return Collections.singleton(lookupServiceAddress);
+ return Collections.singleton(brokerId);
}
@Override
public CompletableFuture> getAvailableBrokersAsync() {
- return CompletableFuture.completedFuture(Collections.singleton(lookupServiceAddress));
+ return CompletableFuture.completedFuture(Collections.singleton(brokerId));
}
@Override
@@ -153,7 +152,6 @@ public String setNamespaceBundleAffinity(String bundle, String broker) {
if (StringUtils.isBlank(broker)) {
return this.bundleBrokerAffinityMap.remove(bundle);
}
- broker = broker.replaceFirst("http[s]?://", "");
return this.bundleBrokerAffinityMap.put(bundle, broker);
}
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/ResourceUnit.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/ResourceUnit.java
index ef4dd2a97b280..c28a8be4c0d3a 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/ResourceUnit.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/ResourceUnit.java
@@ -23,8 +23,6 @@
*/
public interface ResourceUnit extends Comparable {
- String PROPERTY_KEY_BROKER_ZNODE_NAME = "__advertised_addr";
-
String getResourceId();
ResourceDescription getAvailableResource();
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/BrokerRegistryImpl.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/BrokerRegistryImpl.java
index bfdaa078f1999..18e30ddf922d0 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/BrokerRegistryImpl.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/BrokerRegistryImpl.java
@@ -82,7 +82,7 @@ public BrokerRegistryImpl(PulsarService pulsar) {
this.brokerLookupDataLockManager = pulsar.getCoordinationService().getLockManager(BrokerLookupData.class);
this.scheduler = pulsar.getLoadManagerExecutor();
this.listeners = new ArrayList<>();
- this.brokerId = pulsar.getLookupServiceAddress();
+ this.brokerId = pulsar.getBrokerId();
this.brokerLookupData = new BrokerLookupData(
pulsar.getWebServiceAddress(),
pulsar.getWebServiceAddressTls(),
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/ExtensibleLoadManagerImpl.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/ExtensibleLoadManagerImpl.java
index 581183cf95ad3..4d642e400793a 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/ExtensibleLoadManagerImpl.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/ExtensibleLoadManagerImpl.java
@@ -21,6 +21,7 @@
import static java.lang.String.format;
import static org.apache.pulsar.broker.loadbalance.extensions.ExtensibleLoadManagerImpl.Role.Follower;
import static org.apache.pulsar.broker.loadbalance.extensions.ExtensibleLoadManagerImpl.Role.Leader;
+import static org.apache.pulsar.broker.loadbalance.extensions.channel.ServiceUnitStateChannelImpl.TOPIC;
import static org.apache.pulsar.broker.loadbalance.extensions.models.SplitDecision.Label.Success;
import static org.apache.pulsar.broker.loadbalance.extensions.models.SplitDecision.Reason.Admin;
import static org.apache.pulsar.broker.loadbalance.impl.LoadManagerShared.getNamespaceBundle;
@@ -118,6 +119,8 @@ public class ExtensibleLoadManagerImpl implements ExtensibleLoadManager {
private static final long MONITOR_INTERVAL_IN_MILLIS = 120_000;
+ public static final long COMPACTION_THRESHOLD = 5 * 1024 * 1024;
+
private static final String ELECTION_ROOT = "/loadbalance/extension/leader";
private PulsarService pulsar;
@@ -174,6 +177,8 @@ public class ExtensibleLoadManagerImpl implements ExtensibleLoadManager {
private volatile boolean started = false;
+ private boolean configuredSystemTopics = false;
+
private final AssignCounter assignCounter = new AssignCounter();
@Getter
private final UnloadCounter unloadCounter = new UnloadCounter();
@@ -268,7 +273,7 @@ public static boolean isLoadManagerExtensionEnabled(ServiceConfiguration conf) {
}
public static boolean isLoadManagerExtensionEnabled(PulsarService pulsar) {
- return pulsar.getLoadManager().get() instanceof ExtensibleLoadManagerImpl;
+ return pulsar.getLoadManager().get() instanceof ExtensibleLoadManagerWrapper;
}
public static ExtensibleLoadManagerImpl get(LoadManager loadManager) {
@@ -309,6 +314,27 @@ private static void createSystemTopics(PulsarService pulsar) throws PulsarServer
createSystemTopic(pulsar, TOP_BUNDLES_LOAD_DATA_STORE_TOPIC);
}
+ private static boolean configureSystemTopics(PulsarService pulsar) {
+ try {
+ if (ExtensibleLoadManagerImpl.isLoadManagerExtensionEnabled(pulsar)
+ && (pulsar.getConfiguration().isSystemTopicEnabled())
+ && pulsar.getConfiguration().isTopicLevelPoliciesEnabled()) {
+ Long threshold = pulsar.getAdminClient().topicPolicies().getCompactionThreshold(TOPIC);
+ if (threshold == null || COMPACTION_THRESHOLD != threshold.longValue()) {
+ pulsar.getAdminClient().topicPolicies().setCompactionThreshold(TOPIC, COMPACTION_THRESHOLD);
+ log.info("Set compaction threshold: {} bytes for system topic {}.", COMPACTION_THRESHOLD, TOPIC);
+ }
+ } else {
+ log.warn("System topic or topic level policies is disabled. "
+ + "{} compaction threshold follows the broker or namespace policies.", TOPIC);
+ }
+ return true;
+ } catch (Exception e) {
+ log.error("Failed to set compaction threshold for system topic:{}", TOPIC, e);
+ }
+ return false;
+ }
+
/**
* Gets the assigned broker for the given topic.
* @param pulsar PulsarService instance
@@ -348,7 +374,8 @@ public void start() throws PulsarServerException {
try {
this.brokerRegistry = new BrokerRegistryImpl(pulsar);
this.leaderElectionService = new LeaderElectionService(
- pulsar.getCoordinationService(), pulsar.getSafeWebServiceAddress(), ELECTION_ROOT,
+ pulsar.getCoordinationService(), pulsar.getBrokerId(),
+ pulsar.getSafeWebServiceAddress(), ELECTION_ROOT,
state -> {
pulsar.getLoadManagerExecutor().execute(() -> {
if (state == LeaderElectionState.Leading) {
@@ -377,9 +404,9 @@ public void start() throws PulsarServerException {
try {
this.brokerLoadDataStore = LoadDataStoreFactory
- .create(pulsar.getClient(), BROKER_LOAD_DATA_STORE_TOPIC, BrokerLoadData.class);
+ .create(pulsar, BROKER_LOAD_DATA_STORE_TOPIC, BrokerLoadData.class);
this.topBundlesLoadDataStore = LoadDataStoreFactory
- .create(pulsar.getClient(), TOP_BUNDLES_LOAD_DATA_STORE_TOPIC, TopBundlesLoadData.class);
+ .create(pulsar, TOP_BUNDLES_LOAD_DATA_STORE_TOPIC, TopBundlesLoadData.class);
} catch (LoadDataStoreException e) {
throw new PulsarServerException(e);
}
@@ -437,7 +464,10 @@ public void start() throws PulsarServerException {
this.splitScheduler.start();
this.initWaiter.countDown();
this.started = true;
+ log.info("Started load manager.");
} catch (Exception ex) {
+ log.error("Failed to start the extensible load balance and close broker registry {}.",
+ this.brokerRegistry, ex);
if (this.brokerRegistry != null) {
brokerRegistry.close();
}
@@ -569,7 +599,7 @@ private CompletableFuture> dedupeLookupRequest(
if (ex != null) {
assignCounter.incrementFailure();
}
- lookupRequests.remove(key, newFutureCreated.getValue());
+ lookupRequests.remove(key);
});
}
}
@@ -782,18 +812,23 @@ public void close() throws PulsarServerException {
}
public static boolean isInternalTopic(String topic) {
- return topic.startsWith(ServiceUnitStateChannelImpl.TOPIC)
+ return topic.startsWith(TOPIC)
|| topic.startsWith(BROKER_LOAD_DATA_STORE_TOPIC)
|| topic.startsWith(TOP_BUNDLES_LOAD_DATA_STORE_TOPIC);
}
@VisibleForTesting
- void playLeader() {
+ synchronized void playLeader() {
log.info("This broker:{} is setting the role from {} to {}",
- pulsar.getLookupServiceAddress(), role, Leader);
+ pulsar.getBrokerId(), role, Leader);
int retry = 0;
+ boolean becameFollower = false;
while (!Thread.currentThread().isInterrupted()) {
try {
+ if (!serviceUnitStateChannel.isChannelOwner()) {
+ becameFollower = true;
+ break;
+ }
initWaiter.await();
// Confirm the system topics have been created or create them if they do not exist.
// If the leader has changed, the new leader need to reset
@@ -806,8 +841,8 @@ void playLeader() {
serviceUnitStateChannel.scheduleOwnershipMonitor();
break;
} catch (Throwable e) {
- log.error("The broker:{} failed to set the role. Retrying {} th ...",
- pulsar.getLookupServiceAddress(), ++retry, e);
+ log.warn("The broker:{} failed to set the role. Retrying {} th ...",
+ pulsar.getBrokerId(), ++retry, e);
try {
Thread.sleep(Math.min(retry * 10, MAX_ROLE_CHANGE_RETRY_DELAY_IN_MILLIS));
} catch (InterruptedException ex) {
@@ -817,8 +852,15 @@ void playLeader() {
}
}
}
+
+ if (becameFollower) {
+ log.warn("The broker:{} became follower while initializing leader role.", pulsar.getBrokerId());
+ playFollower();
+ return;
+ }
+
role = Leader;
- log.info("This broker:{} plays the leader now.", pulsar.getLookupServiceAddress());
+ log.info("This broker:{} plays the leader now.", pulsar.getBrokerId());
// flush the load data when the leader is elected.
brokerLoadDataReporter.reportAsync(true);
@@ -826,12 +868,17 @@ void playLeader() {
}
@VisibleForTesting
- void playFollower() {
+ synchronized void playFollower() {
log.info("This broker:{} is setting the role from {} to {}",
- pulsar.getLookupServiceAddress(), role, Follower);
+ pulsar.getBrokerId(), role, Follower);
int retry = 0;
+ boolean becameLeader = false;
while (!Thread.currentThread().isInterrupted()) {
try {
+ if (serviceUnitStateChannel.isChannelOwner()) {
+ becameLeader = true;
+ break;
+ }
initWaiter.await();
unloadScheduler.close();
serviceUnitStateChannel.cancelOwnershipMonitor();
@@ -840,8 +887,8 @@ void playFollower() {
topBundlesLoadDataStore.startProducer();
break;
} catch (Throwable e) {
- log.error("The broker:{} failed to set the role. Retrying {} th ...",
- pulsar.getLookupServiceAddress(), ++retry, e);
+ log.warn("The broker:{} failed to set the role. Retrying {} th ...",
+ pulsar.getBrokerId(), ++retry, e);
try {
Thread.sleep(Math.min(retry * 10, MAX_ROLE_CHANGE_RETRY_DELAY_IN_MILLIS));
} catch (InterruptedException ex) {
@@ -851,8 +898,15 @@ void playFollower() {
}
}
}
+
+ if (becameLeader) {
+ log.warn("This broker:{} became leader while initializing follower role.", pulsar.getBrokerId());
+ playLeader();
+ return;
+ }
+
role = Follower;
- log.info("This broker:{} plays a follower now.", pulsar.getLookupServiceAddress());
+ log.info("This broker:{} plays a follower now.", pulsar.getBrokerId());
// flush the load data when the leader is elected.
brokerLoadDataReporter.reportAsync(true);
@@ -880,7 +934,8 @@ public List getMetrics() {
return metricsCollection;
}
- private void monitor() {
+ @VisibleForTesting
+ protected void monitor() {
try {
initWaiter.await();
@@ -888,6 +943,11 @@ private void monitor() {
// Periodically check the role in case ZK watcher fails.
var isChannelOwner = serviceUnitStateChannel.isChannelOwner();
if (isChannelOwner) {
+ // System topic config might fail due to the race condition
+ // with topic policy init(Topic policies cache have not init).
+ if (!configuredSystemTopics) {
+ configuredSystemTopics = configureSystemTopics(pulsar);
+ }
if (role != Leader) {
log.warn("Current role:{} does not match with the channel ownership:{}. "
+ "Playing the leader role.", role, isChannelOwner);
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/ExtensibleLoadManagerWrapper.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/ExtensibleLoadManagerWrapper.java
index 18e949537dedb..cd1561cb70e2d 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/ExtensibleLoadManagerWrapper.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/ExtensibleLoadManagerWrapper.java
@@ -118,13 +118,11 @@ public void setLoadReportForceUpdateFlag() {
@Override
public void writeLoadReportOnZookeeper() throws Exception {
// No-op, this operation is not useful, the load data reporter will automatically write.
- throw new UnsupportedOperationException();
}
@Override
public void writeResourceQuotasToZooKeeper() throws Exception {
// No-op, this operation is not useful, the load data reporter will automatically write.
- throw new UnsupportedOperationException();
}
@Override
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/channel/ServiceUnitStateChannelImpl.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/channel/ServiceUnitStateChannelImpl.java
index 713d98b72507e..d8aacb86902a1 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/channel/ServiceUnitStateChannelImpl.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/channel/ServiceUnitStateChannelImpl.java
@@ -73,6 +73,7 @@
import org.apache.pulsar.broker.PulsarServerException;
import org.apache.pulsar.broker.PulsarService;
import org.apache.pulsar.broker.ServiceConfiguration;
+import org.apache.pulsar.broker.loadbalance.LeaderBroker;
import org.apache.pulsar.broker.loadbalance.LeaderElectionService;
import org.apache.pulsar.broker.loadbalance.extensions.BrokerRegistry;
import org.apache.pulsar.broker.loadbalance.extensions.ExtensibleLoadManagerImpl;
@@ -124,7 +125,7 @@ public class ServiceUnitStateChannelImpl implements ServiceUnitStateChannel {
private final ServiceConfiguration config;
private final Schema schema;
private final Map> getOwnerRequests;
- private final String lookupServiceAddress;
+ private final String brokerId;
private final Map> cleanupJobs;
private final StateChangeListeners stateChangeListeners;
private ExtensibleLoadManagerImpl loadManager;
@@ -201,7 +202,7 @@ enum MetadataState {
public ServiceUnitStateChannelImpl(PulsarService pulsar) {
this.pulsar = pulsar;
this.config = pulsar.getConfig();
- this.lookupServiceAddress = pulsar.getLookupServiceAddress();
+ this.brokerId = pulsar.getBrokerId();
this.schema = Schema.JSON(ServiceUnitStateData.class);
this.getOwnerRequests = new ConcurrentHashMap<>();
this.cleanupJobs = new ConcurrentHashMap<>();
@@ -249,7 +250,7 @@ public void scheduleOwnershipMonitor() {
},
0, ownershipMonitorDelayTimeInSecs, SECONDS);
log.info("This leader broker:{} started the ownership monitor.",
- lookupServiceAddress);
+ brokerId);
}
}
@@ -258,13 +259,13 @@ public void cancelOwnershipMonitor() {
monitorTask.cancel(false);
monitorTask = null;
log.info("This previous leader broker:{} stopped the ownership monitor.",
- lookupServiceAddress);
+ brokerId);
}
}
@Override
public void cleanOwnerships() {
- doCleanup(lookupServiceAddress);
+ doCleanup(brokerId);
}
public synchronized void start() throws PulsarServerException {
@@ -430,19 +431,8 @@ public CompletableFuture> getChannelOwnerAsync() {
new IllegalStateException("Invalid channel state:" + channelState.name()));
}
- return leaderElectionService.readCurrentLeader().thenApply(leader -> {
- //expecting http://broker-xyz:port
- // TODO: discard this protocol prefix removal
- // by a util func that returns lookupServiceAddress(serviceUrl)
- if (leader.isPresent()) {
- String broker = leader.get().getServiceUrl();
- broker = broker.substring(broker.lastIndexOf('/') + 1);
- return Optional.of(broker);
- } else {
- return Optional.empty();
- }
- }
- );
+ return leaderElectionService.readCurrentLeader()
+ .thenApply(leader -> leader.map(LeaderBroker::getBrokerId));
}
public CompletableFuture isChannelOwnerAsync() {
@@ -484,7 +474,35 @@ public boolean isOwner(String serviceUnit, String targetBroker) {
}
public boolean isOwner(String serviceUnit) {
- return isOwner(serviceUnit, lookupServiceAddress);
+ return isOwner(serviceUnit, brokerId);
+ }
+
+ private CompletableFuture> getActiveOwnerAsync(
+ String serviceUnit,
+ ServiceUnitState state,
+ Optional owner) {
+ return deferGetOwnerRequest(serviceUnit)
+ .thenCompose(newOwner -> {
+ if (newOwner == null) {
+ return CompletableFuture.completedFuture(null);
+ }
+
+ return brokerRegistry.lookupAsync(newOwner)
+ .thenApply(lookupData -> {
+ if (lookupData.isPresent()) {
+ return newOwner;
+ } else {
+ throw new IllegalStateException(
+ "The new owner " + newOwner + " is inactive.");
+ }
+ });
+ }).whenComplete((__, e) -> {
+ if (e != null) {
+ log.error("{} failed to get active owner broker. serviceUnit:{}, state:{}, owner:{}",
+ brokerId, serviceUnit, state, owner, e);
+ ownerLookUpCounters.get(state).getFailure().incrementAndGet();
+ }
+ }).thenApply(Optional::ofNullable);
}
public CompletableFuture> getOwnerAsync(String serviceUnit) {
@@ -498,18 +516,22 @@ public CompletableFuture> getOwnerAsync(String serviceUnit) {
ownerLookUpCounters.get(state).getTotal().incrementAndGet();
switch (state) {
case Owned -> {
- return CompletableFuture.completedFuture(Optional.of(data.dstBroker()));
+ return getActiveOwnerAsync(serviceUnit, state, Optional.of(data.dstBroker()));
}
case Splitting -> {
- return CompletableFuture.completedFuture(Optional.of(data.sourceBroker()));
+ return getActiveOwnerAsync(serviceUnit, state, Optional.of(data.sourceBroker()));
}
case Assigning, Releasing -> {
- return deferGetOwnerRequest(serviceUnit).whenComplete((__, e) -> {
- if (e != null) {
- ownerLookUpCounters.get(state).getFailure().incrementAndGet();
- }
- }).thenApply(
- broker -> broker == null ? Optional.empty() : Optional.of(broker));
+ if (isTargetBroker(data.dstBroker())) {
+ return getActiveOwnerAsync(serviceUnit, state, Optional.of(data.dstBroker()));
+ }
+ // If this broker is not the dst broker, return the dst broker as the owner(or empty).
+ // Clients need to connect(redirect) to the dst broker anyway
+ // and wait for the dst broker to receive `Owned`.
+ // This is also required to return getOwnerAsync on the src broker immediately during unloading.
+ // Otherwise, topic creation(getOwnerAsync) could block unloading bundles,
+ // if the topic creation(getOwnerAsync) happens during unloading on the src broker.
+ return CompletableFuture.completedFuture(Optional.ofNullable(data.dstBroker()));
}
case Init, Free -> {
return CompletableFuture.completedFuture(Optional.empty());
@@ -527,6 +549,25 @@ public CompletableFuture> getOwnerAsync(String serviceUnit) {
}
}
+ private Optional getOwner(String serviceUnit) {
+ ServiceUnitStateData data = tableview.get(serviceUnit);
+ ServiceUnitState state = state(data);
+ switch (state) {
+ case Owned -> {
+ return Optional.of(data.dstBroker());
+ }
+ case Splitting -> {
+ return Optional.of(data.sourceBroker());
+ }
+ case Init, Free -> {
+ return Optional.empty();
+ }
+ default -> {
+ return null;
+ }
+ }
+ }
+
@Override
public Optional getAssigned(String serviceUnit) {
if (!validateChannelState(Started, true)) {
@@ -656,7 +697,7 @@ private void handle(String serviceUnit, ServiceUnitStateData data) {
long totalHandledRequests = getHandlerTotalCounter(data).incrementAndGet();
if (debug()) {
log.info("{} received a handle request for serviceUnit:{}, data:{}. totalHandledRequests:{}",
- lookupServiceAddress, serviceUnit, data, totalHandledRequests);
+ brokerId, serviceUnit, data, totalHandledRequests);
}
ServiceUnitState state = state(data);
@@ -715,12 +756,12 @@ private AtomicLong getHandlerCounter(ServiceUnitStateData data, boolean total) {
private void log(Throwable e, String serviceUnit, ServiceUnitStateData data, ServiceUnitStateData next) {
if (e == null) {
- if (log.isDebugEnabled() || isTransferCommand(data)) {
+ if (debug() || isTransferCommand(data)) {
long handlerTotalCount = getHandlerTotalCounter(data).get();
long handlerFailureCount = getHandlerFailureCounter(data).get();
log.info("{} handled {} event for serviceUnit:{}, cur:{}, next:{}, "
+ "totalHandledRequests:{}, totalFailedRequests:{}",
- lookupServiceAddress, getLogEventTag(data), serviceUnit,
+ brokerId, getLogEventTag(data), serviceUnit,
data == null ? "" : data,
next == null ? "" : next,
handlerTotalCount, handlerFailureCount
@@ -731,7 +772,7 @@ lookupServiceAddress, getLogEventTag(data), serviceUnit,
long handlerFailureCount = getHandlerFailureCounter(data).incrementAndGet();
log.error("{} failed to handle {} event for serviceUnit:{}, cur:{}, next:{}, "
+ "totalHandledRequests:{}, totalFailedRequests:{}",
- lookupServiceAddress, getLogEventTag(data), serviceUnit,
+ brokerId, getLogEventTag(data), serviceUnit,
data == null ? "" : data,
next == null ? "" : next,
handlerTotalCount, handlerFailureCount,
@@ -754,6 +795,9 @@ private void handleSkippedEvent(String serviceUnit) {
private void handleOwnEvent(String serviceUnit, ServiceUnitStateData data) {
var getOwnerRequest = getOwnerRequests.remove(serviceUnit);
if (getOwnerRequest != null) {
+ if (debug()) {
+ log.info("Returned owner request for serviceUnit:{}", serviceUnit);
+ }
getOwnerRequest.complete(data.dstBroker());
}
@@ -812,9 +856,14 @@ private void handleFreeEvent(String serviceUnit, ServiceUnitStateData data) {
if (getOwnerRequest != null) {
getOwnerRequest.complete(null);
}
- stateChangeListeners.notify(serviceUnit, data, null);
+
if (isTargetBroker(data.sourceBroker())) {
- log(null, serviceUnit, data, null);
+ stateChangeListeners.notifyOnCompletion(
+ data.force() ? closeServiceUnit(serviceUnit, true)
+ : CompletableFuture.completedFuture(0), serviceUnit, data)
+ .whenComplete((__, e) -> log(e, serviceUnit, data, null));
+ } else {
+ stateChangeListeners.notify(serviceUnit, data, null);
}
}
@@ -864,30 +913,56 @@ private boolean isTargetBroker(String broker) {
if (broker == null) {
return false;
}
- return broker.equals(lookupServiceAddress);
+ return broker.equals(brokerId);
}
private CompletableFuture deferGetOwnerRequest(String serviceUnit) {
+
var requested = new MutableObject>();
try {
return getOwnerRequests
.computeIfAbsent(serviceUnit, k -> {
- CompletableFuture future = new CompletableFuture<>();
+ var ownerBefore = getOwner(serviceUnit);
+ if (ownerBefore != null && ownerBefore.isPresent()) {
+ // Here, we do a quick active check first with the computeIfAbsent lock
+ brokerRegistry.lookupAsync(ownerBefore.get()).getNow(Optional.empty())
+ .ifPresent(__ -> requested.setValue(
+ CompletableFuture.completedFuture(ownerBefore.get())));
+
+ if (requested.getValue() != null) {
+ return requested.getValue();
+ }
+ }
+
+
+ CompletableFuture future =
+ new CompletableFuture().orTimeout(inFlightStateWaitingTimeInMillis,
+ TimeUnit.MILLISECONDS)
+ .exceptionally(e -> {
+ var ownerAfter = getOwner(serviceUnit);
+ log.warn("{} failed to wait for owner for serviceUnit:{}; Trying to "
+ + "return the current owner:{}",
+ brokerId, serviceUnit, ownerAfter, e);
+ if (ownerAfter == null) {
+ throw new IllegalStateException(e);
+ }
+ return ownerAfter.orElse(null);
+ });
+ if (debug()) {
+ log.info("{} is waiting for owner for serviceUnit:{}", brokerId, serviceUnit);
+ }
requested.setValue(future);
return future;
});
} finally {
var future = requested.getValue();
if (future != null) {
- future.orTimeout(inFlightStateWaitingTimeInMillis + 5 * 1000, TimeUnit.MILLISECONDS)
- .whenComplete((v, e) -> {
- if (e != null) {
- getOwnerRequests.remove(serviceUnit, future);
- log.warn("Failed to getOwner for serviceUnit:{}",
- serviceUnit, e);
- }
- }
- );
+ future.whenComplete((__, e) -> {
+ getOwnerRequests.remove(serviceUnit);
+ if (e != null) {
+ log.warn("{} failed to getOwner for serviceUnit:{}", brokerId, serviceUnit, e);
+ }
+ });
}
}
}
@@ -1202,38 +1277,43 @@ private void scheduleCleanup(String broker, long delayInSecs) {
private ServiceUnitStateData getOverrideInactiveBrokerStateData(ServiceUnitStateData orphanData,
- String selectedBroker,
+ Optional selectedBroker,
String inactiveBroker) {
+
+
+ if (selectedBroker.isEmpty()) {
+ return new ServiceUnitStateData(Free, null, inactiveBroker,
+ true, getNextVersionId(orphanData));
+ }
+
if (orphanData.state() == Splitting) {
- return new ServiceUnitStateData(Splitting, orphanData.dstBroker(), selectedBroker,
+ return new ServiceUnitStateData(Splitting, orphanData.dstBroker(), selectedBroker.get(),
Map.copyOf(orphanData.splitServiceUnitToDestBroker()),
true, getNextVersionId(orphanData));
} else {
- return new ServiceUnitStateData(Owned, selectedBroker, inactiveBroker,
+ return new ServiceUnitStateData(Owned, selectedBroker.get(), inactiveBroker,
true, getNextVersionId(orphanData));
}
}
private void overrideOwnership(String serviceUnit, ServiceUnitStateData orphanData, String inactiveBroker) {
Optional selectedBroker = selectBroker(serviceUnit, inactiveBroker);
- if (selectedBroker.isPresent()) {
- var override = getOverrideInactiveBrokerStateData(
- orphanData, selectedBroker.get(), inactiveBroker);
- log.info("Overriding ownership serviceUnit:{} from orphanData:{} to overrideData:{}",
- serviceUnit, orphanData, override);
- publishOverrideEventAsync(serviceUnit, orphanData, override)
- .exceptionally(e -> {
- log.error(
- "Failed to override the ownership serviceUnit:{} orphanData:{}. "
- + "Failed to publish override event. totalCleanupErrorCnt:{}",
- serviceUnit, orphanData, totalCleanupErrorCnt.incrementAndGet());
- return null;
- });
- } else {
- log.error("Failed to override the ownership serviceUnit:{} orphanData:{}. Empty selected broker. "
+ if (selectedBroker.isEmpty()) {
+ log.warn("Empty selected broker for ownership serviceUnit:{} orphanData:{}."
+ "totalCleanupErrorCnt:{}",
serviceUnit, orphanData, totalCleanupErrorCnt.incrementAndGet());
}
+ var override = getOverrideInactiveBrokerStateData(orphanData, selectedBroker, inactiveBroker);
+ log.info("Overriding ownership serviceUnit:{} from orphanData:{} to overrideData:{}",
+ serviceUnit, orphanData, override);
+ publishOverrideEventAsync(serviceUnit, orphanData, override)
+ .exceptionally(e -> {
+ log.error(
+ "Failed to override the ownership serviceUnit:{} orphanData:{}. "
+ + "Failed to publish override event. totalCleanupErrorCnt:{}",
+ serviceUnit, orphanData, totalCleanupErrorCnt.incrementAndGet());
+ return null;
+ });
}
private void waitForCleanups(String broker, boolean excludeSystemTopics, int maxWaitTimeInMillis) {
@@ -1265,7 +1345,7 @@ private void waitForCleanups(String broker, boolean excludeSystemTopics, int max
MILLISECONDS.sleep(OWNERSHIP_CLEAN_UP_WAIT_RETRY_DELAY_IN_MILLIS);
} catch (InterruptedException e) {
log.warn("Interrupted while delaying the next service unit clean-up. Cleaning broker:{}",
- lookupServiceAddress);
+ brokerId);
}
}
}
@@ -1335,7 +1415,7 @@ private synchronized void doCleanup(String broker) {
broker,
cleanupTime,
orphanServiceUnitCleanupCnt,
- totalCleanupErrorCntStart - totalCleanupErrorCnt.get(),
+ totalCleanupErrorCnt.get() - totalCleanupErrorCntStart,
printCleanupMetrics());
}
@@ -1524,7 +1604,7 @@ protected void monitorOwnerships(List brokers) {
inactiveBrokers, inactiveBrokers.size(),
orphanServiceUnitCleanupCnt,
serviceUnitTombstoneCleanupCnt,
- totalCleanupErrorCntStart - totalCleanupErrorCnt.get(),
+ totalCleanupErrorCnt.get() - totalCleanupErrorCntStart,
printCleanupMetrics());
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/policies/IsolationPoliciesHelper.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/policies/IsolationPoliciesHelper.java
index 468552db541ec..56238d6528e60 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/policies/IsolationPoliciesHelper.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/policies/IsolationPoliciesHelper.java
@@ -42,14 +42,14 @@ public CompletableFuture> applyIsolationPoliciesAsync(Map,
private final BrokerHostUsage brokerHostUsage;
- private final String lookupServiceAddress;
+ private final String brokerId;
@Getter
private final BrokerLoadData localData;
@@ -67,10 +67,10 @@ public class BrokerLoadDataReporter implements LoadDataReporter,
private long tombstoneDelayInMillis;
public BrokerLoadDataReporter(PulsarService pulsar,
- String lookupServiceAddress,
+ String brokerId,
LoadDataStore brokerLoadDataStore) {
this.brokerLoadDataStore = brokerLoadDataStore;
- this.lookupServiceAddress = lookupServiceAddress;
+ this.brokerId = brokerId;
this.pulsar = pulsar;
this.conf = this.pulsar.getConfiguration();
if (SystemUtils.IS_OS_LINUX) {
@@ -111,7 +111,7 @@ public CompletableFuture reportAsync(boolean force) {
log.info("publishing load report:{}", localData.toString(conf));
}
CompletableFuture future =
- this.brokerLoadDataStore.pushAsync(this.lookupServiceAddress, newLoadData);
+ this.brokerLoadDataStore.pushAsync(this.brokerId, newLoadData);
future.whenComplete((__, ex) -> {
if (ex == null) {
localData.setReportedAt(System.currentTimeMillis());
@@ -185,7 +185,7 @@ protected void tombstone() {
}
var lastSuccessfulTombstonedAt = lastTombstonedAt;
lastTombstonedAt = now; // dedup first
- brokerLoadDataStore.removeAsync(lookupServiceAddress)
+ brokerLoadDataStore.removeAsync(brokerId)
.whenComplete((__, e) -> {
if (e != null) {
log.error("Failed to clean broker load data.", e);
@@ -209,13 +209,13 @@ public void handleEvent(String serviceUnit, ServiceUnitStateData data, Throwable
ServiceUnitState state = ServiceUnitStateData.state(data);
switch (state) {
case Releasing, Splitting -> {
- if (StringUtils.equals(data.sourceBroker(), lookupServiceAddress)) {
+ if (StringUtils.equals(data.sourceBroker(), brokerId)) {
localData.clear();
tombstone();
}
}
case Owned -> {
- if (StringUtils.equals(data.dstBroker(), lookupServiceAddress)) {
+ if (StringUtils.equals(data.dstBroker(), brokerId)) {
localData.clear();
tombstone();
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/reporter/TopBundleLoadDataReporter.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/reporter/TopBundleLoadDataReporter.java
index 0fa37d3687c20..43e05ad1ac972 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/reporter/TopBundleLoadDataReporter.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/reporter/TopBundleLoadDataReporter.java
@@ -41,7 +41,7 @@ public class TopBundleLoadDataReporter implements LoadDataReporter bundleLoadDataStore;
@@ -53,10 +53,10 @@ public class TopBundleLoadDataReporter implements LoadDataReporter bundleLoadDataStore) {
this.pulsar = pulsar;
- this.lookupServiceAddress = lookupServiceAddress;
+ this.brokerId = brokerId;
this.bundleLoadDataStore = bundleLoadDataStore;
this.lastBundleStatsUpdatedAt = 0;
this.topKBundles = new TopKBundles(pulsar);
@@ -88,7 +88,7 @@ public CompletableFuture reportAsync(boolean force) {
if (ExtensibleLoadManagerImpl.debug(pulsar.getConfiguration(), log)) {
log.info("Reporting TopBundlesLoadData:{}", topKBundles.getLoadData());
}
- return this.bundleLoadDataStore.pushAsync(lookupServiceAddress, topKBundles.getLoadData())
+ return this.bundleLoadDataStore.pushAsync(brokerId, topKBundles.getLoadData())
.exceptionally(e -> {
log.error("Failed to report top-bundles load data.", e);
return null;
@@ -106,7 +106,7 @@ protected void tombstone() {
}
var lastSuccessfulTombstonedAt = lastTombstonedAt;
lastTombstonedAt = now; // dedup first
- bundleLoadDataStore.removeAsync(lookupServiceAddress)
+ bundleLoadDataStore.removeAsync(brokerId)
.whenComplete((__, e) -> {
if (e != null) {
log.error("Failed to clean broker load data.", e);
@@ -129,12 +129,12 @@ public void handleEvent(String serviceUnit, ServiceUnitStateData data, Throwable
ServiceUnitState state = ServiceUnitStateData.state(data);
switch (state) {
case Releasing, Splitting -> {
- if (StringUtils.equals(data.sourceBroker(), lookupServiceAddress)) {
+ if (StringUtils.equals(data.sourceBroker(), brokerId)) {
tombstone();
}
}
case Owned -> {
- if (StringUtils.equals(data.dstBroker(), lookupServiceAddress)) {
+ if (StringUtils.equals(data.dstBroker(), brokerId)) {
tombstone();
}
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/scheduler/UnloadScheduler.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/scheduler/UnloadScheduler.java
index d6c754c90fcf6..218f57932a56b 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/scheduler/UnloadScheduler.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/scheduler/UnloadScheduler.java
@@ -219,9 +219,8 @@ private static NamespaceUnloadStrategy createNamespaceUnloadStrategy(PulsarServi
Thread.currentThread().getContextClassLoader());
log.info("Created namespace unload strategy:{}", unloadStrategy.getClass().getCanonicalName());
} catch (Exception e) {
- log.error("Error when trying to create namespace unload strategy: {}",
- conf.getLoadBalancerLoadPlacementStrategy(), e);
- log.error("create namespace unload strategy failed. using TransferShedder instead.");
+ log.error("Error when trying to create namespace unload strategy: {}. Using {} instead.",
+ conf.getLoadBalancerLoadSheddingStrategy(), TransferShedder.class.getCanonicalName(), e);
unloadStrategy = new TransferShedder();
}
unloadStrategy.initialize(pulsar);
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/store/LoadDataStoreFactory.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/store/LoadDataStoreFactory.java
index 18f39abd76b76..bcb2657c67f05 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/store/LoadDataStoreFactory.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/store/LoadDataStoreFactory.java
@@ -18,15 +18,16 @@
*/
package org.apache.pulsar.broker.loadbalance.extensions.store;
-import org.apache.pulsar.client.api.PulsarClient;
+import org.apache.pulsar.broker.PulsarService;
/**
* The load data store factory, use to create the load data store.
*/
public class LoadDataStoreFactory {
- public static LoadDataStore create(PulsarClient client, String name, Class clazz)
+ public static LoadDataStore create(PulsarService pulsar, String name,
+ Class clazz)
throws LoadDataStoreException {
- return new TableViewLoadDataStoreImpl<>(client, name, clazz);
+ return new TableViewLoadDataStoreImpl<>(pulsar, name, clazz);
}
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/store/TableViewLoadDataStoreImpl.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/store/TableViewLoadDataStoreImpl.java
index 56afbef04565c..d916e91716223 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/store/TableViewLoadDataStoreImpl.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/store/TableViewLoadDataStoreImpl.java
@@ -23,34 +23,46 @@
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.pulsar.broker.PulsarService;
+import org.apache.pulsar.broker.ServiceConfiguration;
import org.apache.pulsar.client.api.Producer;
import org.apache.pulsar.client.api.PulsarClient;
import org.apache.pulsar.client.api.PulsarClientException;
import org.apache.pulsar.client.api.Schema;
import org.apache.pulsar.client.api.TableView;
-import org.apache.pulsar.common.util.FutureUtil;
/**
* The load data store, base on {@link TableView }.
*
* @param Load data type.
*/
+@Slf4j
public class TableViewLoadDataStoreImpl implements LoadDataStore {
+ private static final long LOAD_DATA_REPORT_UPDATE_MAX_INTERVAL_MULTIPLIER_BEFORE_RESTART = 2;
+
private volatile TableView tableView;
+ private volatile long tableViewLastUpdateTimestamp;
private volatile Producer producer;
+ private final ServiceConfiguration conf;
+
private final PulsarClient client;
private final String topic;
private final Class clazz;
- public TableViewLoadDataStoreImpl(PulsarClient client, String topic, Class clazz) throws LoadDataStoreException {
+ public TableViewLoadDataStoreImpl(PulsarService pulsar, String topic, Class clazz)
+ throws LoadDataStoreException {
try {
- this.client = client;
+ this.conf = pulsar.getConfiguration();
+ this.client = pulsar.getClient();
this.topic = topic;
this.clazz = clazz;
} catch (Exception e) {
@@ -60,40 +72,36 @@ public TableViewLoadDataStoreImpl(PulsarClient client, String topic, Class cl
@Override
public synchronized CompletableFuture pushAsync(String key, T loadData) {
- if (producer == null) {
- return FutureUtil.failedFuture(new IllegalStateException("producer has not been started"));
- }
+ validateProducer();
return producer.newMessage().key(key).value(loadData).sendAsync().thenAccept(__ -> {});
}
@Override
public synchronized CompletableFuture removeAsync(String key) {
- if (producer == null) {
- return FutureUtil.failedFuture(new IllegalStateException("producer has not been started"));
- }
+ validateProducer();
return producer.newMessage().key(key).value(null).sendAsync().thenAccept(__ -> {});
}
@Override
public synchronized Optional get(String key) {
- validateTableViewStart();
+ validateTableView();
return Optional.ofNullable(tableView.get(key));
}
@Override
public synchronized void forEach(BiConsumer action) {
- validateTableViewStart();
+ validateTableView();
tableView.forEach(action);
}
public synchronized Set> entrySet() {
- validateTableViewStart();
+ validateTableView();
return tableView.entrySet();
}
@Override
public synchronized int size() {
- validateTableViewStart();
+ validateTableView();
return tableView.size();
}
@@ -116,6 +124,8 @@ public synchronized void startTableView() throws LoadDataStoreException {
if (tableView == null) {
try {
tableView = client.newTableViewBuilder(Schema.JSON(clazz)).topic(topic).create();
+ tableView.forEachAndListen((k, v) ->
+ tableViewLastUpdateTimestamp = System.currentTimeMillis());
} catch (PulsarClientException e) {
tableView = null;
throw new LoadDataStoreException(e);
@@ -150,9 +160,48 @@ public synchronized void init() throws IOException {
start();
}
- private synchronized void validateTableViewStart() {
+ private void validateProducer() {
+ if (producer == null || !producer.isConnected()) {
+ try {
+ if (producer != null) {
+ producer.close();
+ }
+ producer = null;
+ startProducer();
+ log.info("Restarted producer on {}", topic);
+ } catch (Exception e) {
+ log.error("Failed to restart producer on {}", topic, e);
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private void validateTableView() {
+ String restartReason = null;
+
if (tableView == null) {
- throw new IllegalStateException("table view has not been started");
+ restartReason = "table view is null";
+ } else {
+ long inactiveDuration = System.currentTimeMillis() - tableViewLastUpdateTimestamp;
+ long threshold = TimeUnit.MINUTES.toMillis(conf.getLoadBalancerReportUpdateMaxIntervalMinutes())
+ * LOAD_DATA_REPORT_UPDATE_MAX_INTERVAL_MULTIPLIER_BEFORE_RESTART;
+ if (inactiveDuration > threshold) {
+ restartReason = String.format("inactiveDuration=%d secs > threshold = %d secs",
+ TimeUnit.MILLISECONDS.toSeconds(inactiveDuration),
+ TimeUnit.MILLISECONDS.toSeconds(threshold));
+ }
+ }
+
+ if (StringUtils.isNotBlank(restartReason)) {
+ tableViewLastUpdateTimestamp = 0;
+ try {
+ closeTableView();
+ startTableView();
+ log.info("Restarted tableview on {}, {}", topic, restartReason);
+ } catch (Exception e) {
+ log.error("Failed to restart tableview on {}", topic, e);
+ throw new RuntimeException(e);
+ }
}
}
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/LoadManagerShared.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/LoadManagerShared.java
index 5f2e4b1f25d8b..3d627db6cfa9e 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/LoadManagerShared.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/LoadManagerShared.java
@@ -21,8 +21,6 @@
import static com.google.common.base.Preconditions.checkArgument;
import static org.apache.pulsar.common.stats.JvmMetrics.getJvmDirectMemoryUsed;
import io.netty.util.concurrent.FastThreadLocal;
-import java.net.MalformedURLException;
-import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -106,59 +104,56 @@ public static void applyNamespacePolicies(final ServiceUnitId serviceUnit,
if (isIsolationPoliciesPresent) {
LOG.debug("Isolation Policies Present for namespace - [{}]", namespace.toString());
}
- for (final String broker : availableBrokers) {
- final String brokerUrlString = String.format("http://%s", broker);
- URL brokerUrl;
+ for (final String brokerId : availableBrokers) {
+ String brokerHost;
try {
- brokerUrl = new URL(brokerUrlString);
- } catch (MalformedURLException e) {
- LOG.error("Unable to parse brokerUrl from ResourceUnitId", e);
+ brokerHost = parseBrokerHost(brokerId);
+ } catch (IllegalArgumentException e) {
+ LOG.error("Unable to parse host from {}", brokerId, e);
continue;
}
// todo: in future check if the resource unit has resources to take the namespace
if (isIsolationPoliciesPresent) {
// note: serviceUnitID is namespace name and ResourceID is brokerName
- if (policies.isPrimaryBroker(namespace, brokerUrl.getHost())) {
- primariesCache.add(broker);
+ if (policies.isPrimaryBroker(namespace, brokerHost)) {
+ primariesCache.add(brokerId);
if (LOG.isDebugEnabled()) {
LOG.debug("Added Primary Broker - [{}] as possible Candidates for"
- + " namespace - [{}] with policies", brokerUrl.getHost(), namespace.toString());
+ + " namespace - [{}] with policies", brokerHost, namespace.toString());
}
- } else if (policies.isSecondaryBroker(namespace, brokerUrl.getHost())) {
- secondaryCache.add(broker);
+ } else if (policies.isSecondaryBroker(namespace, brokerHost)) {
+ secondaryCache.add(brokerId);
if (LOG.isDebugEnabled()) {
LOG.debug(
"Added Shared Broker - [{}] as possible "
+ "Candidates for namespace - [{}] with policies",
- brokerUrl.getHost(), namespace.toString());
+ brokerHost, namespace.toString());
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Skipping Broker - [{}] not primary broker and not shared" + " for namespace - [{}] ",
- brokerUrl.getHost(), namespace.toString());
+ brokerHost, namespace.toString());
}
}
} else {
// non-persistent topic can be assigned to only those brokers that enabled for non-persistent topic
- if (isNonPersistentTopic
- && !brokerTopicLoadingPredicate.isEnableNonPersistentTopics(brokerUrlString)) {
+ if (isNonPersistentTopic && !brokerTopicLoadingPredicate.isEnableNonPersistentTopics(brokerId)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Filter broker- [{}] because it doesn't support non-persistent namespace - [{}]",
- brokerUrl.getHost(), namespace.toString());
+ brokerHost, namespace.toString());
}
- } else if (!isNonPersistentTopic
- && !brokerTopicLoadingPredicate.isEnablePersistentTopics(brokerUrlString)) {
+ } else if (!isNonPersistentTopic && !brokerTopicLoadingPredicate.isEnablePersistentTopics(brokerId)) {
// persistent topic can be assigned to only brokers that enabled for persistent-topic
if (LOG.isDebugEnabled()) {
LOG.debug("Filter broker- [{}] because broker only supports non-persistent namespace - [{}]",
- brokerUrl.getHost(), namespace.toString());
+ brokerHost, namespace.toString());
}
- } else if (policies.isSharedBroker(brokerUrl.getHost())) {
- secondaryCache.add(broker);
+ } else if (policies.isSharedBroker(brokerHost)) {
+ secondaryCache.add(brokerId);
if (LOG.isDebugEnabled()) {
LOG.debug("Added Shared Broker - [{}] as possible Candidates for namespace - [{}]",
- brokerUrl.getHost(), namespace.toString());
+ brokerHost, namespace.toString());
}
}
}
@@ -181,6 +176,16 @@ public static void applyNamespacePolicies(final ServiceUnitId serviceUnit,
}
}
+ private static String parseBrokerHost(String brokerId) {
+ // use last index to support ipv6 addresses
+ int lastIdx = brokerId.lastIndexOf(':');
+ if (lastIdx > -1) {
+ return brokerId.substring(0, lastIdx);
+ } else {
+ throw new IllegalArgumentException("Invalid brokerId: " + brokerId);
+ }
+ }
+
public static CompletableFuture> applyNamespacePoliciesAsync(
final ServiceUnitId serviceUnit, final SimpleResourceAllocationPolicies policies,
final Set availableBrokers, final BrokerTopicLoadingPredicate brokerTopicLoadingPredicate) {
@@ -199,59 +204,57 @@ public static CompletableFuture> applyNamespacePoliciesAsync(
LOG.debug("Isolation Policies Present for namespace - [{}]", namespace.toString());
}
}
- for (final String broker : availableBrokers) {
- final String brokerUrlString = String.format("http://%s", broker);
- URL brokerUrl;
+ for (final String brokerId : availableBrokers) {
+ String brokerHost;
try {
- brokerUrl = new URL(brokerUrlString);
- } catch (MalformedURLException e) {
- LOG.error("Unable to parse brokerUrl from ResourceUnitId", e);
+ brokerHost = parseBrokerHost(brokerId);
+ } catch (IllegalArgumentException e) {
+ LOG.error("Unable to parse host from {}", brokerId, e);
continue;
}
// todo: in future check if the resource unit has resources to take the namespace
if (isIsolationPoliciesPresent) {
// note: serviceUnitID is namespace name and ResourceID is brokerName
- if (policies.isPrimaryBroker(namespace, brokerUrl.getHost())) {
- primariesCache.add(broker);
+ if (policies.isPrimaryBroker(namespace, brokerHost)) {
+ primariesCache.add(brokerId);
if (LOG.isDebugEnabled()) {
LOG.debug("Added Primary Broker - [{}] as possible Candidates for"
- + " namespace - [{}] with policies", brokerUrl.getHost(), namespace.toString());
+ + " namespace - [{}] with policies", brokerHost, namespace.toString());
}
- } else if (policies.isSecondaryBroker(namespace, brokerUrl.getHost())) {
- secondaryCache.add(broker);
+ } else if (policies.isSecondaryBroker(namespace, brokerHost)) {
+ secondaryCache.add(brokerId);
if (LOG.isDebugEnabled()) {
LOG.debug(
"Added Shared Broker - [{}] as possible "
+ "Candidates for namespace - [{}] with policies",
- brokerUrl.getHost(), namespace.toString());
+ brokerHost, namespace.toString());
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Skipping Broker - [{}] not primary broker and not shared"
- + " for namespace - [{}] ", brokerUrl.getHost(), namespace.toString());
+ + " for namespace - [{}] ", brokerHost, namespace.toString());
}
}
} else {
// non-persistent topic can be assigned to only those brokers that enabled for non-persistent topic
- if (isNonPersistentTopic
- && !brokerTopicLoadingPredicate.isEnableNonPersistentTopics(brokerUrlString)) {
+ if (isNonPersistentTopic && !brokerTopicLoadingPredicate.isEnableNonPersistentTopics(brokerId)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Filter broker- [{}] because it doesn't support non-persistent namespace - [{}]",
- brokerUrl.getHost(), namespace.toString());
+ brokerId, namespace.toString());
}
- } else if (!isNonPersistentTopic
- && !brokerTopicLoadingPredicate.isEnablePersistentTopics(brokerUrlString)) {
+ } else if (!isNonPersistentTopic && !brokerTopicLoadingPredicate
+ .isEnablePersistentTopics(brokerId)) {
// persistent topic can be assigned to only brokers that enabled for persistent-topic
if (LOG.isDebugEnabled()) {
LOG.debug("Filter broker- [{}] because broker only supports non-persistent "
- + "namespace - [{}]", brokerUrl.getHost(), namespace.toString());
+ + "namespace - [{}]", brokerId, namespace.toString());
}
- } else if (policies.isSharedBroker(brokerUrl.getHost())) {
- secondaryCache.add(broker);
+ } else if (policies.isSharedBroker(brokerHost)) {
+ secondaryCache.add(brokerId);
if (LOG.isDebugEnabled()) {
LOG.debug("Added Shared Broker - [{}] as possible Candidates for namespace - [{}]",
- brokerUrl.getHost(), namespace.toString());
+ brokerHost, namespace.toString());
}
}
}
@@ -762,9 +765,9 @@ public static boolean shouldAntiAffinityNamespaceUnload(
}
public interface BrokerTopicLoadingPredicate {
- boolean isEnablePersistentTopics(String brokerUrl);
+ boolean isEnablePersistentTopics(String brokerId);
- boolean isEnableNonPersistentTopics(String brokerUrl);
+ boolean isEnableNonPersistentTopics(String brokerId);
}
/**
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/ModularLoadManagerImpl.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/ModularLoadManagerImpl.java
index 4ecdfefbdd041..974d75d60b203 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/ModularLoadManagerImpl.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/ModularLoadManagerImpl.java
@@ -208,15 +208,15 @@ public ModularLoadManagerImpl() {
this.bundleBrokerAffinityMap = new ConcurrentHashMap<>();
this.brokerTopicLoadingPredicate = new BrokerTopicLoadingPredicate() {
@Override
- public boolean isEnablePersistentTopics(String brokerUrl) {
- final BrokerData brokerData = loadData.getBrokerData().get(brokerUrl.replace("http://", ""));
+ public boolean isEnablePersistentTopics(String brokerId) {
+ final BrokerData brokerData = loadData.getBrokerData().get(brokerId);
return brokerData != null && brokerData.getLocalData() != null
&& brokerData.getLocalData().isPersistentTopicsEnabled();
}
@Override
- public boolean isEnableNonPersistentTopics(String brokerUrl) {
- final BrokerData brokerData = loadData.getBrokerData().get(brokerUrl.replace("http://", ""));
+ public boolean isEnableNonPersistentTopics(String brokerId) {
+ final BrokerData brokerData = loadData.getBrokerData().get(brokerId);
return brokerData != null && brokerData.getLocalData() != null
&& brokerData.getLocalData().isNonPersistentTopicsEnabled();
}
@@ -977,14 +977,14 @@ public void start() throws PulsarServerException {
localData.setNonPersistentTopicsEnabled(pulsar.getConfiguration().isEnableNonPersistentTopics());
localData.setLoadManagerClassName(conf.getLoadManagerClassName());
- String lookupServiceAddress = pulsar.getLookupServiceAddress();
- brokerZnodePath = LoadManager.LOADBALANCE_BROKERS_ROOT + "/" + lookupServiceAddress;
+ String brokerId = pulsar.getBrokerId();
+ brokerZnodePath = LoadManager.LOADBALANCE_BROKERS_ROOT + "/" + brokerId;
updateLocalBrokerData();
brokerDataLock = brokersData.acquireLock(brokerZnodePath, localData).join();
pulsarResources.getLoadBalanceResources()
.getBrokerTimeAverageDataResources()
- .updateTimeAverageBrokerData(lookupServiceAddress, new TimeAverageBrokerData())
+ .updateTimeAverageBrokerData(brokerId, new TimeAverageBrokerData())
.join();
updateAll();
} catch (Exception e) {
@@ -1212,7 +1212,6 @@ public String setNamespaceBundleAffinity(String bundle, String broker) {
if (StringUtils.isBlank(broker)) {
return this.bundleBrokerAffinityMap.remove(bundle);
}
- broker = broker.replaceFirst("http[s]?://", "");
return this.bundleBrokerAffinityMap.put(bundle, broker);
}
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/ModularLoadManagerWrapper.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/ModularLoadManagerWrapper.java
index c61d39cf3159a..c8d81bda1bc13 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/ModularLoadManagerWrapper.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/ModularLoadManagerWrapper.java
@@ -19,7 +19,6 @@
package org.apache.pulsar.broker.loadbalance.impl;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -32,7 +31,6 @@
import org.apache.pulsar.common.naming.ServiceUnitId;
import org.apache.pulsar.common.stats.Metrics;
import org.apache.pulsar.policies.data.loadbalancer.LoadManagerReport;
-import org.apache.pulsar.policies.data.loadbalancer.LocalBrokerData;
/**
* Wrapper class allowing classes of instance ModularLoadManager to be compatible with the interface LoadManager.
@@ -75,20 +73,6 @@ public Optional getLeastLoaded(final ServiceUnitId serviceUnit) {
return leastLoadedBroker.map(this::buildBrokerResourceUnit);
}
- private String getBrokerWebServiceUrl(String broker) {
- LocalBrokerData localData = (loadManager).getBrokerLocalData(broker);
- if (localData != null) {
- return localData.getWebServiceUrl() != null ? localData.getWebServiceUrl()
- : localData.getWebServiceUrlTls();
- }
- return String.format("http://%s", broker);
- }
-
- private String getBrokerZnodeName(String broker, String webServiceUrl) {
- String scheme = webServiceUrl.substring(0, webServiceUrl.indexOf("://"));
- return String.format("%s://%s", scheme, broker);
- }
-
@Override
public List getLoadBalancingMetrics() {
return loadManager.getLoadBalancingMetrics();
@@ -149,10 +133,7 @@ public CompletableFuture> getAvailableBrokersAsync() {
}
private SimpleResourceUnit buildBrokerResourceUnit (String broker) {
- String webServiceUrl = getBrokerWebServiceUrl(broker);
- String brokerZnodeName = getBrokerZnodeName(broker, webServiceUrl);
- return new SimpleResourceUnit(webServiceUrl,
- new PulsarResourceDescription(), Map.of(ResourceUnit.PROPERTY_KEY_BROKER_ZNODE_NAME, brokerZnodeName));
+ return new SimpleResourceUnit(broker, new PulsarResourceDescription());
}
@Override
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/SimpleLoadManagerImpl.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/SimpleLoadManagerImpl.java
index ee60595a485c4..be0580808cafb 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/SimpleLoadManagerImpl.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/impl/SimpleLoadManagerImpl.java
@@ -211,15 +211,15 @@ public SimpleLoadManagerImpl() {
.build();
this.brokerTopicLoadingPredicate = new BrokerTopicLoadingPredicate() {
@Override
- public boolean isEnablePersistentTopics(String brokerUrl) {
- ResourceUnit ru = new SimpleResourceUnit(brokerUrl, new PulsarResourceDescription());
+ public boolean isEnablePersistentTopics(String brokerId) {
+ ResourceUnit ru = new SimpleResourceUnit(brokerId, new PulsarResourceDescription());
LoadReport loadReport = currentLoadReports.get(ru);
return loadReport != null && loadReport.isPersistentTopicsEnabled();
}
@Override
- public boolean isEnableNonPersistentTopics(String brokerUrl) {
- ResourceUnit ru = new SimpleResourceUnit(brokerUrl, new PulsarResourceDescription());
+ public boolean isEnableNonPersistentTopics(String brokerId) {
+ ResourceUnit ru = new SimpleResourceUnit(brokerId, new PulsarResourceDescription());
LoadReport loadReport = currentLoadReports.get(ru);
return loadReport != null && loadReport.isNonPersistentTopicsEnabled();
}
@@ -266,8 +266,8 @@ public SimpleLoadManagerImpl(PulsarService pulsar) {
@Override
public void start() throws PulsarServerException {
// Register the brokers in metadata store
- String lookupServiceAddress = pulsar.getLookupServiceAddress();
- String brokerLockPath = LOADBALANCE_BROKERS_ROOT + "/" + lookupServiceAddress;
+ String brokerId = pulsar.getBrokerId();
+ String brokerLockPath = LOADBALANCE_BROKERS_ROOT + "/" + brokerId;
try {
LoadReport loadReport = null;
@@ -653,7 +653,6 @@ public void writeResourceQuotasToZooKeeper() throws Exception {
*/
private synchronized void doLoadRanking() {
ResourceUnitRanking.setCpuUsageByMsgRate(this.realtimeCpuLoadFactor);
- String hostname = pulsar.getAdvertisedAddress();
String strategy = this.getLoadBalancerPlacementStrategy();
log.info("doLoadRanking - load balancing strategy: {}", strategy);
if (!currentLoadReports.isEmpty()) {
@@ -702,8 +701,8 @@ private synchronized void doLoadRanking() {
}
// update metrics
- if (resourceUnit.getResourceId().contains(hostname)) {
- updateLoadBalancingMetrics(hostname, finalRank, ranking);
+ if (resourceUnit.getResourceId().equals(pulsar.getBrokerId())) {
+ updateLoadBalancingMetrics(pulsar.getAdvertisedAddress(), finalRank, ranking);
}
}
updateBrokerToNamespaceToBundle();
@@ -711,7 +710,7 @@ private synchronized void doLoadRanking() {
this.resourceUnitRankings = newResourceUnitRankings;
} else {
log.info("Leader broker[{}] No ResourceUnits to rank this run, Using Old Ranking",
- pulsar.getSafeWebServiceAddress());
+ pulsar.getBrokerId());
}
}
@@ -855,7 +854,7 @@ private synchronized ResourceUnit findBrokerForPlacement(Multimap ConcurrentOpenHashMap.>newBuilder()
.build())
@@ -876,7 +875,7 @@ private Multimap getFinalCandidates(ServiceUnitId serviceUni
availableBrokersCache.clear();
for (final Set resourceUnits : availableBrokers.values()) {
for (final ResourceUnit resourceUnit : resourceUnits) {
- availableBrokersCache.add(resourceUnit.getResourceId().replace("http://", ""));
+ availableBrokersCache.add(resourceUnit.getResourceId());
}
}
brokerCandidateCache.clear();
@@ -899,7 +898,7 @@ private Multimap getFinalCandidates(ServiceUnitId serviceUni
final Long rank = entry.getKey();
final Set resourceUnits = entry.getValue();
for (final ResourceUnit resourceUnit : resourceUnits) {
- if (brokerCandidateCache.contains(resourceUnit.getResourceId().replace("http://", ""))) {
+ if (brokerCandidateCache.contains(resourceUnit.getResourceId())) {
result.put(rank, resourceUnit);
}
}
@@ -928,8 +927,7 @@ private Map> getAvailableBrokers(ServiceUnitId serviceUn
availableBrokers = new HashMap<>();
for (String broker : activeBrokers) {
- ResourceUnit resourceUnit = new SimpleResourceUnit(String.format("http://%s", broker),
- new PulsarResourceDescription());
+ ResourceUnit resourceUnit = new SimpleResourceUnit(broker, new PulsarResourceDescription());
availableBrokers.computeIfAbsent(0L, key -> new TreeSet<>()).add(resourceUnit);
}
log.info("Choosing at random from broker list: [{}]", availableBrokers.values());
@@ -956,7 +954,7 @@ private synchronized ResourceUnit getLeastLoadedBroker(ServiceUnitId serviceUnit
Iterator> candidateIterator = finalCandidates.entries().iterator();
while (candidateIterator.hasNext()) {
Map.Entry candidate = candidateIterator.next();
- String candidateBrokerName = candidate.getValue().getResourceId().replace("http://", "");
+ String candidateBrokerName = candidate.getValue().getResourceId();
if (!activeBrokers.contains(candidateBrokerName)) {
candidateIterator.remove(); // Current candidate points to an inactive broker, so remove it
}
@@ -1005,8 +1003,7 @@ private void updateRanking() {
try {
String key = String.format("%s/%s", LOADBALANCE_BROKERS_ROOT, broker);
LoadReport lr = loadReports.readLock(key).join().get();
- ResourceUnit ru = new SimpleResourceUnit(String.format("http://%s", lr.getName()),
- fromLoadReport(lr));
+ ResourceUnit ru = new SimpleResourceUnit(lr.getName(), fromLoadReport(lr));
this.currentLoadReports.put(ru, lr);
} catch (Exception e) {
log.warn("Error reading load report from Cache for broker - [{}], [{}]", broker, e);
@@ -1078,7 +1075,7 @@ private LoadReport generateLoadReportForcefully() throws Exception {
loadReport.setProtocols(pulsar.getProtocolDataToAdvertise());
loadReport.setNonPersistentTopicsEnabled(pulsar.getConfiguration().isEnableNonPersistentTopics());
loadReport.setPersistentTopicsEnabled(pulsar.getConfiguration().isEnablePersistentTopics());
- loadReport.setName(pulsar.getLookupServiceAddress());
+ loadReport.setName(pulsar.getBrokerId());
loadReport.setBrokerVersionString(pulsar.getBrokerVersion());
SystemResourceUsage systemResourceUsage = this.getSystemResourceUsage();
@@ -1121,8 +1118,8 @@ private LoadReport generateLoadReportForcefully() throws Exception {
loadReport.setAllocatedMsgRateIn(allocatedQuota.getMsgRateIn());
loadReport.setAllocatedMsgRateOut(allocatedQuota.getMsgRateOut());
- final ResourceUnit resourceUnit = new SimpleResourceUnit(
- String.format("http://%s", loadReport.getName()), fromLoadReport(loadReport));
+ final ResourceUnit resourceUnit =
+ new SimpleResourceUnit(loadReport.getName(), fromLoadReport(loadReport));
Set preAllocatedBundles;
if (resourceUnitRankings.containsKey(resourceUnit)) {
preAllocatedBundles = resourceUnitRankings.get(resourceUnit).getPreAllocatedBundles();
@@ -1277,7 +1274,7 @@ private synchronized void updateBrokerToNamespaceToBundle() {
final Set preallocatedBundles = resourceUnitRankings.get(resourceUnit).getPreAllocatedBundles();
final ConcurrentOpenHashMap> namespaceToBundleRange =
brokerToNamespaceToBundleRange
- .computeIfAbsent(broker.replace("http://", ""),
+ .computeIfAbsent(broker,
k -> ConcurrentOpenHashMap.>newBuilder()
.build());
@@ -1455,7 +1452,6 @@ public String setNamespaceBundleAffinity(String bundle, String broker) {
if (StringUtils.isBlank(broker)) {
return this.bundleBrokerAffinityMap.remove(bundle);
}
- broker = broker.replaceFirst("http[s]?://", "");
return this.bundleBrokerAffinityMap.put(bundle, broker);
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/lookup/TopicLookupBase.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/lookup/TopicLookupBase.java
index c4a39cd0d4455..7b2c777414884 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/lookup/TopicLookupBase.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/lookup/TopicLookupBase.java
@@ -30,6 +30,7 @@
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
+import org.apache.pulsar.broker.PulsarServerException;
import org.apache.pulsar.broker.PulsarService;
import org.apache.pulsar.broker.ServiceConfiguration;
import org.apache.pulsar.broker.authentication.AuthenticationDataSource;
@@ -333,13 +334,16 @@ public static CompletableFuture lookupTopicAsync(PulsarService pulsarSe
private static void handleLookupError(CompletableFuture lookupFuture, String topicName, String clientAppId,
long requestId, Throwable ex){
- final Throwable unwrapEx = FutureUtil.unwrapCompletionException(ex);
+ Throwable unwrapEx = FutureUtil.unwrapCompletionException(ex);
final String errorMsg = unwrapEx.getMessage();
+ if (unwrapEx instanceof PulsarServerException) {
+ unwrapEx = FutureUtil.unwrapCompletionException(unwrapEx.getCause());
+ }
if (unwrapEx instanceof IllegalStateException) {
// Current broker still hold the bundle's lock, but the bundle is being unloading.
log.info("Failed to lookup {} for topic {} with error {}", clientAppId, topicName, errorMsg);
lookupFuture.complete(newLookupErrorResponse(ServerError.MetadataError, errorMsg, requestId));
- } else if (unwrapEx instanceof MetadataStoreException){
+ } else if (unwrapEx instanceof MetadataStoreException) {
// Load bundle ownership or acquire lock failed.
// Differ with "IllegalStateException", print warning log.
log.warn("Failed to lookup {} for topic {} with error {}", clientAppId, topicName, errorMsg);
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/namespace/NamespaceService.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/namespace/NamespaceService.java
index 4a54d4e090852..b55eda150afe6 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/namespace/NamespaceService.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/namespace/NamespaceService.java
@@ -192,7 +192,7 @@ public CompletableFuture> getBrokerServiceUrlAsync(TopicN
return findRedirectLookupResultAsync(bundle).thenCompose(optResult -> {
if (optResult.isPresent()) {
LOG.info("[{}] Redirect lookup request to {} for topic {}",
- pulsar.getSafeWebServiceAddress(), optResult.get(), topic);
+ pulsar.getBrokerId(), optResult.get(), topic);
return CompletableFuture.completedFuture(optResult);
}
if (ExtensibleLoadManagerImpl.isLoadManagerExtensionEnabled(config)) {
@@ -298,7 +298,7 @@ private CompletableFuture> internalGetWebServiceUrl(@Nullable Serv
return findRedirectLookupResultAsync(bundle).thenCompose(optResult -> {
if (optResult.isPresent()) {
LOG.info("[{}] Redirect lookup request to {} for topic {}",
- pulsar.getSafeWebServiceAddress(), optResult.get(), topic);
+ pulsar.getBrokerId(), optResult.get(), topic);
try {
LookupData lookupData = optResult.get().getLookupData();
final String redirectUrl = options.isRequestHttps()
@@ -338,17 +338,17 @@ private CompletableFuture> internalGetWebServiceUrl(@Nullable Serv
* @throws PulsarServerException if an unexpected error occurs
*/
public void registerBootstrapNamespaces() throws PulsarServerException {
- String lookupServiceAddress = pulsar.getLookupServiceAddress();
+ String brokerId = pulsar.getBrokerId();
// ensure that we own the heartbeat namespace
- if (registerNamespace(getHeartbeatNamespace(lookupServiceAddress, config), true)) {
+ if (registerNamespace(getHeartbeatNamespace(brokerId, config), true)) {
LOG.info("added heartbeat namespace name in local cache: ns={}",
- getHeartbeatNamespace(lookupServiceAddress, config));
+ getHeartbeatNamespace(brokerId, config));
}
// ensure that we own the heartbeat namespace
- if (registerNamespace(getHeartbeatNamespaceV2(lookupServiceAddress, config), true)) {
+ if (registerNamespace(getHeartbeatNamespaceV2(brokerId, config), true)) {
LOG.info("added heartbeat namespace name in local cache: ns={}",
- getHeartbeatNamespaceV2(lookupServiceAddress, config));
+ getHeartbeatNamespaceV2(brokerId, config));
}
// we may not need strict ownership checking for bootstrap names for now
@@ -506,7 +506,6 @@ private void searchForCandidateBroker(NamespaceBundle bundle,
return;
}
String candidateBroker;
- String candidateBrokerAdvertisedAddr = null;
LeaderElectionService les = pulsar.getLeaderElectionService();
if (les == null) {
@@ -541,14 +540,14 @@ private void searchForCandidateBroker(NamespaceBundle bundle,
if (options.isAuthoritative()) {
// leader broker already assigned the current broker as owner
- candidateBroker = pulsar.getSafeWebServiceAddress();
+ candidateBroker = pulsar.getBrokerId();
} else {
LoadManager loadManager = this.loadManager.get();
boolean makeLoadManagerDecisionOnThisBroker = !loadManager.isCentralized() || les.isLeader();
if (!makeLoadManagerDecisionOnThisBroker) {
// If leader is not active, fallback to pick the least loaded from current broker loadmanager
boolean leaderBrokerActive = currentLeader.isPresent()
- && isBrokerActive(currentLeader.get().getServiceUrl());
+ && isBrokerActive(currentLeader.get().getBrokerId());
if (!leaderBrokerActive) {
makeLoadManagerDecisionOnThisBroker = true;
if (currentLeader.isEmpty()) {
@@ -567,7 +566,7 @@ private void searchForCandidateBroker(NamespaceBundle bundle,
}
}
if (makeLoadManagerDecisionOnThisBroker) {
- Optional> availableBroker = getLeastLoadedFromLoadManager(bundle);
+ Optional availableBroker = getLeastLoadedFromLoadManager(bundle);
if (availableBroker.isEmpty()) {
LOG.warn("Load manager didn't return any available broker. "
+ "Returning empty result to lookup. NamespaceBundle[{}]",
@@ -575,12 +574,11 @@ private void searchForCandidateBroker(NamespaceBundle bundle,
lookupFuture.complete(Optional.empty());
return;
}
- candidateBroker = availableBroker.get().getLeft();
- candidateBrokerAdvertisedAddr = availableBroker.get().getRight();
+ candidateBroker = availableBroker.get();
authoritativeRedirect = true;
} else {
// forward to leader broker to make assignment
- candidateBroker = currentLeader.get().getServiceUrl();
+ candidateBroker = currentLeader.get().getBrokerId();
}
}
}
@@ -593,7 +591,7 @@ private void searchForCandidateBroker(NamespaceBundle bundle,
try {
Objects.requireNonNull(candidateBroker);
- if (candidateBroker.equals(pulsar.getSafeWebServiceAddress())) {
+ if (candidateBroker.equals(pulsar.getBrokerId())) {
// Load manager decided that the local broker should try to become the owner
ownershipCache.tryAcquiringOwnership(bundle).thenAccept(ownerInfo -> {
if (ownerInfo.isDisabled()) {
@@ -644,8 +642,7 @@ private void searchForCandidateBroker(NamespaceBundle bundle,
}
// Now setting the redirect url
- createLookupResult(candidateBrokerAdvertisedAddr == null ? candidateBroker
- : candidateBrokerAdvertisedAddr, authoritativeRedirect, options.getAdvertisedListenerName())
+ createLookupResult(candidateBroker, authoritativeRedirect, options.getAdvertisedListenerName())
.thenAccept(lookupResult -> lookupFuture.complete(Optional.of(lookupResult)))
.exceptionally(ex -> {
lookupFuture.completeExceptionally(ex);
@@ -665,7 +662,7 @@ public CompletableFuture createLookupResult(String candidateBroker
CompletableFuture lookupFuture = new CompletableFuture<>();
try {
checkArgument(StringUtils.isNotBlank(candidateBroker), "Lookup broker can't be null %s", candidateBroker);
- String path = LoadManager.LOADBALANCE_BROKERS_ROOT + "/" + parseHostAndPort(candidateBroker);
+ String path = LoadManager.LOADBALANCE_BROKERS_ROOT + "/" + candidateBroker;
localBrokerDataCache.get(path).thenAccept(reportData -> {
if (reportData.isPresent()) {
@@ -702,29 +699,19 @@ public CompletableFuture createLookupResult(String candidateBroker
}
public boolean isBrokerActive(String candidateBroker) {
- String candidateBrokerHostAndPort = parseHostAndPort(candidateBroker);
Set availableBrokers = getAvailableBrokers();
- if (availableBrokers.contains(candidateBrokerHostAndPort)) {
+ if (availableBrokers.contains(candidateBroker)) {
if (LOG.isDebugEnabled()) {
- LOG.debug("Broker {} ({}) is available for.", candidateBroker, candidateBrokerHostAndPort);
+ LOG.debug("Broker {} is available for.", candidateBroker);
}
return true;
} else {
- LOG.warn("Broker {} ({}) couldn't be found in available brokers {}",
- candidateBroker, candidateBrokerHostAndPort,
- String.join(",", availableBrokers));
+ LOG.warn("Broker {} couldn't be found in available brokers {}",
+ candidateBroker, String.join(",", availableBrokers));
return false;
}
}
- private static String parseHostAndPort(String candidateBroker) {
- int uriSeparatorPos = candidateBroker.indexOf("://");
- if (uriSeparatorPos == -1) {
- throw new IllegalArgumentException("'" + candidateBroker + "' isn't an URI.");
- }
- return candidateBroker.substring(uriSeparatorPos + 3);
- }
-
private Set getAvailableBrokers() {
try {
return loadManager.get().getAvailableBrokers();
@@ -740,7 +727,7 @@ private Set getAvailableBrokers() {
* @return the least loaded broker addresses
* @throws Exception if an error occurs
*/
- private Optional> getLeastLoadedFromLoadManager(ServiceUnitId serviceUnit) throws Exception {
+ private Optional getLeastLoadedFromLoadManager(ServiceUnitId serviceUnit) throws Exception {
Optional leastLoadedBroker = loadManager.get().getLeastLoaded(serviceUnit);
if (leastLoadedBroker.isEmpty()) {
LOG.warn("No broker is available for {}", serviceUnit);
@@ -748,15 +735,13 @@ private Optional> getLeastLoadedFromLoadManager(ServiceUnit
}
String lookupAddress = leastLoadedBroker.get().getResourceId();
- String advertisedAddr = (String) leastLoadedBroker.get()
- .getProperty(ResourceUnit.PROPERTY_KEY_BROKER_ZNODE_NAME);
if (LOG.isDebugEnabled()) {
LOG.debug("{} : redirecting to the least loaded broker, lookup address={}",
- pulsar.getSafeWebServiceAddress(),
+ pulsar.getBrokerId(),
lookupAddress);
}
- return Optional.of(Pair.of(lookupAddress, advertisedAddr));
+ return Optional.of(lookupAddress);
}
public CompletableFuture unloadNamespaceBundle(NamespaceBundle bundle) {
@@ -1447,6 +1432,9 @@ private CompletableFuture> getPartitionsForTopic(TopicName topicNam
});
}
+ /***
+ * List persistent topics names under a namespace, the topic name contains the partition suffix.
+ */
public CompletableFuture> getListOfPersistentTopics(NamespaceName namespaceName) {
return pulsar.getPulsarResources().getTopicResources().listPersistentTopicsAsync(namespaceName);
}
@@ -1564,7 +1552,7 @@ public CompletableFuture checkOwnershipPresentAsync(NamespaceBundle bun
}
public void unloadSLANamespace() throws Exception {
- NamespaceName namespaceName = getSLAMonitorNamespace(pulsar.getLookupServiceAddress(), config);
+ NamespaceName namespaceName = getSLAMonitorNamespace(pulsar.getBrokerId(), config);
LOG.info("Checking owner for SLA namespace {}", namespaceName);
@@ -1597,7 +1585,7 @@ public static String checkHeartbeatNamespace(ServiceUnitId ns) {
Matcher m = HEARTBEAT_NAMESPACE_PATTERN.matcher(ns.getNamespaceObject().toString());
if (m.matches()) {
LOG.debug("Heartbeat namespace matched the lookup namespace {}", ns.getNamespaceObject().toString());
- return String.format("http://%s", m.group(1));
+ return m.group(1);
} else {
return null;
}
@@ -1607,7 +1595,7 @@ public static String checkHeartbeatNamespaceV2(ServiceUnitId ns) {
Matcher m = HEARTBEAT_NAMESPACE_PATTERN_V2.matcher(ns.getNamespaceObject().toString());
if (m.matches()) {
LOG.debug("Heartbeat namespace v2 matched the lookup namespace {}", ns.getNamespaceObject().toString());
- return String.format("http://%s", m.group(1));
+ return m.group(1);
} else {
return null;
}
@@ -1616,7 +1604,7 @@ public static String checkHeartbeatNamespaceV2(ServiceUnitId ns) {
public static String getSLAMonitorBrokerName(ServiceUnitId ns) {
Matcher m = SLA_NAMESPACE_PATTERN.matcher(ns.getNamespaceObject().toString());
if (m.matches()) {
- return String.format("http://%s", m.group(1));
+ return m.group(1);
} else {
return null;
}
@@ -1647,16 +1635,16 @@ public static boolean isHeartbeatNamespace(ServiceUnitId ns) {
}
public boolean registerSLANamespace() throws PulsarServerException {
- String lookupServiceAddress = pulsar.getLookupServiceAddress();
- boolean isNameSpaceRegistered = registerNamespace(getSLAMonitorNamespace(lookupServiceAddress, config), false);
+ String brokerId = pulsar.getBrokerId();
+ boolean isNameSpaceRegistered = registerNamespace(getSLAMonitorNamespace(brokerId, config), false);
if (isNameSpaceRegistered) {
if (LOG.isDebugEnabled()) {
LOG.debug("Added SLA Monitoring namespace name in local cache: ns={}",
- getSLAMonitorNamespace(lookupServiceAddress, config));
+ getSLAMonitorNamespace(brokerId, config));
}
} else if (LOG.isDebugEnabled()) {
LOG.debug("SLA Monitoring not owned by the broker: ns={}",
- getSLAMonitorNamespace(lookupServiceAddress, config));
+ getSLAMonitorNamespace(brokerId, config));
}
return isNameSpaceRegistered;
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/protocol/ProtocolHandlers.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/protocol/ProtocolHandlers.java
index 42a82b2de762b..4059ccf5f26eb 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/protocol/ProtocolHandlers.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/protocol/ProtocolHandlers.java
@@ -24,6 +24,7 @@
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -51,6 +52,9 @@ public class ProtocolHandlers implements AutoCloseable {
* @return the collection of protocol handlers
*/
public static ProtocolHandlers load(ServiceConfiguration conf) throws IOException {
+ if (conf.getMessagingProtocols().isEmpty()) {
+ return new ProtocolHandlers(Collections.emptyMap());
+ }
ProtocolHandlerDefinitions definitions =
ProtocolHandlerUtils.searchForHandlers(
conf.getProtocolHandlerDirectory(), conf.getNarExtractionDirectory());
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/AbstractReplicator.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/AbstractReplicator.java
index 6dd296d16b53b..1b5b2824257b0 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/AbstractReplicator.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/AbstractReplicator.java
@@ -194,7 +194,7 @@ protected synchronized CompletableFuture closeProducerAsync() {
return CompletableFuture.completedFuture(null);
}
CompletableFuture future = producer.closeAsync();
- future.thenRun(() -> {
+ return future.thenRun(() -> {
STATE_UPDATER.set(this, State.Stopped);
this.producer = null;
// deactivate further read
@@ -209,7 +209,6 @@ protected synchronized CompletableFuture closeProducerAsync() {
brokerService.executor().schedule(this::closeProducerAsync, waitTimeMs, TimeUnit.MILLISECONDS);
return null;
});
- return future;
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BrokerService.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BrokerService.java
index 4077762bb0640..6363e197616ca 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BrokerService.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BrokerService.java
@@ -123,8 +123,6 @@
import org.apache.pulsar.broker.service.persistent.PersistentTopic;
import org.apache.pulsar.broker.service.persistent.SystemTopic;
import org.apache.pulsar.broker.service.plugin.EntryFilterProvider;
-import org.apache.pulsar.broker.service.schema.BookkeeperSchemaStorage;
-import org.apache.pulsar.broker.service.schema.SchemaRegistryService;
import org.apache.pulsar.broker.stats.ClusterReplicationMetrics;
import org.apache.pulsar.broker.stats.prometheus.metrics.ObserverGauge;
import org.apache.pulsar.broker.stats.prometheus.metrics.Summary;
@@ -375,8 +373,8 @@ public BrokerService(PulsarService pulsar, EventLoopGroup eventLoopGroup) throws
if (pulsar.getConfiguration().getMaxUnackedMessagesPerBroker() > 0
&& pulsar.getConfiguration().getMaxUnackedMessagesPerSubscriptionOnBrokerBlocked() > 0.0) {
this.maxUnackedMessages = pulsar.getConfiguration().getMaxUnackedMessagesPerBroker();
- this.maxUnackedMsgsPerDispatcher = (int) ((maxUnackedMessages
- * pulsar.getConfiguration().getMaxUnackedMessagesPerSubscriptionOnBrokerBlocked()) / 100);
+ this.maxUnackedMsgsPerDispatcher = (int) (maxUnackedMessages
+ * pulsar.getConfiguration().getMaxUnackedMessagesPerSubscriptionOnBrokerBlocked());
log.info("Enabling per-broker unack-message limit {} and dispatcher-limit {} on blocked-broker",
maxUnackedMessages, maxUnackedMsgsPerDispatcher);
// block misbehaving dispatcher by checking periodically
@@ -961,43 +959,49 @@ public CompletableFuture> getTopic(final TopicName topicName, bo
}
final boolean isPersistentTopic = topicName.getDomain().equals(TopicDomain.persistent);
if (isPersistentTopic) {
- final CompletableFuture> topicPoliciesFuture =
- getTopicPoliciesBypassSystemTopic(topicName);
- return topicPoliciesFuture.exceptionally(ex -> {
- final Throwable rc = FutureUtil.unwrapCompletionException(ex);
- final String errorInfo = String.format("Topic creation encountered an exception by initialize"
- + " topic policies service. topic_name=%s error_message=%s", topicName, rc.getMessage());
- log.error(errorInfo, rc);
- throw FutureUtil.wrapToCompletionException(new ServiceUnitNotReadyException(errorInfo));
- }).thenCompose(optionalTopicPolicies -> {
- final TopicPolicies topicPolicies = optionalTopicPolicies.orElse(null);
- return topics.computeIfAbsent(topicName.toString(), (tpName) -> {
- if (topicName.isPartitioned()) {
- final TopicName topicNameEntity = TopicName.get(topicName.getPartitionedTopicName());
- return fetchPartitionedTopicMetadataAsync(topicNameEntity)
- .thenCompose((metadata) -> {
- // Allow crate non-partitioned persistent topic that name includes `partition`
- if (metadata.partitions == 0
- || topicName.getPartitionIndex() < metadata.partitions) {
- return loadOrCreatePersistentTopic(tpName, createIfMissing,
- properties, topicPolicies);
- }
- final String errorMsg =
- String.format("Illegal topic partition name %s with max allowed "
- + "%d partitions", topicName, metadata.partitions);
- log.warn(errorMsg);
- return FutureUtil
- .failedFuture(new BrokerServiceException.NotAllowedException(errorMsg));
- });
- }
- return loadOrCreatePersistentTopic(tpName, createIfMissing, properties, topicPolicies);
- }).thenCompose(optionalTopic -> {
- if (!optionalTopic.isPresent() && createIfMissing) {
- log.warn("[{}] Try to recreate the topic with createIfMissing=true "
- + "but the returned topic is empty", topicName);
- return getTopic(topicName, createIfMissing, properties);
- }
- return CompletableFuture.completedFuture(optionalTopic);
+ return pulsar.getPulsarResources().getTopicResources().persistentTopicExists(topicName)
+ .thenCompose(exists -> {
+ if (!exists && !createIfMissing) {
+ return CompletableFuture.completedFuture(Optional.empty());
+ }
+ return getTopicPoliciesBypassSystemTopic(topicName).exceptionally(ex -> {
+ final Throwable rc = FutureUtil.unwrapCompletionException(ex);
+ final String errorInfo = String.format("Topic creation encountered an exception by initialize"
+ + " topic policies service. topic_name=%s error_message=%s", topicName,
+ rc.getMessage());
+ log.error(errorInfo, rc);
+ throw FutureUtil.wrapToCompletionException(new ServiceUnitNotReadyException(errorInfo));
+ }).thenCompose(optionalTopicPolicies -> {
+ final TopicPolicies topicPolicies = optionalTopicPolicies.orElse(null);
+ return topics.computeIfAbsent(topicName.toString(), (tpName) -> {
+ if (topicName.isPartitioned()) {
+ final TopicName topicNameEntity = TopicName.get(topicName.getPartitionedTopicName());
+ return fetchPartitionedTopicMetadataAsync(topicNameEntity)
+ .thenCompose((metadata) -> {
+ // Allow crate non-partitioned persistent topic that name includes
+ // `partition`
+ if (metadata.partitions == 0
+ || topicName.getPartitionIndex() < metadata.partitions) {
+ return loadOrCreatePersistentTopic(tpName, createIfMissing,
+ properties, topicPolicies);
+ }
+ final String errorMsg =
+ String.format("Illegal topic partition name %s with max allowed "
+ + "%d partitions", topicName, metadata.partitions);
+ log.warn(errorMsg);
+ return FutureUtil.failedFuture(
+ new BrokerServiceException.NotAllowedException(errorMsg));
+ });
+ }
+ return loadOrCreatePersistentTopic(tpName, createIfMissing, properties, topicPolicies);
+ }).thenCompose(optionalTopic -> {
+ if (!optionalTopic.isPresent() && createIfMissing) {
+ log.warn("[{}] Try to recreate the topic with createIfMissing=true "
+ + "but the returned topic is empty", topicName);
+ return getTopic(topicName, createIfMissing, properties);
+ }
+ return CompletableFuture.completedFuture(optionalTopic);
+ });
});
});
} else {
@@ -1773,10 +1777,17 @@ private CompletableFuture getManagedLedgerConfig(@Nonnull T
}
if (retentionPolicies == null) {
- retentionPolicies = policies.map(p -> p.retention_policies).orElseGet(
- () -> new RetentionPolicies(serviceConfig.getDefaultRetentionTimeInMinutes(),
- serviceConfig.getDefaultRetentionSizeInMB())
- );
+ if (SystemTopicNames.isSystemTopic(topicName)) {
+ if (log.isDebugEnabled()) {
+ log.debug("{} Disable data retention policy for system topic.", topicName);
+ }
+ retentionPolicies = new RetentionPolicies(0, 0);
+ } else {
+ retentionPolicies = policies.map(p -> p.retention_policies).orElseGet(
+ () -> new RetentionPolicies(serviceConfig.getDefaultRetentionTimeInMinutes(),
+ serviceConfig.getDefaultRetentionSizeInMB())
+ );
+ }
}
ManagedLedgerConfig managedLedgerConfig = new ManagedLedgerConfig();
@@ -2112,7 +2123,7 @@ public CompletableFuture checkTopicNsOwnership(final String topic) {
} else {
String msg = String.format("Namespace bundle for topic (%s) not served by this instance:%s. "
+ "Please redo the lookup. Request is denied: namespace=%s",
- topic, pulsar.getLookupServiceAddress(), topicName.getNamespace());
+ topic, pulsar.getBrokerId(), topicName.getNamespace());
log.warn(msg);
return FutureUtil.failedFuture(new ServiceUnitNotReadyException(msg));
}
@@ -3447,22 +3458,21 @@ public CompletableFuture deleteTopicPolicies(TopicName topicName) {
}
public CompletableFuture deleteSchema(TopicName topicName) {
+ // delete schema at the upper level when deleting the partitioned topic.
+ if (topicName.isPartitioned()) {
+ return CompletableFuture.completedFuture(null);
+ }
String base = topicName.getPartitionedTopicName();
String id = TopicName.get(base).getSchemaName();
- SchemaRegistryService schemaRegistryService = getPulsar().getSchemaRegistryService();
- return BookkeeperSchemaStorage.ignoreUnrecoverableBKException(schemaRegistryService.getSchema(id))
- .thenCompose(schema -> {
- if (schema != null) {
- // It's different from `SchemasResource.deleteSchema`
- // because when we delete a topic, the schema
- // history is meaningless. But when we delete a schema of a topic, a new schema could be
- // registered in the future.
- log.info("Delete schema storage of id: {}", id);
- return getPulsar().getSchemaRegistryService().deleteSchemaStorage(id);
- } else {
- return CompletableFuture.completedFuture(null);
- }
- });
+ return getPulsar().getSchemaRegistryService().deleteSchemaStorage(id).whenComplete((vid, ex) -> {
+ if (vid != null && ex == null) {
+ // It's different from `SchemasResource.deleteSchema`
+ // because when we delete a topic, the schema
+ // history is meaningless. But when we delete a schema of a topic, a new schema could be
+ // registered in the future.
+ log.info("Deleted schema storage of id: {}", id);
+ }
+ });
}
private CompletableFuture checkMaxTopicsPerNamespace(TopicName topicName, int numPartitions) {
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/ConsistentHashingStickyKeyConsumerSelector.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/ConsistentHashingStickyKeyConsumerSelector.java
index ea491bd40d332..b2b2b512c8cfc 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/ConsistentHashingStickyKeyConsumerSelector.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/ConsistentHashingStickyKeyConsumerSelector.java
@@ -39,7 +39,8 @@
* number of keys assigned to each consumer.
*/
public class ConsistentHashingStickyKeyConsumerSelector implements StickyKeyConsumerSelector {
-
+ // use NUL character as field separator for hash key calculation
+ private static final String KEY_SEPARATOR = "\0";
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
// Consistent-Hash ring
@@ -59,8 +60,7 @@ public CompletableFuture addConsumer(Consumer consumer) {
// Insert multiple points on the hash ring for every consumer
// The points are deterministically added based on the hash of the consumer name
for (int i = 0; i < numberOfPoints; i++) {
- String key = consumer.consumerName() + i;
- int hash = Murmur3_32Hash.getInstance().makeHash(key.getBytes());
+ int hash = calculateHashForConsumerAndIndex(consumer, i);
hashRing.compute(hash, (k, v) -> {
if (v == null) {
return Lists.newArrayList(consumer);
@@ -79,14 +79,18 @@ public CompletableFuture addConsumer(Consumer consumer) {
}
}
+ private static int calculateHashForConsumerAndIndex(Consumer consumer, int index) {
+ String key = consumer.consumerName() + KEY_SEPARATOR + index;
+ return Murmur3_32Hash.getInstance().makeHash(key.getBytes());
+ }
+
@Override
public void removeConsumer(Consumer consumer) {
rwLock.writeLock().lock();
try {
// Remove all the points that were added for this consumer
for (int i = 0; i < numberOfPoints; i++) {
- String key = consumer.consumerName() + i;
- int hash = Murmur3_32Hash.getInstance().makeHash(key.getBytes());
+ int hash = calculateHashForConsumerAndIndex(consumer, i);
hashRing.compute(hash, (k, v) -> {
if (v == null) {
return null;
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/Consumer.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/Consumer.java
index 83dcd8d6c1616..4cd54420200be 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/Consumer.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/Consumer.java
@@ -324,7 +324,7 @@ public Future sendMessages(final List extends Entry> entries, EntryBatch
if (pendingAcks != null) {
int batchSize = batchSizes.getBatchSize(i);
int stickyKeyHash = getStickyKeyHash(entry);
- long[] ackSet = getCursorAckSet(PositionImpl.get(entry.getLedgerId(), entry.getEntryId()));
+ long[] ackSet = batchIndexesAcks == null ? null : batchIndexesAcks.getAckSet(i);
if (ackSet != null) {
unackedMessages -= (batchSize - BitSet.valueOf(ackSet).cardinality());
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/PulsarCommandSenderImpl.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/PulsarCommandSenderImpl.java
index dd74fc4e71ed2..105650caaaf13 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/PulsarCommandSenderImpl.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/PulsarCommandSenderImpl.java
@@ -356,12 +356,18 @@ public void sendEndTxnErrorResponse(long requestId, TxnID txnID, ServerError err
writeAndFlush(outBuf);
}
+ /***
+ * @param topics topic names which are matching, the topic name contains the partition suffix.
+ */
@Override
public void sendWatchTopicListSuccess(long requestId, long watcherId, String topicsHash, List topics) {
BaseCommand command = Commands.newWatchTopicListSuccess(requestId, watcherId, topicsHash, topics);
interceptAndWriteCommand(command);
}
+ /***
+ * {@inheritDoc}
+ */
@Override
public void sendWatchTopicListUpdate(long watcherId,
List newTopics, List deletedTopics, String topicsHash) {
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/ServerCnx.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/ServerCnx.java
index 9f2b98aeb40d9..0d9b5ea73e0c9 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/ServerCnx.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/ServerCnx.java
@@ -2170,7 +2170,8 @@ protected void handleGetLastMessageId(CommandGetLastMessageId getLastMessageId)
(PositionImpl) markDeletePosition,
partitionIndex,
requestId,
- consumer.getSubscription().getName());
+ consumer.getSubscription().getName(),
+ consumer.readCompacted());
}).exceptionally(e -> {
writeAndFlush(Commands.newError(getLastMessageId.getRequestId(),
ServerError.UnknownError, "Failed to recover Transaction Buffer."));
@@ -2188,15 +2189,17 @@ private void getLargestBatchIndexWhenPossible(
PositionImpl markDeletePosition,
int partitionIndex,
long requestId,
- String subscriptionName) {
+ String subscriptionName,
+ boolean readCompacted) {
PersistentTopic persistentTopic = (PersistentTopic) topic;
ManagedLedgerImpl ml = (ManagedLedgerImpl) persistentTopic.getManagedLedger();
// If it's not pointing to a valid entry, respond messageId of the current position.
// If the compaction cursor reach the end of the topic, respond messageId from compacted ledger
- CompletableFuture compactionHorizonFuture =
- persistentTopic.getTopicCompactionService().getLastCompactedPosition();
+ CompletableFuture compactionHorizonFuture = readCompacted
+ ? persistentTopic.getTopicCompactionService().getLastCompactedPosition() :
+ CompletableFuture.completedFuture(null);
compactionHorizonFuture.whenComplete((compactionHorizon, ex) -> {
if (ex != null) {
@@ -2205,8 +2208,22 @@ private void getLargestBatchIndexWhenPossible(
return;
}
- if (lastPosition.getEntryId() == -1 || (compactionHorizon != null
- && lastPosition.compareTo((PositionImpl) compactionHorizon) <= 0)) {
+ if (lastPosition.getEntryId() == -1 || !ml.ledgerExists(lastPosition.getLedgerId())) {
+ // there is no entry in the original topic
+ if (compactionHorizon != null) {
+ // if readCompacted is true, we need to read the last entry from compacted topic
+ handleLastMessageIdFromCompactionService(persistentTopic, requestId, partitionIndex,
+ markDeletePosition);
+ } else {
+ // if readCompacted is false, we need to return MessageId.earliest
+ writeAndFlush(Commands.newGetLastMessageIdResponse(requestId, -1, -1, partitionIndex, -1,
+ markDeletePosition != null ? markDeletePosition.getLedgerId() : -1,
+ markDeletePosition != null ? markDeletePosition.getEntryId() : -1));
+ }
+ return;
+ }
+
+ if (compactionHorizon != null && lastPosition.compareTo((PositionImpl) compactionHorizon) <= 0) {
handleLastMessageIdFromCompactionService(persistentTopic, requestId, partitionIndex,
markDeletePosition);
return;
@@ -2241,7 +2258,8 @@ public String toString() {
batchSizeFuture.whenComplete((batchSize, e) -> {
if (e != null) {
- if (e.getCause() instanceof ManagedLedgerException.NonRecoverableLedgerException) {
+ if (e.getCause() instanceof ManagedLedgerException.NonRecoverableLedgerException
+ && readCompacted) {
handleLastMessageIdFromCompactionService(persistentTopic, requestId, partitionIndex,
markDeletePosition);
} else {
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/SystemTopicBasedTopicPoliciesService.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/SystemTopicBasedTopicPoliciesService.java
index 80fecbe67b646..71f78e21f938f 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/SystemTopicBasedTopicPoliciesService.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/SystemTopicBasedTopicPoliciesService.java
@@ -19,6 +19,8 @@
package org.apache.pulsar.broker.service;
import static java.util.Objects.requireNonNull;
+import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
+import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
import java.util.HashSet;
@@ -29,6 +31,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nonnull;
import org.apache.commons.lang3.tuple.MutablePair;
@@ -84,10 +87,25 @@ public class SystemTopicBasedTopicPoliciesService implements TopicPoliciesServic
@VisibleForTesting
final Map>> listeners = new ConcurrentHashMap<>();
+ private final AsyncLoadingCache> writerCaches;
+
public SystemTopicBasedTopicPoliciesService(PulsarService pulsarService) {
this.pulsarService = pulsarService;
this.clusterName = pulsarService.getConfiguration().getClusterName();
this.localCluster = Sets.newHashSet(clusterName);
+ this.writerCaches = Caffeine.newBuilder()
+ .expireAfterAccess(5, TimeUnit.MINUTES)
+ .removalListener((namespaceName, writer, cause) -> {
+ ((SystemTopicClient.Writer) writer).closeAsync().exceptionally(ex -> {
+ log.error("[{}] Close writer error.", namespaceName, ex);
+ return null;
+ });
+ })
+ .buildAsync((namespaceName, executor) -> {
+ SystemTopicClient systemTopicClient = namespaceEventsSystemTopicFactory
+ .createTopicPoliciesSystemTopicClient(namespaceName);
+ return systemTopicClient.newWriterAsync();
+ });
}
@Override
@@ -122,39 +140,32 @@ private CompletableFuture sendTopicPolicyEvent(TopicName topicName, Action
} catch (PulsarServerException e) {
return CompletableFuture.failedFuture(e);
}
-
- SystemTopicClient systemTopicClient = namespaceEventsSystemTopicFactory
- .createTopicPoliciesSystemTopicClient(topicName.getNamespaceObject());
-
- return systemTopicClient.newWriterAsync()
- .thenCompose(writer -> {
- PulsarEvent event = getPulsarEvent(topicName, actionType, policies);
- CompletableFuture writeFuture =
- ActionType.DELETE.equals(actionType) ? writer.deleteAsync(getEventKey(event), event)
- : writer.writeAsync(getEventKey(event), event);
- return writeFuture.handle((messageId, e) -> {
- if (e != null) {
- return CompletableFuture.failedFuture(e);
+ CompletableFuture result = new CompletableFuture<>();
+ writerCaches.get(topicName.getNamespaceObject())
+ .whenComplete((writer, cause) -> {
+ if (cause != null) {
+ writerCaches.synchronous().invalidate(topicName.getNamespaceObject());
+ result.completeExceptionally(cause);
} else {
- if (messageId != null) {
- return CompletableFuture.completedFuture(null);
- } else {
- return CompletableFuture.failedFuture(
- new RuntimeException("Got message id is null."));
- }
- }
- }).thenRun(() ->
- writer.closeAsync().whenComplete((v, cause) -> {
- if (cause != null) {
- log.error("[{}] Close writer error.", topicName, cause);
+ PulsarEvent event = getPulsarEvent(topicName, actionType, policies);
+ CompletableFuture writeFuture = ActionType.DELETE.equals(actionType)
+ ? writer.deleteAsync(getEventKey(event), event)
+ : writer.writeAsync(getEventKey(event), event);
+ writeFuture.whenComplete((messageId, e) -> {
+ if (e != null) {
+ result.completeExceptionally(e);
+ } else {
+ if (messageId != null) {
+ result.complete(null);
} else {
- if (log.isDebugEnabled()) {
- log.debug("[{}] Close writer success.", topicName);
- }
+ result.completeExceptionally(
+ new RuntimeException("Got message id is null."));
}
- })
- );
+ }
+ });
+ }
});
+ return result;
});
}
@@ -364,7 +375,7 @@ public CompletableFuture removeOwnedNamespaceBundleAsync(NamespaceBundle n
}
AtomicInteger bundlesCount = ownedBundlesCountPerNamespace.get(namespace);
if (bundlesCount == null || bundlesCount.decrementAndGet() <= 0) {
- cleanCacheAndCloseReader(namespace, true);
+ cleanCacheAndCloseReader(namespace, true, true);
}
return CompletableFuture.completedFuture(null);
}
@@ -440,6 +451,14 @@ private void initPolicesCache(SystemTopicClient.Reader reader, Comp
}
private void cleanCacheAndCloseReader(@Nonnull NamespaceName namespace, boolean cleanOwnedBundlesCount) {
+ cleanCacheAndCloseReader(namespace, cleanOwnedBundlesCount, false);
+ }
+
+ private void cleanCacheAndCloseReader(@Nonnull NamespaceName namespace, boolean cleanOwnedBundlesCount,
+ boolean cleanWriterCache) {
+ if (cleanWriterCache) {
+ writerCaches.synchronous().invalidate(namespace);
+ }
CompletableFuture> readerFuture = readerCaches.remove(namespace);
if (cleanOwnedBundlesCount) {
@@ -688,5 +707,10 @@ protected Map>> getListeners(
return listeners;
}
+ @VisibleForTesting
+ protected AsyncLoadingCache> getWriterCaches() {
+ return writerCaches;
+ }
+
private static final Logger log = LoggerFactory.getLogger(SystemTopicBasedTopicPoliciesService.class);
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/TopicListService.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/TopicListService.java
index 7aa50057d73c9..aea5b9fc65b46 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/TopicListService.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/TopicListService.java
@@ -31,6 +31,7 @@
import org.apache.pulsar.common.api.proto.CommandWatchTopicListClose;
import org.apache.pulsar.common.api.proto.ServerError;
import org.apache.pulsar.common.naming.NamespaceName;
+import org.apache.pulsar.common.naming.TopicName;
import org.apache.pulsar.common.topics.TopicList;
import org.apache.pulsar.common.util.collections.ConcurrentLongHashMap;
import org.apache.pulsar.metadata.api.NotificationType;
@@ -42,11 +43,16 @@ public class TopicListService {
public static class TopicListWatcher implements BiConsumer {
+ /** Topic names which are matching, the topic name contains the partition suffix. **/
private final List matchingTopics;
private final TopicListService topicListService;
private final long id;
+ /** The regexp for the topic name(not contains partition suffix). **/
private final Pattern topicsPattern;
+ /***
+ * @param topicsPattern The regexp for the topic name(not contains partition suffix).
+ */
public TopicListWatcher(TopicListService topicListService, long id,
Pattern topicsPattern, List topics) {
this.topicListService = topicListService;
@@ -59,9 +65,12 @@ public List getMatchingTopics() {
return matchingTopics;
}
+ /***
+ * @param topicName topic name which contains partition suffix.
+ */
@Override
public void accept(String topicName, NotificationType notificationType) {
- if (topicsPattern.matcher(topicName).matches()) {
+ if (topicsPattern.matcher(TopicName.get(topicName).getPartitionedTopicName()).matches()) {
List newTopics;
List deletedTopics;
if (notificationType == NotificationType.Deleted) {
@@ -109,6 +118,9 @@ public void inactivate() {
}
}
+ /***
+ * @param topicsPattern The regexp for the topic name(not contains partition suffix).
+ */
public void handleWatchTopicList(NamespaceName namespaceName, long watcherId, long requestId, Pattern topicsPattern,
String topicsHash, Semaphore lookupSemaphore) {
@@ -184,7 +196,9 @@ public void handleWatchTopicList(NamespaceName namespaceName, long watcherId, lo
});
}
-
+ /***
+ * @param topicsPattern The regexp for the topic name(not contains partition suffix).
+ */
public void initializeTopicsListWatcher(CompletableFuture watcherFuture,
NamespaceName namespace, long watcherId, Pattern topicsPattern) {
namespaceService.getListOfPersistentTopics(namespace).
@@ -246,6 +260,10 @@ public void deleteTopicListWatcher(Long watcherId) {
log.info("[{}] Closed watcher, watcherId={}", connection.getRemoteAddress(), watcherId);
}
+ /**
+ * @param deletedTopics topic names deleted(contains the partition suffix).
+ * @param newTopics topics names added(contains the partition suffix).
+ */
public void sendTopicListUpdate(long watcherId, String topicsHash, List deletedTopics,
List newTopics) {
connection.getCommandSender().sendWatchTopicListUpdate(watcherId, newTopics, deletedTopics, topicsHash);
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/nonpersistent/NonPersistentDispatcherMultipleConsumers.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/nonpersistent/NonPersistentDispatcherMultipleConsumers.java
index 29bca715741ad..399a524a197e9 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/nonpersistent/NonPersistentDispatcherMultipleConsumers.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/nonpersistent/NonPersistentDispatcherMultipleConsumers.java
@@ -191,7 +191,7 @@ public RedeliveryTracker getRedeliveryTracker() {
}
@Override
- public void sendMessages(List entries) {
+ public synchronized void sendMessages(List entries) {
Consumer consumer = TOTAL_AVAILABLE_PERMITS_UPDATER.get(this) > 0 ? getNextConsumer() : null;
if (consumer != null) {
SendMessageInfo sendMessageInfo = SendMessageInfo.getThreadLocal();
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/nonpersistent/NonPersistentTopic.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/nonpersistent/NonPersistentTopic.java
index 00cf3a6583b9a..2fa85f262de37 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/nonpersistent/NonPersistentTopic.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/nonpersistent/NonPersistentTopic.java
@@ -968,7 +968,7 @@ public CompletableFuture extends TopicStatsImpl> asyncGetStats(GetStatsOptions
});
stats.topicEpoch = topicEpoch.orElse(null);
- stats.ownerBroker = brokerService.pulsar().getLookupServiceAddress();
+ stats.ownerBroker = brokerService.pulsar().getBrokerId();
future.complete(stats);
return future;
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/GeoPersistentReplicator.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/GeoPersistentReplicator.java
index 191b19e317163..4ef2710fea61a 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/GeoPersistentReplicator.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/GeoPersistentReplicator.java
@@ -123,18 +123,6 @@ protected boolean replicateEntries(List entries) {
continue;
}
- if (msg.isExpired(messageTTLInSeconds)) {
- msgExpired.recordEvent(0 /* no value stat */);
- if (log.isDebugEnabled()) {
- log.debug("[{}] Discarding expired message at position {}, replicateTo {}",
- replicatorId, entry.getPosition(), msg.getReplicateTo());
- }
- cursor.asyncDelete(entry.getPosition(), this, entry.getPosition());
- entry.release();
- msg.recycle();
- continue;
- }
-
if (STATE_UPDATER.get(this) != State.Started || isLocalMessageSkippedOnce) {
// The producer is not ready yet after having stopped/restarted. Drop the message because it will
// recovered when the producer is ready
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentDispatcherSingleActiveConsumer.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentDispatcherSingleActiveConsumer.java
index 806773af45189..387ba83d9cd62 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentDispatcherSingleActiveConsumer.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentDispatcherSingleActiveConsumer.java
@@ -54,6 +54,7 @@
import org.apache.pulsar.common.api.proto.CommandSubscribe.SubType;
import org.apache.pulsar.common.util.Codec;
import org.apache.pulsar.compaction.CompactedTopicUtils;
+import org.apache.pulsar.compaction.Compactor;
import org.apache.pulsar.compaction.TopicCompactionService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -107,9 +108,9 @@ protected void scheduleReadOnActiveConsumer() {
if (log.isDebugEnabled()) {
log.debug("[{}] Rewind cursor and read more entries without delay", name);
}
- cursor.rewind();
-
Consumer activeConsumer = ACTIVE_CONSUMER_UPDATER.get(this);
+ cursor.rewind(activeConsumer != null && activeConsumer.readCompacted());
+
notifyActiveConsumerChanged(activeConsumer);
readMoreEntries(activeConsumer);
return;
@@ -127,9 +128,9 @@ protected void scheduleReadOnActiveConsumer() {
log.debug("[{}] Rewind cursor and read more entries after {} ms delay", name,
serviceConfig.getActiveConsumerFailoverDelayTimeMillis());
}
- cursor.rewind();
-
Consumer activeConsumer = ACTIVE_CONSUMER_UPDATER.get(this);
+ cursor.rewind(activeConsumer != null && activeConsumer.readCompacted());
+
notifyActiveConsumerChanged(activeConsumer);
readMoreEntries(activeConsumer);
readOnActiveConsumerTask = null;
@@ -206,7 +207,7 @@ private synchronized void internalReadEntriesComplete(final List entries,
}
}
entries.forEach(Entry::release);
- cursor.rewind();
+ cursor.rewind(currentConsumer != null ? currentConsumer.readCompacted() : readConsumer.readCompacted());
if (currentConsumer != null) {
notifyActiveConsumerChanged(currentConsumer);
readMoreEntries(currentConsumer);
@@ -301,7 +302,7 @@ private synchronized void internalRedeliverUnacknowledgedMessages(Consumer consu
}
cursor.cancelPendingReadRequest();
havePendingRead = false;
- cursor.rewind();
+ cursor.rewind(consumer.readCompacted());
if (log.isDebugEnabled()) {
log.debug("[{}-{}] Cursor rewinded, redelivering unacknowledged messages. ", name, consumer);
}
@@ -360,7 +361,9 @@ private void readMoreEntries(Consumer consumer) {
}
havePendingRead = true;
if (consumer.readCompacted()) {
- boolean readFromEarliest = isFirstRead && MessageId.earliest.equals(consumer.getStartMessageId());
+ boolean readFromEarliest = isFirstRead && MessageId.earliest.equals(consumer.getStartMessageId())
+ && (!cursor.isDurable() || cursor.getName().equals(Compactor.COMPACTION_SUBSCRIPTION)
+ || hasValidMarkDeletePosition(cursor));
TopicCompactionService topicCompactionService = topic.getTopicCompactionService();
CompactedTopicUtils.asyncReadCompactedEntries(topicCompactionService, cursor, messagesToRead,
bytesToRead, topic.getMaxReadPosition(), readFromEarliest, this, true, consumer);
@@ -378,6 +381,13 @@ private void readMoreEntries(Consumer consumer) {
}
}
+ private boolean hasValidMarkDeletePosition(ManagedCursor cursor) {
+ // If `markDeletedPosition.entryID == -1L` then the md-position is an invalid position,
+ // since the initial md-position of the consumer will be set to it.
+ // See ManagedLedgerImpl#asyncOpenCursor and ManagedLedgerImpl#getFirstPosition
+ return cursor.getMarkDeletedPosition() != null && cursor.getMarkDeletedPosition().getEntryId() == -1L;
+ }
+
@Override
protected void reScheduleRead() {
if (isRescheduleReadInProgress.compareAndSet(false, true)) {
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentMessageExpiryMonitor.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentMessageExpiryMonitor.java
index 020dc5323e55b..ac391c1050340 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentMessageExpiryMonitor.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentMessageExpiryMonitor.java
@@ -18,8 +18,10 @@
*/
package org.apache.pulsar.broker.service.persistent;
+import com.google.common.annotations.VisibleForTesting;
import java.util.Objects;
import java.util.Optional;
+import java.util.SortedMap;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.LongAdder;
import javax.annotation.Nullable;
@@ -30,8 +32,10 @@
import org.apache.bookkeeper.mledger.ManagedLedgerException.LedgerNotExistException;
import org.apache.bookkeeper.mledger.ManagedLedgerException.NonRecoverableLedgerException;
import org.apache.bookkeeper.mledger.Position;
+import org.apache.bookkeeper.mledger.impl.ManagedCursorImpl;
import org.apache.bookkeeper.mledger.impl.ManagedLedgerImpl;
import org.apache.bookkeeper.mledger.impl.PositionImpl;
+import org.apache.bookkeeper.mledger.proto.MLDataFormats;
import org.apache.pulsar.broker.service.MessageExpirer;
import org.apache.pulsar.client.impl.MessageImpl;
import org.apache.pulsar.common.api.proto.CommandSubscribe.SubType;
@@ -48,7 +52,6 @@ public class PersistentMessageExpiryMonitor implements FindEntryCallback, Messag
private final String topicName;
private final Rate msgExpired;
private final LongAdder totalMsgExpired;
- private final boolean autoSkipNonRecoverableData;
private final PersistentSubscription subscription;
private static final int FALSE = 0;
@@ -68,8 +71,12 @@ public PersistentMessageExpiryMonitor(PersistentTopic topic, String subscription
this.subscription = subscription;
this.msgExpired = new Rate();
this.totalMsgExpired = new LongAdder();
+ }
+
+ @VisibleForTesting
+ public boolean isAutoSkipNonRecoverableData() {
// check to avoid test failures
- this.autoSkipNonRecoverableData = this.cursor.getManagedLedger() != null
+ return this.cursor.getManagedLedger() != null
&& this.cursor.getManagedLedger().getConfig().isAutoSkipNonRecoverableData();
}
@@ -78,7 +85,9 @@ public boolean expireMessages(int messageTTLInSeconds) {
if (expirationCheckInProgressUpdater.compareAndSet(this, FALSE, TRUE)) {
log.info("[{}][{}] Starting message expiry check, ttl= {} seconds", topicName, subName,
messageTTLInSeconds);
-
+ // First filter the entire Ledger reached TTL based on the Ledger closing time to avoid client clock skew
+ checkExpiryByLedgerClosureTime(cursor, messageTTLInSeconds);
+ // Some part of entries in active Ledger may have reached TTL, so we need to continue searching.
cursor.asyncFindNewestMatching(ManagedCursor.FindPositionConstraint.SearchActiveEntries, entry -> {
try {
long entryTimestamp = Commands.getEntryTimestamp(entry.getDataBuffer());
@@ -100,6 +109,35 @@ public boolean expireMessages(int messageTTLInSeconds) {
}
}
+ private void checkExpiryByLedgerClosureTime(ManagedCursor cursor, int messageTTLInSeconds) {
+ if (messageTTLInSeconds <= 0) {
+ return;
+ }
+ if (cursor instanceof ManagedCursorImpl managedCursor) {
+ ManagedLedgerImpl managedLedger = (ManagedLedgerImpl) managedCursor.getManagedLedger();
+ Position deletedPosition = managedCursor.getMarkDeletedPosition();
+ SortedMap ledgerInfoSortedMap =
+ managedLedger.getLedgersInfo().subMap(deletedPosition.getLedgerId(), true,
+ managedLedger.getLedgersInfo().lastKey(), true);
+ MLDataFormats.ManagedLedgerInfo.LedgerInfo info = null;
+ for (MLDataFormats.ManagedLedgerInfo.LedgerInfo ledgerInfo : ledgerInfoSortedMap.values()) {
+ if (!ledgerInfo.hasTimestamp() || !MessageImpl.isEntryExpired(messageTTLInSeconds,
+ ledgerInfo.getTimestamp())) {
+ break;
+ }
+ info = ledgerInfo;
+ }
+ if (info != null && info.getLedgerId() > -1) {
+ PositionImpl position = PositionImpl.get(info.getLedgerId(), info.getEntries() - 1);
+ if (((PositionImpl) managedLedger.getLastConfirmedEntry()).compareTo(position) < 0) {
+ findEntryComplete(managedLedger.getLastConfirmedEntry(), null);
+ } else {
+ findEntryComplete(position, null);
+ }
+ }
+ }
+ }
+
@Override
public boolean expireMessages(Position messagePosition) {
// If it's beyond last position of this topic, do nothing.
@@ -177,7 +215,8 @@ public void findEntryComplete(Position position, Object ctx) {
if (position != null) {
log.info("[{}][{}] Expiring all messages until position {}", topicName, subName, position);
Position prevMarkDeletePos = cursor.getMarkDeletedPosition();
- cursor.asyncMarkDelete(position, markDeleteCallback, cursor.getNumberOfEntriesInBacklog(false));
+ cursor.asyncMarkDelete(position, cursor.getProperties(), markDeleteCallback,
+ cursor.getNumberOfEntriesInBacklog(false));
if (!Objects.equals(cursor.getMarkDeletedPosition(), prevMarkDeletePos) && subscription != null) {
subscription.updateLastMarkDeleteAdvancedTimestamp();
}
@@ -195,7 +234,7 @@ public void findEntryFailed(ManagedLedgerException exception, Optional
if (log.isDebugEnabled()) {
log.debug("[{}][{}] Finding expired entry operation failed", topicName, subName, exception);
}
- if (autoSkipNonRecoverableData && failedReadPosition.isPresent()
+ if (isAutoSkipNonRecoverableData() && failedReadPosition.isPresent()
&& (exception instanceof NonRecoverableLedgerException)) {
log.warn("[{}][{}] read failed from ledger at position:{} : {}", topicName, subName, failedReadPosition,
exception.getMessage());
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentSubscription.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentSubscription.java
index dc79146110f00..e5d90bf0ef42f 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentSubscription.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentSubscription.java
@@ -29,6 +29,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
+import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
@@ -514,9 +515,15 @@ public String getTypeString() {
return "Null";
}
- @Override
public CompletableFuture analyzeBacklog(Optional position) {
-
+ final ManagedLedger managedLedger = topic.getManagedLedger();
+ final String newNonDurableCursorName = "analyze-backlog-" + UUID.randomUUID();
+ ManagedCursor newNonDurableCursor;
+ try {
+ newNonDurableCursor = ((ManagedCursorImpl) cursor).duplicateNonDurableCursor(newNonDurableCursorName);
+ } catch (ManagedLedgerException e) {
+ return CompletableFuture.failedFuture(e);
+ }
long start = System.currentTimeMillis();
if (log.isDebugEnabled()) {
log.debug("[{}][{}] Starting to analyze backlog", topicName, subName);
@@ -531,7 +538,7 @@ public CompletableFuture analyzeBacklog(Optional
AtomicLong rejectedMessages = new AtomicLong();
AtomicLong rescheduledMessages = new AtomicLong();
- Position currentPosition = cursor.getMarkDeletedPosition();
+ Position currentPosition = newNonDurableCursor.getMarkDeletedPosition();
if (log.isDebugEnabled()) {
log.debug("[{}][{}] currentPosition {}",
@@ -591,7 +598,7 @@ public CompletableFuture analyzeBacklog(Optional
return true;
};
- return cursor.scan(
+ CompletableFuture res = newNonDurableCursor.scan(
position,
condition,
batchSize,
@@ -618,7 +625,22 @@ public CompletableFuture analyzeBacklog(Optional
topicName, subName, end - start, result);
return result;
});
+ res.whenComplete((__, ex) -> {
+ managedLedger.asyncDeleteCursor(newNonDurableCursorName,
+ new AsyncCallbacks.DeleteCursorCallback(){
+ @Override
+ public void deleteCursorComplete(Object ctx) {
+ // Nothing to do.
+ }
+ @Override
+ public void deleteCursorFailed(ManagedLedgerException exception, Object ctx) {
+ log.warn("[{}][{}] Delete non-durable cursor[{}] failed when analyze backlog.",
+ topicName, subName, newNonDurableCursor.getName());
+ }
+ }, null);
+ });
+ return res;
}
@Override
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentTopic.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentTopic.java
index 48069cf555448..bb0796b48fd77 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentTopic.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentTopic.java
@@ -230,7 +230,8 @@ public static boolean isDedupCursorName(String name) {
protected final MessageDeduplication messageDeduplication;
private static final Long COMPACTION_NEVER_RUN = -0xfebecffeL;
- private CompletableFuture currentCompaction = CompletableFuture.completedFuture(COMPACTION_NEVER_RUN);
+ private volatile CompletableFuture currentCompaction = CompletableFuture.completedFuture(
+ COMPACTION_NEVER_RUN);
private TopicCompactionService topicCompactionService;
// TODO: Create compaction strategy from topic policy when exposing strategic compaction to users.
@@ -514,7 +515,7 @@ private PersistentSubscription createPersistentSubscription(String subscriptionN
}
}
- private static boolean isCompactionSubscription(String subscriptionName) {
+ public static boolean isCompactionSubscription(String subscriptionName) {
return COMPACTION_SUBSCRIPTION.equals(subscriptionName);
}
@@ -1006,7 +1007,9 @@ public CompletableFuture subscribe(final TransportCnx cnx, String subs
}
private CompletableFuture getDurableSubscription(String subscriptionName,
- InitialPosition initialPosition, long startMessageRollbackDurationSec, boolean replicated,
+ InitialPosition initialPosition,
+ long startMessageRollbackDurationSec,
+ boolean replicated,
Map subscriptionProperties) {
CompletableFuture subscriptionFuture = new CompletableFuture<>();
if (checkMaxSubscriptionsPerTopicExceed(subscriptionName)) {
@@ -1016,7 +1019,6 @@ private CompletableFuture getDurableSubscription(String subscripti
}
Map properties = PersistentSubscription.getBaseCursorProperties(replicated);
-
ledger.asyncOpenCursor(Codec.encode(subscriptionName), initialPosition, properties, subscriptionProperties,
new OpenCursorCallback() {
@Override
@@ -1196,13 +1198,14 @@ private void asyncDeleteCursorWithClearDelayedMessage(String subscriptionName,
CompletableFuture unsubscribeFuture) {
PersistentSubscription persistentSubscription = subscriptions.get(subscriptionName);
if (persistentSubscription == null) {
- log.warn("[{}][{}] Can't find subscription, skip clear delayed message", topic, subscriptionName);
+ log.warn("[{}][{}] Can't find subscription, skip delete cursor", topic, subscriptionName);
unsubscribeFuture.complete(null);
return;
}
+
if (!isDelayedDeliveryEnabled()
|| !(brokerService.getDelayedDeliveryTrackerFactory() instanceof BucketDelayedDeliveryTrackerFactory)) {
- asyncDeleteCursor(subscriptionName, unsubscribeFuture);
+ asyncDeleteCursorWithCleanCompactionLedger(persistentSubscription, unsubscribeFuture);
return;
}
@@ -1217,7 +1220,7 @@ private void asyncDeleteCursorWithClearDelayedMessage(String subscriptionName,
if (ex != null) {
unsubscribeFuture.completeExceptionally(ex);
} else {
- asyncDeleteCursor(subscriptionName, unsubscribeFuture);
+ asyncDeleteCursorWithCleanCompactionLedger(persistentSubscription, unsubscribeFuture);
}
});
}
@@ -1227,6 +1230,29 @@ private void asyncDeleteCursorWithClearDelayedMessage(String subscriptionName,
dispatcher.clearDelayedMessages().whenComplete((__, ex) -> {
if (ex != null) {
unsubscribeFuture.completeExceptionally(ex);
+ } else {
+ asyncDeleteCursorWithCleanCompactionLedger(persistentSubscription, unsubscribeFuture);
+ }
+ });
+ }
+
+ private void asyncDeleteCursorWithCleanCompactionLedger(PersistentSubscription subscription,
+ CompletableFuture unsubscribeFuture) {
+ final String subscriptionName = subscription.getName();
+ if ((!isCompactionSubscription(subscriptionName)) || !(subscription instanceof PulsarCompactorSubscription)) {
+ asyncDeleteCursor(subscriptionName, unsubscribeFuture);
+ return;
+ }
+
+ currentCompaction.handle((__, e) -> {
+ if (e != null) {
+ log.warn("[{}][{}] Last compaction task failed", topic, subscriptionName);
+ }
+ return ((PulsarCompactorSubscription) subscription).cleanCompactedLedger();
+ }).whenComplete((__, ex) -> {
+ if (ex != null) {
+ log.error("[{}][{}] Error cleaning compacted ledger", topic, subscriptionName, ex);
+ unsubscribeFuture.completeExceptionally(ex);
} else {
asyncDeleteCursor(subscriptionName, unsubscribeFuture);
}
@@ -1729,11 +1755,11 @@ private CompletableFuture checkShadowReplication() {
public void checkMessageExpiry() {
int messageTtlInSeconds = topicPolicies.getMessageTTLInSeconds().get();
if (messageTtlInSeconds != 0) {
- subscriptions.forEach((__, sub) -> sub.expireMessages(messageTtlInSeconds));
- replicators.forEach((__, replicator)
- -> ((PersistentReplicator) replicator).expireMessages(messageTtlInSeconds));
- shadowReplicators.forEach((__, replicator)
- -> ((PersistentReplicator) replicator).expireMessages(messageTtlInSeconds));
+ subscriptions.forEach((__, sub) -> {
+ if (!isCompactionSubscription(sub.getName())) {
+ sub.expireMessages(messageTtlInSeconds);
+ }
+ });
}
}
@@ -2384,7 +2410,7 @@ public CompletableFuture extends TopicStatsImpl> asyncGetStats(GetStatsOptions
stats.backlogSize = ledger.getEstimatedBacklogSize();
stats.deduplicationStatus = messageDeduplication.getStatus().toString();
stats.topicEpoch = topicEpoch.orElse(null);
- stats.ownerBroker = brokerService.pulsar().getLookupServiceAddress();
+ stats.ownerBroker = brokerService.pulsar().getBrokerId();
stats.offloadedStorageSize = ledger.getOffloadedSize();
stats.lastOffloadLedgerId = ledger.getLastOffloadedLedgerId();
stats.lastOffloadSuccessTimeStamp = ledger.getLastOffloadedSuccessTimestamp();
@@ -2892,7 +2918,7 @@ public void checkGC() {
replCloseFuture.thenCompose(v -> delete(deleteMode == InactiveTopicDeleteMode.delete_when_no_subscriptions,
deleteMode == InactiveTopicDeleteMode.delete_when_subscriptions_caught_up, false))
- .thenApply((res) -> tryToDeletePartitionedMetadata())
+ .thenCompose((res) -> tryToDeletePartitionedMetadata())
.thenRun(() -> log.info("[{}] Topic deleted successfully due to inactivity", topic))
.exceptionally(e -> {
if (e.getCause() instanceof TopicBusyException) {
@@ -2900,6 +2926,8 @@ public void checkGC() {
if (log.isDebugEnabled()) {
log.debug("[{}] Did not delete busy topic: {}", topic, e.getCause().getMessage());
}
+ } else if (e.getCause() instanceof UnsupportedOperationException) {
+ log.info("[{}] Skip to delete partitioned topic: {}", topic, e.getCause().getMessage());
} else {
log.warn("[{}] Inactive topic deletion failed", topic, e);
}
@@ -2944,7 +2972,7 @@ private CompletableFuture tryToDeletePartitionedMetadata() {
.filter(topicExist -> topicExist)
.findAny();
if (anyExistPartition.isPresent()) {
- log.error("[{}] Delete topic metadata failed because"
+ log.info("[{}] Delete topic metadata failed because"
+ " another partition exist.", topicName);
throw new UnsupportedOperationException(
String.format("Another partition exists for [%s].",
@@ -3427,17 +3455,29 @@ public void readEntryFailed(ManagedLedgerException exception, Object ctx) {
public synchronized void triggerCompaction()
throws PulsarServerException, AlreadyRunningException {
if (currentCompaction.isDone()) {
+ if (!lock.readLock().tryLock()) {
+ log.info("[{}] Conflict topic-close, topic-delete, skip triggering compaction", topic);
+ return;
+ }
+ try {
+ if (isClosingOrDeleting) {
+ log.info("[{}] Topic is closing or deleting, skip triggering compaction", topic);
+ return;
+ }
- if (strategicCompactionMap.containsKey(topic)) {
- currentCompaction = brokerService.pulsar().getStrategicCompactor()
- .compact(topic, strategicCompactionMap.get(topic));
- } else {
- currentCompaction = topicCompactionService.compact().thenApply(x -> null);
+ if (strategicCompactionMap.containsKey(topic)) {
+ currentCompaction = brokerService.pulsar().getStrategicCompactor()
+ .compact(topic, strategicCompactionMap.get(topic));
+ } else {
+ currentCompaction = topicCompactionService.compact().thenApply(x -> null);
+ }
+ } finally {
+ lock.readLock().unlock();
}
currentCompaction.whenComplete((ignore, ex) -> {
- if (ex != null){
- log.warn("[{}] Compaction failure.", topic, ex);
- }
+ if (ex != null) {
+ log.warn("[{}] Compaction failure.", topic, ex);
+ }
});
} else {
throw new AlreadyRunningException("Compaction already in progress");
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PulsarCompactorSubscription.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PulsarCompactorSubscription.java
index dbb09f6ac39fd..fe13aeb572e2e 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PulsarCompactorSubscription.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PulsarCompactorSubscription.java
@@ -22,12 +22,15 @@
import static org.apache.pulsar.broker.service.AbstractBaseDispatcher.checkAndApplyReachedEndOfTopicOrTopicMigration;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
import org.apache.bookkeeper.mledger.AsyncCallbacks.MarkDeleteCallback;
import org.apache.bookkeeper.mledger.ManagedCursor;
import org.apache.bookkeeper.mledger.ManagedLedgerException;
import org.apache.bookkeeper.mledger.Position;
import org.apache.pulsar.common.api.proto.CommandAck.AckType;
import org.apache.pulsar.compaction.CompactedTopic;
+import org.apache.pulsar.compaction.CompactedTopicContext;
+import org.apache.pulsar.compaction.CompactedTopicImpl;
import org.apache.pulsar.compaction.Compactor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -106,5 +109,19 @@ public void markDeleteFailed(ManagedLedgerException exception, Object ctx) {
}
}
+ CompletableFuture cleanCompactedLedger() {
+ final CompletableFuture compactedTopicContextFuture =
+ ((CompactedTopicImpl) compactedTopic).getCompactedTopicContextFuture();
+ if (compactedTopicContextFuture != null) {
+ return compactedTopicContextFuture.thenCompose(context -> {
+ long compactedLedgerId = context.getLedger().getId();
+ ((CompactedTopicImpl) compactedTopic).reset();
+ return compactedTopic.deleteCompactedLedger(compactedLedgerId);
+ });
+ } else {
+ return CompletableFuture.completedFuture(null);
+ }
+ }
+
private static final Logger log = LoggerFactory.getLogger(PulsarCompactorSubscription.class);
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/ShadowReplicator.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/ShadowReplicator.java
index 493072eb0c837..85e837ff1879a 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/ShadowReplicator.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/ShadowReplicator.java
@@ -76,18 +76,6 @@ protected boolean replicateEntries(List entries) {
continue;
}
- if (msg.isExpired(messageTTLInSeconds)) {
- msgExpired.recordEvent(0 /* no value stat */);
- if (log.isDebugEnabled()) {
- log.debug("[{}] Discarding expired message at position {}, replicateTo {}",
- replicatorId, entry.getPosition(), msg.getReplicateTo());
- }
- cursor.asyncDelete(entry.getPosition(), this, entry.getPosition());
- entry.release();
- msg.recycle();
- continue;
- }
-
if (STATE_UPDATER.get(this) != State.Started || isLocalMessageSkippedOnce) {
// The producer is not ready yet after having stopped/restarted. Drop the message because it will
// recovered when the producer is ready
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/schema/BookkeeperSchemaStorage.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/schema/BookkeeperSchemaStorage.java
index 78e30f6fff827..c509764bf6710 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/schema/BookkeeperSchemaStorage.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/schema/BookkeeperSchemaStorage.java
@@ -707,7 +707,8 @@ public static Exception bkException(String operation, int rc, long ledgerId, lon
message += " - entry=" + entryId;
}
boolean recoverable = rc != BKException.Code.NoSuchLedgerExistsException
- && rc != BKException.Code.NoSuchEntryException;
+ && rc != BKException.Code.NoSuchEntryException
+ && rc != BKException.Code.NoSuchLedgerExistsOnMetadataServerException;
return new SchemaException(recoverable, message);
}
@@ -716,7 +717,8 @@ public static CompletableFuture ignoreUnrecoverableBKException(Completabl
if (t.getCause() != null
&& (t.getCause() instanceof SchemaException)
&& !((SchemaException) t.getCause()).isRecoverable()) {
- // Meeting NoSuchLedgerExistsException or NoSuchEntryException when reading schemas in
+ // Meeting NoSuchLedgerExistsException, NoSuchEntryException or
+ // NoSuchLedgerExistsOnMetadataServerException when reading schemas in
// bookkeeper. This also means that the data has already been deleted by other operations
// in deleting schema.
if (log.isDebugEnabled()) {
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/stats/DimensionStats.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/stats/DimensionStats.java
index 1b6f981ca4e21..54965e4c783d8 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/stats/DimensionStats.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/stats/DimensionStats.java
@@ -18,6 +18,7 @@
*/
package org.apache.pulsar.broker.stats;
+import static com.google.common.base.Preconditions.checkArgument;
import static io.prometheus.client.CollectorRegistry.defaultRegistry;
import io.prometheus.client.Collector;
import io.prometheus.client.Summary;
@@ -70,6 +71,7 @@ public DimensionStats(String name, long updateDurationInSec) {
}
public void recordDimensionTimeValue(long latency, TimeUnit unit) {
+ checkArgument(latency >= 0);
summary.observe(unit.toMillis(latency));
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/stats/prometheus/metrics/LongAdderCounter.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/stats/prometheus/metrics/LongAdderCounter.java
index 8ade2bc883f9a..c2816f5a2a013 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/stats/prometheus/metrics/LongAdderCounter.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/stats/prometheus/metrics/LongAdderCounter.java
@@ -18,6 +18,7 @@
*/
package org.apache.pulsar.broker.stats.prometheus.metrics;
+import static com.google.common.base.Preconditions.checkArgument;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;
import org.apache.bookkeeper.stats.Counter;
@@ -57,6 +58,7 @@ public void addCount(long delta) {
@Override
public void addLatency(long eventLatency, TimeUnit unit) {
+ checkArgument(eventLatency >= 0);
long valueMillis = unit.toMillis(eventLatency);
counter.add(valueMillis);
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/systopic/TopicPoliciesSystemTopicClient.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/systopic/TopicPoliciesSystemTopicClient.java
index 3fd8921c15efa..b7cff2e08c2d0 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/systopic/TopicPoliciesSystemTopicClient.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/systopic/TopicPoliciesSystemTopicClient.java
@@ -30,6 +30,8 @@
import org.apache.pulsar.client.api.PulsarClientException;
import org.apache.pulsar.client.api.Schema;
import org.apache.pulsar.client.api.TypedMessageBuilder;
+import org.apache.pulsar.client.api.schema.SchemaDefinition;
+import org.apache.pulsar.client.internal.DefaultImplementation;
import org.apache.pulsar.common.events.ActionType;
import org.apache.pulsar.common.events.PulsarEvent;
import org.apache.pulsar.common.naming.TopicName;
@@ -41,13 +43,17 @@
*/
public class TopicPoliciesSystemTopicClient extends SystemTopicClientBase {
+ static Schema avroSchema = DefaultImplementation.getDefaultImplementation()
+ .newAvroSchema(SchemaDefinition.builder().withPojo(PulsarEvent.class).build());
+
public TopicPoliciesSystemTopicClient(PulsarClient client, TopicName topicName) {
super(client, topicName);
+
}
@Override
protected CompletableFuture> newWriterAsyncInternal() {
- return client.newProducer(Schema.AVRO(PulsarEvent.class))
+ return client.newProducer(avroSchema)
.topic(topicName.toString())
.enableBatching(false)
.createAsync()
@@ -61,7 +67,7 @@ protected CompletableFuture> newWriterAsyncInternal() {
@Override
protected CompletableFuture> newReaderAsyncInternal() {
- return client.newReader(Schema.AVRO(PulsarEvent.class))
+ return client.newReader(avroSchema)
.topic(topicName.toString())
.startMessageId(MessageId.earliest)
.readCompacted(true)
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/transaction/buffer/impl/TopicTransactionBuffer.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/transaction/buffer/impl/TopicTransactionBuffer.java
index f356921d6988e..5392e473947e6 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/transaction/buffer/impl/TopicTransactionBuffer.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/transaction/buffer/impl/TopicTransactionBuffer.java
@@ -287,8 +287,8 @@ private void handleTransactionMessage(TxnID txnId, Position position) {
.checkAbortedTransaction(txnId)) {
ongoingTxns.put(txnId, (PositionImpl) position);
PositionImpl firstPosition = ongoingTxns.get(ongoingTxns.firstKey());
- //max read position is less than first ongoing transaction message position, so entryId -1
- maxReadPosition = PositionImpl.get(firstPosition.getLedgerId(), firstPosition.getEntryId() - 1);
+ // max read position is less than first ongoing transaction message position
+ maxReadPosition = ((ManagedLedgerImpl) topic.getManagedLedger()).getPreviousPosition(firstPosition);
}
}
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java
index e8192cde3fdf3..e23286ae4492e 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java
@@ -56,7 +56,9 @@
import org.apache.pulsar.broker.authentication.AuthenticationDataSource;
import org.apache.pulsar.broker.authentication.AuthenticationParameters;
import org.apache.pulsar.broker.authorization.AuthorizationService;
+import org.apache.pulsar.broker.loadbalance.LoadManager;
import org.apache.pulsar.broker.loadbalance.extensions.ExtensibleLoadManagerImpl;
+import org.apache.pulsar.broker.loadbalance.extensions.data.BrokerLookupData;
import org.apache.pulsar.broker.namespace.LookupOptions;
import org.apache.pulsar.broker.namespace.NamespaceService;
import org.apache.pulsar.broker.resources.BookieResources;
@@ -93,6 +95,7 @@
import org.apache.pulsar.common.policies.path.PolicyPath;
import org.apache.pulsar.common.util.FutureUtil;
import org.apache.pulsar.metadata.api.MetadataStoreException;
+import org.apache.pulsar.metadata.api.coordination.LockManager;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
@@ -1199,24 +1202,43 @@ protected CompletableFuture canUpdateCluster(String tenant, Set ol
/**
* Redirect the call to the specified broker.
*
- * @param broker
- * Broker name
+ * @param brokerId broker's id (lookup service address)
*/
- protected void validateBrokerName(String broker) {
- String brokerUrl = String.format("http://%s", broker);
- String brokerUrlTls = String.format("https://%s", broker);
- if (!brokerUrl.equals(pulsar().getWebServiceAddress())
- && !brokerUrlTls.equals(pulsar().getWebServiceAddressTls())) {
- String[] parts = broker.split(":");
- checkArgument(parts.length == 2, String.format("Invalid broker url %s", broker));
- String host = parts[0];
- int port = Integer.parseInt(parts[1]);
-
- URI redirect = UriBuilder.fromUri(uri.getRequestUri()).host(host).port(port).build();
- log.debug("[{}] Redirecting the rest call to {}: broker={}", clientAppId(), redirect, broker);
- throw new WebApplicationException(Response.temporaryRedirect(redirect).build());
-
+ protected CompletableFuture maybeRedirectToBroker(String brokerId) {
+ // backwards compatibility
+ String cleanedBrokerId = brokerId.replaceFirst("http[s]?://", "");
+ if (pulsar.getBrokerId().equals(cleanedBrokerId)
+ // backwards compatibility
+ || ("http://" + cleanedBrokerId).equals(pulsar().getWebServiceAddress())
+ || ("https://" + cleanedBrokerId).equals(pulsar().getWebServiceAddressTls())) {
+ // no need to redirect, the current broker matches the given broker id
+ return CompletableFuture.completedFuture(null);
}
+ LockManager brokerLookupDataLockManager =
+ pulsar().getCoordinationService().getLockManager(BrokerLookupData.class);
+ return brokerLookupDataLockManager.readLock(LoadManager.LOADBALANCE_BROKERS_ROOT + "/" + cleanedBrokerId)
+ .thenAccept(brokerLookupDataOptional -> {
+ if (brokerLookupDataOptional.isEmpty()) {
+ throw new RestException(Status.NOT_FOUND,
+ "Broker id '" + brokerId + "' not found in available brokers.");
+ }
+ brokerLookupDataOptional.ifPresent(brokerLookupData -> {
+ URI targetBrokerUri;
+ if ((isRequestHttps() || StringUtils.isBlank(brokerLookupData.getWebServiceUrl()))
+ && StringUtils.isNotBlank(brokerLookupData.getWebServiceUrlTls())) {
+ targetBrokerUri = URI.create(brokerLookupData.getWebServiceUrlTls());
+ } else {
+ targetBrokerUri = URI.create(brokerLookupData.getWebServiceUrl());
+ }
+ URI redirect = UriBuilder.fromUri(uri.getRequestUri())
+ .scheme(targetBrokerUri.getScheme())
+ .host(targetBrokerUri.getHost())
+ .port(targetBrokerUri.getPort()).build();
+ log.debug("[{}] Redirecting the rest call to {}: broker={}", clientAppId(), redirect,
+ cleanedBrokerId);
+ throw new WebApplicationException(Response.temporaryRedirect(redirect).build());
+ });
+ });
}
public void validateTopicPolicyOperation(TopicName topicName, PolicyName policy, PolicyOperation operation) {
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/client/impl/RawBatchConverter.java b/pulsar-broker/src/main/java/org/apache/pulsar/client/impl/RawBatchConverter.java
index dfa65d1995381..4c24f6d303668 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/client/impl/RawBatchConverter.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/client/impl/RawBatchConverter.java
@@ -134,7 +134,11 @@ public static Optional rebatchMessage(RawMessage msg,
msg.getMessageIdData().getEntryId(),
msg.getMessageIdData().getPartition(),
i);
- if (!singleMessageMetadata.hasPartitionKey()) {
+ if (singleMessageMetadata.isCompactedOut()) {
+ // we may read compacted out message from the compacted topic
+ Commands.serializeSingleMessageInBatchWithPayload(emptyMetadata,
+ Unpooled.EMPTY_BUFFER, batchBuffer);
+ } else if (!singleMessageMetadata.hasPartitionKey()) {
if (retainNullKey) {
messagesRetained++;
Commands.serializeSingleMessageInBatchWithPayload(singleMessageMetadata,
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/client/impl/RawBatchMessageContainerImpl.java b/pulsar-broker/src/main/java/org/apache/pulsar/client/impl/RawBatchMessageContainerImpl.java
index ba8d3db7178d9..374f1e30c0a89 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/client/impl/RawBatchMessageContainerImpl.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/client/impl/RawBatchMessageContainerImpl.java
@@ -187,6 +187,7 @@ public ByteBuf toByteBuf() {
idData.writeTo(buf);
buf.writeInt(metadataAndPayload.readableBytes());
buf.writeBytes(metadataAndPayload);
+ metadataAndPayload.release();
encryptedPayload.release();
clear();
return buf;
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/client/impl/RawReaderImpl.java b/pulsar-broker/src/main/java/org/apache/pulsar/client/impl/RawReaderImpl.java
index 70bda888bf7ea..f65232413991f 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/client/impl/RawReaderImpl.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/client/impl/RawReaderImpl.java
@@ -59,6 +59,7 @@ public RawReaderImpl(PulsarClientImpl client, String topic, String subscription,
consumerConfiguration.setReceiverQueueSize(DEFAULT_RECEIVER_QUEUE_SIZE);
consumerConfiguration.setReadCompacted(true);
consumerConfiguration.setSubscriptionInitialPosition(SubscriptionInitialPosition.Earliest);
+ consumerConfiguration.setAckReceiptEnabled(true);
consumer = new RawConsumerImpl(client, consumerConfiguration,
consumerFuture);
@@ -122,7 +123,7 @@ static class RawConsumerImpl extends ConsumerImpl {
MessageId.earliest,
0 /* startMessageRollbackDurationInSec */,
Schema.BYTES, null,
- true
+ false
);
incomingRawMessages = new GrowableArrayBlockingQueue<>();
pendingRawReceives = new ConcurrentLinkedQueue<>();
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/compaction/CompactedTopicImpl.java b/pulsar-broker/src/main/java/org/apache/pulsar/compaction/CompactedTopicImpl.java
index 8794e2736d4d4..dfafbc41cb45c 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/compaction/CompactedTopicImpl.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/compaction/CompactedTopicImpl.java
@@ -32,6 +32,7 @@
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
+import java.util.function.Predicate;
import javax.annotation.Nullable;
import org.apache.bookkeeper.client.BKException;
import org.apache.bookkeeper.client.BookKeeper;
@@ -100,7 +101,11 @@ public void asyncReadEntriesOrWait(ManagedCursor cursor,
boolean isFirstRead,
ReadEntriesCallback callback, Consumer consumer) {
PositionImpl cursorPosition;
- if (isFirstRead && MessageId.earliest.equals(consumer.getStartMessageId())){
+ boolean readFromEarliest = isFirstRead && MessageId.earliest.equals(consumer.getStartMessageId())
+ && (!cursor.isDurable() || cursor.getName().equals(Compactor.COMPACTION_SUBSCRIPTION)
+ || cursor.getMarkDeletedPosition() == null
+ || cursor.getMarkDeletedPosition().getEntryId() == -1L);
+ if (readFromEarliest){
cursorPosition = PositionImpl.EARLIEST;
} else {
cursorPosition = (PositionImpl) cursor.getReadPosition();
@@ -320,6 +325,55 @@ public CompletableFuture readLastEntryOfCompactedLedger() {
});
}
+ CompletableFuture findFirstMatchEntry(final Predicate predicate) {
+ var compactedTopicContextFuture = this.getCompactedTopicContextFuture();
+
+ if (compactedTopicContextFuture == null) {
+ return CompletableFuture.completedFuture(null);
+ }
+ return compactedTopicContextFuture.thenCompose(compactedTopicContext -> {
+ LedgerHandle lh = compactedTopicContext.getLedger();
+ CompletableFuture promise = new CompletableFuture<>();
+ findFirstMatchIndexLoop(predicate, 0L, lh.getLastAddConfirmed(), promise, null, lh);
+ return promise.thenCompose(index -> {
+ if (index == null) {
+ return CompletableFuture.completedFuture(null);
+ }
+ return readEntries(lh, index, index).thenApply(entries -> entries.get(0));
+ });
+ });
+ }
+ private static void findFirstMatchIndexLoop(final Predicate predicate,
+ final long start, final long end,
+ final CompletableFuture promise,
+ final Long lastMatchIndex,
+ final LedgerHandle lh) {
+ if (start > end) {
+ promise.complete(lastMatchIndex);
+ return;
+ }
+
+ long mid = (start + end) / 2;
+ readEntries(lh, mid, mid).thenAccept(entries -> {
+ Entry entry = entries.get(0);
+ final boolean isMatch;
+ try {
+ isMatch = predicate.test(entry);
+ } finally {
+ entry.release();
+ }
+
+ if (isMatch) {
+ findFirstMatchIndexLoop(predicate, start, mid - 1, promise, mid, lh);
+ } else {
+ findFirstMatchIndexLoop(predicate, mid + 1, end, promise, lastMatchIndex, lh);
+ }
+ }).exceptionally(ex -> {
+ promise.completeExceptionally(ex);
+ return null;
+ });
+ }
+
private static int comparePositionAndMessageId(PositionImpl p, MessageIdData m) {
return ComparisonChain.start()
.compare(p.getLedgerId(), m.getLedgerId())
@@ -330,6 +384,11 @@ public Optional getCompactionHorizon() {
return Optional.ofNullable(this.compactionHorizon);
}
+ public void reset() {
+ this.compactionHorizon = null;
+ this.compactedTopicContext = null;
+ }
+
@Nullable
public CompletableFuture getCompactedTopicContextFuture() {
return compactedTopicContext;
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/compaction/PulsarTopicCompactionService.java b/pulsar-broker/src/main/java/org/apache/pulsar/compaction/PulsarTopicCompactionService.java
index 1d3f94dcb9048..16543bc7aa77f 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/compaction/PulsarTopicCompactionService.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/compaction/PulsarTopicCompactionService.java
@@ -22,7 +22,6 @@
import static org.apache.pulsar.compaction.CompactedTopicImpl.COMPACT_LEDGER_EMPTY;
import static org.apache.pulsar.compaction.CompactedTopicImpl.NEWER_THAN_COMPACTED;
import static org.apache.pulsar.compaction.CompactedTopicImpl.findStartPoint;
-import static org.apache.pulsar.compaction.CompactedTopicImpl.readEntries;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
@@ -33,7 +32,6 @@
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import org.apache.bookkeeper.client.BookKeeper;
-import org.apache.bookkeeper.client.LedgerHandle;
import org.apache.bookkeeper.mledger.Entry;
import org.apache.bookkeeper.mledger.Position;
import org.apache.bookkeeper.mledger.impl.PositionImpl;
@@ -116,7 +114,7 @@ public CompletableFuture findEntryByPublishTime(long publishTime) {
final Predicate predicate = entry -> {
return Commands.parseMessageMetadata(entry.getDataBuffer()).getPublishTime() >= publishTime;
};
- return findFirstMatchEntry(predicate);
+ return compactedTopic.findFirstMatchEntry(predicate);
}
@Override
@@ -128,57 +126,7 @@ public CompletableFuture findEntryByEntryIndex(long entryIndex) {
}
return brokerEntryMetadata.getIndex() >= entryIndex;
};
- return findFirstMatchEntry(predicate);
- }
-
- private CompletableFuture findFirstMatchEntry(final Predicate predicate) {
- var compactedTopicContextFuture = compactedTopic.getCompactedTopicContextFuture();
-
- if (compactedTopicContextFuture == null) {
- return CompletableFuture.completedFuture(null);
- }
- return compactedTopicContextFuture.thenCompose(compactedTopicContext -> {
- LedgerHandle lh = compactedTopicContext.getLedger();
- CompletableFuture promise = new CompletableFuture<>();
- findFirstMatchIndexLoop(predicate, 0L, lh.getLastAddConfirmed(), promise, null, lh);
- return promise.thenCompose(index -> {
- if (index == null) {
- return CompletableFuture.completedFuture(null);
- }
- return readEntries(lh, index, index).thenApply(entries -> entries.get(0));
- });
- });
- }
-
- private static void findFirstMatchIndexLoop(final Predicate predicate,
- final long start, final long end,
- final CompletableFuture promise,
- final Long lastMatchIndex,
- final LedgerHandle lh) {
- if (start > end) {
- promise.complete(lastMatchIndex);
- return;
- }
-
- long mid = (start + end) / 2;
- readEntries(lh, mid, mid).thenAccept(entries -> {
- Entry entry = entries.get(0);
- final boolean isMatch;
- try {
- isMatch = predicate.test(entry);
- } finally {
- entry.release();
- }
-
- if (isMatch) {
- findFirstMatchIndexLoop(predicate, start, mid - 1, promise, mid, lh);
- } else {
- findFirstMatchIndexLoop(predicate, mid + 1, end, promise, lastMatchIndex, lh);
- }
- }).exceptionally(ex -> {
- promise.completeExceptionally(ex);
- return null;
- });
+ return compactedTopic.findFirstMatchEntry(predicate);
}
public CompactedTopicImpl getCompactedTopic() {
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/SLAMonitoringTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/SLAMonitoringTest.java
index 47949d7312b88..4a6524bf24521 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/SLAMonitoringTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/SLAMonitoringTest.java
@@ -102,9 +102,9 @@ void setup() throws Exception {
createTenant(pulsarAdmins[BROKER_COUNT - 1]);
for (int i = 0; i < BROKER_COUNT; i++) {
- String topic = String.format("%s/%s/%s:%s", NamespaceService.SLA_NAMESPACE_PROPERTY, "my-cluster",
- pulsarServices[i].getAdvertisedAddress(), brokerWebServicePorts[i]);
- pulsarAdmins[0].namespaces().createNamespace(topic);
+ var namespaceName = NamespaceService.getSLAMonitorNamespace(pulsarServices[i].getBrokerId(),
+ pulsarServices[i].getConfig());
+ pulsarAdmins[0].namespaces().createNamespace(namespaceName.toString());
}
}
@@ -173,9 +173,9 @@ public void testOwnedNamespaces() {
public void testOwnershipViaAdminAfterSetup() {
for (int i = 0; i < BROKER_COUNT; i++) {
try {
- String topic = String.format("persistent://%s/%s/%s:%s/%s",
- NamespaceService.SLA_NAMESPACE_PROPERTY, "my-cluster", pulsarServices[i].getAdvertisedAddress(),
- brokerWebServicePorts[i], "my-topic");
+ String topic = String.format("persistent://%s/%s/%s/%s",
+ NamespaceService.SLA_NAMESPACE_PROPERTY, "my-cluster",
+ pulsarServices[i].getBrokerId(), "my-topic");
assertEquals(pulsarAdmins[0].lookups().lookupTopic(topic),
"pulsar://" + pulsarServices[i].getAdvertisedAddress() + ":" + brokerNativeBrokerPorts[i]);
} catch (Exception e) {
@@ -199,8 +199,8 @@ public void testUnloadIfBrokerCrashes() {
fail("Should be a able to close the broker index " + crashIndex + " Exception: " + e);
}
- String topic = String.format("persistent://%s/%s/%s:%s/%s", NamespaceService.SLA_NAMESPACE_PROPERTY,
- "my-cluster", pulsarServices[crashIndex].getAdvertisedAddress(), brokerWebServicePorts[crashIndex],
+ String topic = String.format("persistent://%s/%s/%s/%s", NamespaceService.SLA_NAMESPACE_PROPERTY,
+ "my-cluster", pulsarServices[crashIndex].getBrokerId(),
"my-topic");
log.info("Lookup for namespace {}", topic);
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/TopicEventsListenerTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/TopicEventsListenerTest.java
index e6459bbf74c31..ceb3c1d0d9335 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/TopicEventsListenerTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/TopicEventsListenerTest.java
@@ -126,7 +126,7 @@ public void testEvents(String topicTypePersistence, String topicTypePartitioned,
boolean forceDelete) throws Exception {
String topicName = topicTypePersistence + "://" + namespace + "/" + "topic-" + UUID.randomUUID();
- createTopicAndVerifyEvents(topicTypePartitioned, topicName);
+ createTopicAndVerifyEvents(topicTypePersistence, topicTypePartitioned, topicName);
events.clear();
if (topicTypePartitioned.equals("partitioned")) {
@@ -150,7 +150,7 @@ public void testEventsWithUnload(String topicTypePersistence, String topicTypePa
boolean forceDelete) throws Exception {
String topicName = topicTypePersistence + "://" + namespace + "/" + "topic-" + UUID.randomUUID();
- createTopicAndVerifyEvents(topicTypePartitioned, topicName);
+ createTopicAndVerifyEvents(topicTypePersistence, topicTypePartitioned, topicName);
events.clear();
admin.topics().unload(topicName);
@@ -182,7 +182,7 @@ public void testEventsActiveSub(String topicTypePersistence, String topicTypePar
boolean forceDelete) throws Exception {
String topicName = topicTypePersistence + "://" + namespace + "/" + "topic-" + UUID.randomUUID();
- createTopicAndVerifyEvents(topicTypePartitioned, topicName);
+ createTopicAndVerifyEvents(topicTypePersistence, topicTypePartitioned, topicName);
Consumer consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName("sub").subscribe();
Producer producer = pulsarClient.newProducer().topic(topicName).create();
@@ -238,7 +238,7 @@ public void testEventsActiveSub(String topicTypePersistence, String topicTypePar
public void testTopicAutoGC(String topicTypePersistence, String topicTypePartitioned) throws Exception {
String topicName = topicTypePersistence + "://" + namespace + "/" + "topic-" + UUID.randomUUID();
- createTopicAndVerifyEvents(topicTypePartitioned, topicName);
+ createTopicAndVerifyEvents(topicTypePersistence, topicTypePartitioned, topicName);
admin.namespaces().setInactiveTopicPolicies(namespace,
new InactiveTopicPolicies(InactiveTopicDeleteMode.delete_when_no_subscriptions, 1, true));
@@ -262,25 +262,21 @@ public void testTopicAutoGC(String topicTypePersistence, String topicTypePartiti
);
}
- private void createTopicAndVerifyEvents(String topicTypePartitioned, String topicName) throws Exception {
+ private void createTopicAndVerifyEvents(String topicDomain, String topicTypePartitioned, String topicName) throws Exception {
final String[] expectedEvents;
- if (topicTypePartitioned.equals("partitioned")) {
- topicNameToWatch = topicName + "-partition-1";
- admin.topics().createPartitionedTopic(topicName, 2);
- triggerPartitionsCreation(topicName);
-
+ if (topicDomain.equalsIgnoreCase("persistent") || topicTypePartitioned.equals("partitioned")) {
expectedEvents = new String[]{
"LOAD__BEFORE",
"CREATE__BEFORE",
"CREATE__SUCCESS",
"LOAD__SUCCESS"
};
-
} else {
- topicNameToWatch = topicName;
- admin.topics().createNonPartitionedTopic(topicName);
-
expectedEvents = new String[]{
+ // Before https://github.com/apache/pulsar/pull/21995, Pulsar will skip create topic if the topic
+ // was already exists, and the action "check topic exists" will try to load Managed ledger,
+ // the check triggers two exrtra events: [LOAD__BEFORE, LOAD__FAILURE].
+ // #21995 fixed this wrong behavior, so remove these two events.
"LOAD__BEFORE",
"LOAD__FAILURE",
"LOAD__BEFORE",
@@ -288,7 +284,14 @@ private void createTopicAndVerifyEvents(String topicTypePartitioned, String topi
"CREATE__SUCCESS",
"LOAD__SUCCESS"
};
-
+ }
+ if (topicTypePartitioned.equals("partitioned")) {
+ topicNameToWatch = topicName + "-partition-1";
+ admin.topics().createPartitionedTopic(topicName, 2);
+ triggerPartitionsCreation(topicName);
+ } else {
+ topicNameToWatch = topicName;
+ admin.topics().createNonPartitionedTopic(topicName);
}
Awaitility.waitAtMost(10, TimeUnit.SECONDS).untilAsserted(() ->
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApi2Test.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApi2Test.java
index 9a5d25fa0c867..3899338870451 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApi2Test.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApi2Test.java
@@ -516,8 +516,7 @@ public void nonPersistentTopics() throws Exception {
assertEquals(topicStats.getSubscriptions().get("my-sub").getMsgDropRate(), 0);
assertEquals(topicStats.getPublishers().size(), 0);
assertEquals(topicStats.getMsgDropRate(), 0);
- assertEquals(topicStats.getOwnerBroker(),
- pulsar.getAdvertisedAddress() + ":" + pulsar.getConfiguration().getWebServicePort().get());
+ assertEquals(topicStats.getOwnerBroker(), pulsar.getBrokerId());
PersistentTopicInternalStats internalStats = admin.topics().getInternalStats(nonPersistentTopicName, false);
assertEquals(internalStats.cursors.keySet(), Set.of("my-sub"));
@@ -1310,7 +1309,7 @@ public void brokerNamespaceIsolationPolicies() throws Exception {
String cluster = pulsar.getConfiguration().getClusterName();
String namespaceRegex = "other/" + cluster + "/other.*";
String brokerName = pulsar.getAdvertisedAddress();
- String brokerAddress = brokerName + ":" + pulsar.getConfiguration().getWebServicePort().get();
+ String brokerAddress = pulsar.getBrokerId();
Map parameters1 = new HashMap<>();
parameters1.put("min_limit", "1");
@@ -1318,7 +1317,7 @@ public void brokerNamespaceIsolationPolicies() throws Exception {
NamespaceIsolationData nsPolicyData1 = NamespaceIsolationData.builder()
.namespaces(Collections.singletonList(namespaceRegex))
- .primary(Collections.singletonList(brokerName + ":[0-9]*"))
+ .primary(Collections.singletonList(brokerName))
.secondary(Collections.singletonList(brokerName + ".*"))
.autoFailoverPolicy(AutoFailoverPolicyData.builder()
.policyType(AutoFailoverPolicyType.min_available)
@@ -3390,4 +3389,61 @@ private void testSetBacklogQuotasNamespaceLevelIfRetentionExists() throws Except
// cleanup.
admin.namespaces().deleteNamespace(ns);
}
+
+ @Test
+ private void testAnalyzeSubscriptionBacklogNotCauseStuck() throws Exception {
+ final String topic = BrokerTestUtil.newUniqueName("persistent://" + defaultNamespace + "/tp");
+ final String subscription = "s1";
+ admin.topics().createNonPartitionedTopic(topic);
+ // Send 10 messages.
+ Consumer consumer = pulsarClient.newConsumer(Schema.STRING).topic(topic).subscriptionName(subscription)
+ .receiverQueueSize(0).subscribe();
+ Producer producer = pulsarClient.newProducer(Schema.STRING).topic(topic).create();
+ for (int i = 0; i < 10; i++) {
+ producer.send(i + "");
+ }
+
+ // Verify consumer can receive all messages after calling "analyzeSubscriptionBacklog".
+ admin.topics().analyzeSubscriptionBacklog(topic, subscription, Optional.of(MessageIdImpl.earliest));
+ for (int i = 0; i < 10; i++) {
+ Awaitility.await().untilAsserted(() -> {
+ Message m = consumer.receive();
+ assertNotNull(m);
+ consumer.acknowledge(m);
+ });
+ }
+
+ // cleanup.
+ consumer.close();
+ producer.close();
+ admin.topics().delete(topic);
+ }
+
+ @Test
+ public void testGetStatsIfPartitionNotExists() throws Exception {
+ // create topic.
+ final String partitionedTp = BrokerTestUtil.newUniqueName("persistent://" + defaultNamespace + "/tp");
+ admin.topics().createPartitionedTopic(partitionedTp, 1);
+ TopicName partition0 = TopicName.get(partitionedTp).getPartition(0);
+ boolean topicExists1 = pulsar.getBrokerService().getTopic(partition0.toString(), false).join().isPresent();
+ assertTrue(topicExists1);
+ // Verify topics-stats works.
+ TopicStats topicStats = admin.topics().getStats(partition0.toString());
+ assertNotNull(topicStats);
+
+ // Delete partition and call topic-stats again.
+ admin.topics().delete(partition0.toString());
+ boolean topicExists2 = pulsar.getBrokerService().getTopic(partition0.toString(), false).join().isPresent();
+ assertFalse(topicExists2);
+ // Verify: respond 404.
+ try {
+ admin.topics().getStats(partition0.toString());
+ fail("Should respond 404 after the partition was deleted");
+ } catch (Exception ex) {
+ assertTrue(ex.getMessage().contains("Topic partitions were not yet created"));
+ }
+
+ // cleanup.
+ admin.topics().deletePartitionedTopic(partitionedTp);
+ }
}
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiHealthCheckTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiHealthCheckTest.java
index a780f889de85f..357422b11f6ce 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiHealthCheckTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiHealthCheckTest.java
@@ -23,6 +23,7 @@
import static org.testng.Assert.assertTrue;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
+import java.lang.reflect.Field;
import java.time.Duration;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -31,13 +32,21 @@
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
+import org.apache.pulsar.broker.PulsarService;
import org.apache.pulsar.broker.auth.MockedPulsarServiceBaseTest;
import org.apache.pulsar.client.admin.PulsarAdminException;
+import org.apache.pulsar.client.api.MessageId;
+import org.apache.pulsar.client.api.Producer;
+import org.apache.pulsar.client.api.PulsarClient;
+import org.apache.pulsar.client.api.Schema;
+import org.apache.pulsar.client.impl.ProducerBuilderImpl;
+import org.apache.pulsar.client.impl.PulsarClientImpl;
import org.apache.pulsar.common.naming.TopicVersion;
import org.apache.pulsar.common.policies.data.ClusterData;
import org.apache.pulsar.common.policies.data.TenantInfoImpl;
import org.apache.pulsar.compaction.Compactor;
import org.awaitility.Awaitility;
+import org.mockito.Mockito;
import org.springframework.util.CollectionUtils;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
@@ -236,4 +245,58 @@ public void testHealthCheckupV2() throws Exception {
))
);
}
+
+ class DummyProducerBuilder extends ProducerBuilderImpl {
+ // This is a dummy producer builder to test the health check timeout
+ // the producer constructed by this builder will not send any message
+ public DummyProducerBuilder(PulsarClientImpl client, Schema schema) {
+ super(client, schema);
+ }
+
+ @Override
+ public CompletableFuture> createAsync() {
+ CompletableFuture> future = new CompletableFuture<>();
+ super.createAsync().thenAccept(producer -> {
+ Producer spyProducer = Mockito.spy(producer);
+ Mockito.doReturn(CompletableFuture.completedFuture(MessageId.earliest))
+ .when(spyProducer).sendAsync(Mockito.any());
+ future.complete(spyProducer);
+ }).exceptionally(ex -> {
+ future.completeExceptionally(ex);
+ return null;
+ });
+ return future;
+ }
+ }
+
+ @Test
+ public void testHealthCheckTimeOut() throws Exception {
+ final String testHealthCheckTopic = String.format("persistent://pulsar/localhost:%s/healthcheck",
+ pulsar.getConfig().getWebServicePort().get());
+ PulsarClient client = pulsar.getClient();
+ PulsarClient spyClient = Mockito.spy(client);
+ Mockito.doReturn(new DummyProducerBuilder<>((PulsarClientImpl) spyClient, Schema.BYTES))
+ .when(spyClient).newProducer(Schema.STRING);
+ // use reflection to replace the client in the broker
+ Field field = PulsarService.class.getDeclaredField("client");
+ field.setAccessible(true);
+ field.set(pulsar, spyClient);
+ try {
+ admin.brokers().healthcheck(TopicVersion.V2);
+ throw new Exception("Should not reach here");
+ } catch (PulsarAdminException e) {
+ log.info("Exception caught", e);
+ assertTrue(e.getMessage().contains("LowOverheadTimeoutException"));
+ }
+ // To ensure we don't have any subscription, the producers and readers are closed.
+ Awaitility.await().untilAsserted(() ->
+ assertTrue(CollectionUtils.isEmpty(admin.topics()
+ .getSubscriptions(testHealthCheckTopic).stream()
+ // All system topics are using compaction, even though is not explicitly set in the policies.
+ .filter(v -> !v.equals(Compactor.COMPACTION_SUBSCRIPTION))
+ .collect(Collectors.toList())
+ ))
+ );
+ }
+
}
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiMultiBrokersTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiMultiBrokersTest.java
index de4cf9658b201..7c9154a27ff69 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiMultiBrokersTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiMultiBrokersTest.java
@@ -81,9 +81,8 @@ public void testGetLeaderBroker()
assertTrue(leaderBroker.isPresent());
log.info("Leader broker is {}", leaderBroker);
for (PulsarAdmin admin : getAllAdmins()) {
- String serviceUrl = admin.brokers().getLeaderBroker().getServiceUrl();
- log.info("Pulsar admin get leader broker is {}", serviceUrl);
- assertEquals(leaderBroker.get().getServiceUrl(), serviceUrl);
+ String brokerId = admin.brokers().getLeaderBroker().getBrokerId();
+ assertEquals(leaderBroker.get().getBrokerId(), brokerId);
}
}
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiTest.java
index 0df378356703c..b28cfc98fdb07 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiTest.java
@@ -333,10 +333,12 @@ public void clusters() throws Exception {
} catch (PulsarAdminException e) {
assertTrue(e instanceof PreconditionFailedException);
}
+
+ restartBroker();
}
@Test
- public void clusterNamespaceIsolationPolicies() throws PulsarAdminException {
+ public void clusterNamespaceIsolationPolicies() throws Exception {
try {
// create
String policyName1 = "policy-1";
@@ -512,6 +514,7 @@ public void clusterNamespaceIsolationPolicies() throws PulsarAdminException {
// Ok
}
+ restartBroker();
}
@Test
@@ -529,7 +532,8 @@ public void brokers() throws Exception {
Assert.assertEquals(list2.size(), 1);
BrokerInfo leaderBroker = admin.brokers().getLeaderBroker();
- Assert.assertEquals(leaderBroker.getServiceUrl(), pulsar.getLeaderElectionService().getCurrentLeader().map(LeaderBroker::getServiceUrl).get());
+ Assert.assertEquals(leaderBroker.getBrokerId(),
+ pulsar.getLeaderElectionService().getCurrentLeader().map(LeaderBroker::getBrokerId).get());
Map nsMap = admin.brokers().getOwnedNamespaces("test", list.get(0));
// since sla-monitor ns is not created nsMap.size() == 1 (for HeartBeat Namespace)
@@ -537,7 +541,7 @@ public void brokers() throws Exception {
for (String ns : nsMap.keySet()) {
NamespaceOwnershipStatus nsStatus = nsMap.get(ns);
if (ns.equals(
- NamespaceService.getHeartbeatNamespace(pulsar.getLookupServiceAddress(), pulsar.getConfiguration())
+ NamespaceService.getHeartbeatNamespace(pulsar.getBrokerId(), pulsar.getConfiguration())
+ "/0x00000000_0xffffffff")) {
assertEquals(nsStatus.broker_assignment, BrokerAssignment.shared);
assertFalse(nsStatus.is_controlled);
@@ -545,10 +549,7 @@ public void brokers() throws Exception {
}
}
- String[] parts = list.get(0).split(":");
- Assert.assertEquals(parts.length, 2);
- Map nsMap2 = adminTls.brokers().getOwnedNamespaces("test",
- String.format("%s:%d", parts[0], pulsar.getListenPortHTTPS().get()));
+ Map nsMap2 = adminTls.brokers().getOwnedNamespaces("test", list.get(0));
Assert.assertEquals(nsMap2.size(), 2);
deleteNamespaceWithRetry("prop-xyz/ns1", false);
@@ -703,6 +704,10 @@ public void testInvalidDynamicConfigContentInMetadata() throws Exception {
Awaitility.await().until(() -> pulsar.getConfiguration().getBrokerShutdownTimeoutMs() == newValue);
// verify value is updated
assertEquals(pulsar.getConfiguration().getBrokerShutdownTimeoutMs(), newValue);
+ // reset config
+ pulsar.getConfiguration().setBrokerShutdownTimeoutMs(0L);
+ // restart broker
+ restartBroker();
}
/**
@@ -801,6 +806,8 @@ public void namespaces() throws Exception {
TenantInfoImpl tenantInfo = new TenantInfoImpl(Set.of("role1", "role2"),
Set.of("test", "usw"));
admin.tenants().updateTenant("prop-xyz", tenantInfo);
+ Awaitility.await().untilAsserted(() ->
+ assertEquals(admin.tenants().getTenantInfo("prop-xyz").getAllowedClusters(), Set.of("test", "usw")));
assertEquals(admin.namespaces().getPolicies("prop-xyz/ns1").bundles, PoliciesUtil.defaultBundle());
@@ -933,8 +940,7 @@ public void persistentTopics(String topicName) throws Exception {
assertEquals(topicStats.getSubscriptions().get(subName).getConsumers().size(), 1);
assertEquals(topicStats.getSubscriptions().get(subName).getMsgBacklog(), 10);
assertEquals(topicStats.getPublishers().size(), 0);
- assertEquals(topicStats.getOwnerBroker(),
- pulsar.getAdvertisedAddress() + ":" + pulsar.getConfiguration().getWebServicePortTls().get());
+ assertEquals(topicStats.getOwnerBroker(), pulsar.getBrokerId());
PersistentTopicInternalStats internalStats = admin.topics().getInternalStats(persistentTopicName, false);
assertEquals(internalStats.cursors.keySet(), Set.of(Codec.encode(subName)));
@@ -3191,6 +3197,9 @@ public void testTopicBundleRangeLookup() throws PulsarAdminException, PulsarServ
TenantInfoImpl tenantInfo = new TenantInfoImpl(Set.of("role1", "role2"),
Set.of("test", "usw"));
admin.tenants().updateTenant("prop-xyz", tenantInfo);
+ Awaitility.await().untilAsserted(() ->
+ assertEquals(admin.tenants().getTenantInfo("prop-xyz").getAllowedClusters(),
+ tenantInfo.getAllowedClusters()));
admin.namespaces().createNamespace("prop-xyz/getBundleNs", 100);
assertEquals(admin.namespaces().getPolicies("prop-xyz/getBundleNs").bundles.getNumBundles(), 100);
@@ -3384,6 +3393,9 @@ public void testCreateAndDeleteNamespaceWithBundles() throws Exception {
TenantInfoImpl tenantInfo = new TenantInfoImpl(Set.of("role1", "role2"),
Set.of("test", "usw"));
admin.tenants().updateTenant("prop-xyz", tenantInfo);
+ Awaitility.await().untilAsserted(() ->
+ assertEquals(admin.tenants().getTenantInfo("prop-xyz").getAllowedClusters(),
+ tenantInfo.getAllowedClusters()));
String ns = BrokerTestUtil.newUniqueName("prop-xyz/ns");
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminTest.java
index 3caeb591bc8f3..2894903c0d0c1 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminTest.java
@@ -302,7 +302,7 @@ public void clusters() throws Exception {
NamespaceIsolationDataImpl policyData = NamespaceIsolationDataImpl.builder()
.namespaces(Collections.singletonList("dummy/colo/ns"))
- .primary(Collections.singletonList("localhost" + ":" + pulsar.getListenPortHTTP()))
+ .primary(Collections.singletonList(pulsar.getAdvertisedAddress()))
.autoFailoverPolicy(AutoFailoverPolicyData.builder()
.policyType(AutoFailoverPolicyType.min_available)
.parameters(parameters1)
@@ -722,11 +722,12 @@ public void brokers() throws Exception {
assertTrue(res instanceof Set);
Set activeBrokers = (Set) res;
assertEquals(activeBrokers.size(), 1);
- assertEquals(activeBrokers, Set.of(pulsar.getAdvertisedAddress() + ":" + pulsar.getListenPortHTTP().get()));
+ assertEquals(activeBrokers, Set.of(pulsar.getBrokerId()));
Object leaderBrokerRes = asyncRequests(ctx -> brokers.getLeaderBroker(ctx));
assertTrue(leaderBrokerRes instanceof BrokerInfo);
BrokerInfo leaderBroker = (BrokerInfo)leaderBrokerRes;
- assertEquals(leaderBroker.getServiceUrl(), pulsar.getLeaderElectionService().getCurrentLeader().map(LeaderBroker::getServiceUrl).get());
+ assertEquals(leaderBroker.getBrokerId(),
+ pulsar.getLeaderElectionService().getCurrentLeader().map(LeaderBroker::getBrokerId).get());
}
@Test
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AnalyzeBacklogSubscriptionTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AnalyzeBacklogSubscriptionTest.java
index 64b2a58ab86e8..f8aa3dc355d92 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AnalyzeBacklogSubscriptionTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AnalyzeBacklogSubscriptionTest.java
@@ -154,17 +154,17 @@ private void verifyBacklog(String topic, String subscription, int numEntries, in
AnalyzeSubscriptionBacklogResult analyzeSubscriptionBacklogResult
= admin.topics().analyzeSubscriptionBacklog(topic, subscription, Optional.empty());
- assertEquals(numEntries, analyzeSubscriptionBacklogResult.getEntries());
- assertEquals(numEntries, analyzeSubscriptionBacklogResult.getFilterAcceptedEntries());
- assertEquals(0, analyzeSubscriptionBacklogResult.getFilterRejectedEntries());
- assertEquals(0, analyzeSubscriptionBacklogResult.getFilterRescheduledEntries());
- assertEquals(0, analyzeSubscriptionBacklogResult.getFilterRescheduledEntries());
+ assertEquals(analyzeSubscriptionBacklogResult.getEntries(), numEntries);
+ assertEquals(analyzeSubscriptionBacklogResult.getFilterAcceptedEntries(), numEntries);
+ assertEquals(analyzeSubscriptionBacklogResult.getFilterRejectedEntries(), 0);
+ assertEquals(analyzeSubscriptionBacklogResult.getFilterRescheduledEntries(), 0);
+ assertEquals(analyzeSubscriptionBacklogResult.getFilterRescheduledEntries(), 0);
- assertEquals(numMessages, analyzeSubscriptionBacklogResult.getMessages());
- assertEquals(numMessages, analyzeSubscriptionBacklogResult.getFilterAcceptedMessages());
- assertEquals(0, analyzeSubscriptionBacklogResult.getFilterRejectedMessages());
+ assertEquals(analyzeSubscriptionBacklogResult.getMessages(), numMessages);
+ assertEquals(analyzeSubscriptionBacklogResult.getFilterAcceptedMessages(), numMessages);
+ assertEquals(analyzeSubscriptionBacklogResult.getFilterRejectedMessages(), 0);
- assertEquals(0, analyzeSubscriptionBacklogResult.getFilterRescheduledMessages());
+ assertEquals(analyzeSubscriptionBacklogResult.getFilterRescheduledMessages(), 0);
assertFalse(analyzeSubscriptionBacklogResult.isAborted());
}
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/PersistentTopicsTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/PersistentTopicsTest.java
index 7939b19283946..23cb413614f9a 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/PersistentTopicsTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/PersistentTopicsTest.java
@@ -31,6 +31,8 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertSame;
import static org.testng.Assert.assertTrue;
import java.lang.reflect.Field;
import java.util.ArrayList;
@@ -65,6 +67,7 @@
import org.apache.pulsar.broker.service.Topic;
import org.apache.pulsar.broker.web.PulsarWebResource;
import org.apache.pulsar.broker.web.RestException;
+import org.apache.pulsar.client.admin.LongRunningProcessStatus;
import org.apache.pulsar.client.admin.PulsarAdmin;
import org.apache.pulsar.client.admin.PulsarAdminException;
import org.apache.pulsar.client.admin.Topics;
@@ -87,6 +90,7 @@
import org.apache.pulsar.common.partition.PartitionedTopicMetadata;
import org.apache.pulsar.common.policies.data.AuthAction;
import org.apache.pulsar.common.policies.data.ClusterData;
+import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats;
import org.apache.pulsar.common.policies.data.Policies;
import org.apache.pulsar.common.policies.data.RetentionPolicies;
import org.apache.pulsar.common.policies.data.TenantInfoImpl;
@@ -1364,21 +1368,12 @@ public void testGetMessageById() throws Exception {
Message message2 = admin.topics().getMessageById(topicName2, id2.getLedgerId(), id2.getEntryId());
Assert.assertEquals(message2.getData(), data2.getBytes());
- Message message3 = null;
- try {
- message3 = admin.topics().getMessageById(topicName2, id1.getLedgerId(), id1.getEntryId());
- Assert.fail();
- } catch (Exception e) {
- Assert.assertNull(message3);
- }
-
- Message message4 = null;
- try {
- message4 = admin.topics().getMessageById(topicName1, id2.getLedgerId(), id2.getEntryId());
- Assert.fail();
- } catch (Exception e) {
- Assert.assertNull(message4);
- }
+ Assert.expectThrows(PulsarAdminException.NotFoundException.class, () -> {
+ admin.topics().getMessageById(topicName2, id1.getLedgerId(), id1.getEntryId());
+ });
+ Assert.expectThrows(PulsarAdminException.NotFoundException.class, () -> {
+ admin.topics().getMessageById(topicName1, id2.getLedgerId(), id2.getEntryId());
+ });
}
@Test
@@ -1459,6 +1454,69 @@ public void onSendAcknowledgement(Producer producer, Message message, MessageId
.compareTo(id2) > 0);
}
+ @Test
+ public void testGetMessageIdByTimestampWithCompaction() throws Exception {
+ TenantInfoImpl tenantInfo = new TenantInfoImpl(Set.of("role1", "role2"), Set.of("test"));
+ admin.tenants().createTenant("tenant-xyz", tenantInfo);
+ admin.namespaces().createNamespace("tenant-xyz/ns-abc", Set.of("test"));
+ final String topicName = "persistent://tenant-xyz/ns-abc/testGetMessageIdByTimestampWithCompaction";
+ admin.topics().createNonPartitionedTopic(topicName);
+
+ Map