-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support of PrometheusRawMetricsProvider for the Pulsar-Proxy #14681
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really great work !
IIUC this change is totally backward compatible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, good work @cbornet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, looks good.
I think we need to address metrics providers that are added late to ensure they are added to the metricsServlet
.
pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyServiceStarter.java
Outdated
Show resolved
Hide resolved
public void addPrometheusRawMetricsProvider(PrometheusRawMetricsProvider metricsProvider) { | ||
metricsProviders.add(metricsProvider); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this method could be called from other threads in protocol handlers, I think we should consider using a concurrent data structure to back the metricsProviders
list. Technically, it appears that the current implementation in the broker does not use a concurrent data structure, so I could be wrong about this class's thread safety requirements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@codelipenghui as you contributed the code for the broker, can you say if we should use concurrent structures for PulsarService
's pendingMetricsProviders
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initially, it was only considered when loading plugins when starting the broker, which will not be modified after the broker started. To avoid any potential thread-safety issues(maybe some users want to modify after the broker started), using concurrent structures looks good to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to @michaeljmarshall 's question, otherwise LGTM to me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, just left a minor comment.
private static final int HTTP_STATUS_OK_200 = 200; | ||
private static final int HTTP_STATUS_INTERNAL_SERVER_ERROR_500 = 500; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason for introducing new http status code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@codelipenghui
because we are not importing org.eclipse.jetty.http.HttpStatus anymore
500 is used below
pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyService.java
Show resolved
Hide resolved
if (metricsServlet == null) { | ||
if (pendingMetricsProviders == null) { | ||
pendingMetricsProviders = new LinkedList<>(); | ||
synchronized (this) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about moving 'synchronized' to the signature of the method ?
if (pendingMetricsProviders != null) { | ||
pendingMetricsProviders.forEach(provider -> metricsServlet.addRawMetricsProvider(provider)); | ||
this.pendingMetricsProviders = null; | ||
synchronized (this) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about creating a method like this:
private synchronised void initMetricsServlet() {
this.metricsServlet = new PrometheusMetricsServlet(-1L, proxyConfig.getClusterName());
if (pendingMetricsProviders != null) {
pendingMetricsProviders.forEach(provider -> metricsServlet.addRawMetricsProvider(provider));
this.pendingMetricsProviders = null;
}
}
@@ -347,7 +349,9 @@ public void close() throws IOException { | |||
proxyAdditionalServlets = null; | |||
} | |||
|
|||
metricsServlet = null; | |||
synchronized (this) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about using a synchronised method here instead of using "synchronized (this)" ?
probably it is a matter of taste, but I think that "synchronized (this)" is quite hard to read
if (metricsServlet == null) { | ||
if (pendingMetricsProviders == null) { | ||
pendingMetricsProviders = new LinkedList<>(); | ||
synchronized (this) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about moving 'synchronized' to the signature of the method ?
pendingMetricsProviders.forEach(provider -> metricsServlet.addRawMetricsProvider(provider)); | ||
this.pendingMetricsProviders = null; | ||
|
||
synchronized (this) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about moving this code to a dedicated method ?
private synchronised void initMetricsServlet() {
..
}
@@ -356,6 +374,10 @@ public void close() throws IOException { | |||
} | |||
} | |||
|
|||
private synchronized void resetMetricsServlet() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getMetricsServlet should be synchronized
as well (spotbugs should not pass)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
this.pendingMetricsProviders = null; | ||
} | ||
|
||
createMetricsServlet(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
below we have a non synchronised access to metricsServlet.
I suggest to use a synchronised
getter and use it while calling addWebServerHandlers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be only one thread here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lgtm
@michaeljmarshall please take another look
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks for addressing my comments @cbornet!
) (cherry picked from commit ea95b28)
Motivation
PR #9021 added support for
PrometheusRawMetricsProvider
in the broker so that plugins can add their metrics to the Prometheus/metrics
endpoint.This change does the same for the Pulsar Proxy. Proxy plugins (additional servlets, extensions, ...) can then add their Prometheus providers with
ProxyService::addPrometheusRawMetricsProvider
.This helps having common metrics code for servlets and handlers/extensions when they can be deployed both in the broker and the proxy.
Modifications
PrometheusRawMetricsProvider
moved frompulsar-broker
topulsar-broker-common
under the same package name so the change is non-breaking.PrometheusMetricsServlet
non-specific to the broker and have anotherPulsarPrometheusMetricsServlet
that extends it for the broker.PrometheusMetricsGenerator
methods used by both the broker and the proxy to aPrometheusMetricsGeneratorUtils
class.ProxyService::addPrometheusRawMetricsProvider
method for use by plugins.PrometheusMetricsServlet
instead of the classicalMetricsServlet
and add registeredPrometheusRawMetricsProvider
to it onWebServer
startup.PrometheusMetricsServlet
is aware of the PrometheusCollectorRegistry
so it's backward compatible for existing plugins that are registering standard Prometheus metrics./metrics
endpoint.Verifying this change
This change is already covered by existing tests, such as
org.apache.pulsar.broker.stats.PrometheusMetricsTest
.This change added tests and can be verified by running
org.apache.pulsar.proxy.server.ProxyPrometheusMetricsTest
Does this pull request potentially affect one of the following parts:
If
yes
was chosen, please highlight the changesDocumentation
Check the box below or label this PR directly (if you have committer privilege).
Need to update docs?
doc-required
(If you need help on updating docs, create a doc issue)
no-need-doc
This should be first documented for the broker.
doc
(If this PR contains doc changes)