-
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
[PIP-338] Add default lookup listener and fix inconsistency with listener's usage between different protocols #22039
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,172 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# PIP-338: Add default lookup listener and fix inconsistency with listener's usage between different protocols | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Background knowledge | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Currently, the `internalListenerName` specified in the broker config assists in selecting a listener from the `advertisedListeners` list to define the service URL for broker-to-broker communication. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
On the client side, for lookup requests, there is an option to provide the listener name either as a query parameter or in the header [X-Pulsar-ListenerName]. This choice indicates which listener should be chosen from the list of advertised listeners as a response to the client's request. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The expectation is that, in the event of a 200/307 response, the broker service address corresponding to the listener passed in the request will be returned, as outlined in PIP-95 (Pull Request: https://github.com/apache/pulsar/pull/12072). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Motivation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Issue in existing Code flow | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Even if the client sends a `listenerName` (PIP-91/PIP-95) corresponding to the http protocol broker’s addresses, it is only used in the `pulsar` and `pulsar+ssl` protocols and is not consistent for the other protocols. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
See [code](https://github.com/apache/pulsar/blob/a77fb6ed165ac8ad6968558077d80ea3edaf9a7e/pulsar-broker/src/main/java/org/apache/pulsar/broker/namespace/NamespaceService.java#L482) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (options.hasAdvertisedListenerName()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
AdvertisedListener listener = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
nsData.get().getAdvertisedListeners().get(options.getAdvertisedListenerName()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (listener == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
future.completeExceptionally( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
new PulsarServerException("the broker do not have " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+ options.getAdvertisedListenerName() + " listener")); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
URI url = listener.getBrokerServiceUrl(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
URI urlTls = listener.getBrokerServiceUrlTls(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
future.complete(Optional.of(new LookupResult(nsData.get(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
url == null ? null : url.toString(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
urlTls == null ? null : urlTls.toString()))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
future.complete(Optional.of(new LookupResult(nsData.get()))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
As we can see, in the existing flow, the listenerName is not acknowledged in case of http protocol. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Here, it is trying to obtain the broker service url. If the listener has service url/tls (pulsar protocols) then they are used in the response, but the HTTP urls are from the internal listener only (when configured). Later in the call chain, the http urls are used for redirect location - which may be non resolvable for the client. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
See [code](https://github.com/apache/pulsar/blob/a77fb6ed165ac8ad6968558077d80ea3edaf9a7e/pulsar-broker/src/main/java/org/apache/pulsar/broker/lookup/TopicLookupBase.java#L103) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
```java | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (result.isRedirect()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
boolean newAuthoritative = result.isAuthoritativeRedirect(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
URI redirect; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
String redirectUrl = isRequestHttps() ? result.getLookupData().getHttpUrlTls() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
: result.getLookupData().getHttpUrl(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (redirectUrl == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
log.error("Redirected cluster's service url is not configured"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
throw new RestException(Response.Status.PRECONDITION_FAILED, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"Redirected cluster's service url is not configured."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
String lookupPath = topicName.isV2() ? LOOKUP_PATH_V2 : LOOKUP_PATH_V1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
String path = String.format("%s%s%s?authoritative=%s", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
redirectUrl, lookupPath, topicName.getLookupName(), newAuthoritative); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
path = listenerName == null ? path : path + "&listenerName=" + listenerName; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
redirect = new URI(path); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Need to have lookupListener | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The Pulsar broker has the concept of advertised listeners representing broker endpoints that are discoverable by, and accessible to, Pulsar clients. In addition, one listener may be designated as the internal listener, to be used for broker-to-broker communication. Listeners may be understood as named routes to the broker. If a listener name is not specified, the response contains the endpoint for the internal listener, or the first listener for the protocol. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
But the challenge arises when it considers the same internalListener for lookup requests redirects. This may result in returning an unresolvable broker service address to the clients. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
It may not be possible to pass the listenerName from the clients, which consequently may lead to cluster downtime. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
it might not be feasible to have the listerner config at the client side in every tech stack or connector. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Also, Currently, there is no option to pass the listener while using the admin APIs. As admin APIs can be called from an external network as well, the use of the internal listener’s broker service URL can lead all admin operations to get affected. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Moreover, as per current code provided above, even if the client provides a listenerName/listener header, the redirect urls are taken from the internal listener or the first listener with http protocol which may not be a client-resolvable http address. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Proposed Solution | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
To fix inconsistency with all protocol’s service url, we need to include all broker addresses associated with the listener name while returning the lookupResult, rather than solely the service URL | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Also, Introduce a broker-level property to determine the appropriate listener for lookup calls if listenerName is missing from the client request. This can help deal with the side effects of not having listerName on the client side when multiple advertisedListeners are being used. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Also, this can help to determine the broker service URL that needs to be returned in case of lookup redirects and also for using Pulsar admin API calls from outside the network. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This will help in having a more transparent listener identifier for internal and external communications. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+62
to
+80
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking out aloud here, I haven't come to a conclusion yet. In PIP-95 there's a solution that I'd prefer instead:
The main problem I have is that there is already a design for solving the problem, but that has never been implemented in Pulsar. I acknowledge that the benefit of the The limitation of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that there are inconsistencies in design and implementation. Maybe there were missing things in design of PIP-91 and 95 itself as well. I do not think a bind address mapping is going to solve this. For instance, the same http scheme/protocol based external facing url leads to multiple different protocol changes in output - http based lookup call can either result in the client using the pulsar protocol as the output, or an http based redirect url as an output. http based admin calls always end up with either 200 or http based redirect url. Both the above cases may have the same entry point in terms of bind address. Please correct me if my understanding is wrong here in the examples. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To add to this, I actually would refer to what kafka has been doing for a long while.. our proposal is actually very close to kafka KIP 103 which PIP-95 was actually supposed to be doing.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @grssam I hope the comment #22039 (comment) explains how this can be solved for the Pulsar Admin API http redirects. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about http lookup leading to pulsar protocol being used? The common use case for clients is to use the http load balancer address for initial service discovery, which means that the lookup service used by the client will be the HttpLookupService .. Now in this case, when the actual lookup happens, if its a redirect, the http redirect url is used but in case its a 200 response, the pulsar protocol address is used. Thus - leading to different output listener for the same bind address. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The code at pulsar/pulsar-broker/src/main/java/org/apache/pulsar/broker/namespace/NamespaceService.java Lines 189 to 197 in 3158fd3
findRedirectLookupResultAsync method ignores that listener name that is passed in in the LookupOptions parameter. That looks like a bug and not an implementation gap. :)
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those are exactly the inconsistencies that we are trying to fix here. Along with a default lookup listener so that we are not "forced" to just send a custom listener name from the client side without which the system does not work at all. A default shouldn't be something that is this broken for users that without overriding, things don't work at all. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Design & Implementation Details | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Approach | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
**1. Fix Code Flow - Inconsistency with listener usage with protocols other than pulsar and pulsar+ssl** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Currently, lookup result return the broker service url corresponding to the internal listerner in case of http or https protocol urls. To address the issue in the workflow, it is necessary to include all broker addresses associated with the listener name, rather than solely the service URL. There can be a check if all broker addresses corresponding to the given listener is null, then use the default listener(discussed below) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For topic lookups a Pulsar binary protocol endpoint is always returned. It doesn't make sense to return all endpoints for topic lookups. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, its confusing to see the http urls present in lookup response, and we are lacking historic info on why they are present. But if and when they are already present, they should be resolvable based in the ask of the request/default config. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes you are right, this is the bug that would need fixing, mentioned in the previous comment #22039 (comment) . |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
**2. Only return the service URL corresponding to the lookupListener** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This approach introduces one new configuration `lookupListenerName` in the broker.conf. The `lookupListenerName` is used to specify which service URL should be returned to the clients from the lookup requests or redirects. `lookupListenerName` should be present in the `advertisedListeners` list. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't make sense to me. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the users will configure an external, client resolvable listener name as the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How would you know what to use, the "internalListenerName" or the "lookupListenerName"? I think that "internalListenerName" is badly named, it's essentially the default listener name. From that perspective, the "lookupListenerName" is conflicting since it's another default. The problem is that lookups are needed also within the cluster and perhaps for multiple different listeners. The design of PIP-95 is that there isn't a single "external" listener. I think it's better to keep the current PIP-95 design and perhaps extend it to include That way it would be possible to have a different default listener name for different bind address/port as it is already possible for the Pulsar binary protocol. That was a scope limitation of PIP-95, mentioned also in this comment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Any documentation supporting this idea of internallistenerName being default? Because the way it is used currently, it makes it evident that the address represented by this listener is supposed to be an intenal usage thing. The core issue arises when your deterministic sticky named address is not resolvable outside the cluster. If we ignore that problem, then this is a non issue. Or in other words, the problem arises when we want to use deterministic sticky addresses to brokers as identifier rather than its IP address. One of the main reason to do this is the fact that this address will be used to identify a broker wrt bundle ownership i.e. even a single pod restart would lead to removal of that broker and creation of a "new" broker in the cluster. Another use case of having a static deterministic broker address is for the regex used in namespace isolation policies. Generally, these static addresses (stateful pod name) are not resolvable outside the k8s cluster. Thus, we have to set out advertised address as this static, non-client-resolvable address as we are using isolation groups feature and for the other reason mentioned above as well. Now as soon as we do that, we have to set the same address in the list of advertised addresses as well, that too, as the internal listener address other wise the check at this line breaks in because So, now, as per implementation itself, at this point, internalListenerName cannot be assumed as the default listener as its something only resolveable inside the pulsar cluster/k8s namespace.
Could you run us through how that would work for the case when a normal producer client does an http based lookup call only to use the native protocol response from it and to also ensure that redirect in case of 307 response of that lookup call also works correctly? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It would be expected that there would be a way to address that. There are simply bugs and gaps in the current solution as it has been discussed and I think that we are pretty close in getting to a consensus.
It's referred to as "internal listener", but in the implementation, it is used as the default. For example pulsar/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BrokerService.java Lines 543 to 552 in 5df97b4
Since the "internalListenerName" is set to the first listener if it is unset, there's always going to be one listener that is the "internal listener". In many ways it could be seen as a default.
The PRs #21894 & #21937 fixed the issue #21897 and there's no coupling from the brokerId to the "advertised listeners" any more. The fixes have been backported all the way to branch-3.0 . There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, after your PRs the issue of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
According to the current release policy, there's no active maintenance for 2.10 branch in the Apache Pulsar project, the dev mailing list thread is https://lists.apache.org/thread/n5zfhohvk7olodfx2gksxk9n5f2pt3h8 . The Apache Pulsar community and Apache Pulsar PMC could change this, but this is the current state. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I checked this in slack, and we will go from 2.10 to 3.0.x directly, but we were already halfway done with 2.9 to 2.10 upgrade and only in production found this bug. Currently there is no documentation on upgrade paths at all. All previous conversations with SN or on slack were indicating to do upgrades one version at a time. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This still holds true. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That's true, however that's a general rule in thumb. In this case since 3.0.x is the oldest actively supported, it makes sense to target it. If an upgrade path works between 2 versions in tests & experiments, there shouldn't be a reason why it couldn't be used. Obviously it might require some more care in validating if rollbacks are possible. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Users can set up the `lookupListenerName`. for example: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
existing configs: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
advertisedListeners=internal:pulsar://xyz-broker:6660,internal:pulsar+ssl://192.168.1.11:6651,external:http://192.168.1.11:8080 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
internalListenerName=internal | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
new config required: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
lookupListenerName=external | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
In the approach, when the client sends a lookup request to the broker to get the owner broker without the listnerName, the broker only returns one service URL that is with the same listener name for which the default value of the lookupListenerName is given in the broker configuration. Therefore, we must allow the client to get the corresponding service URL with the same advertised listener name as present in the broker config. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be covered in the broker code. pulsar/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java Lines 1207 to 1242 in 3158fd3
The advertised listeners are available in the BrokerLookupData records which are already used. Line 35 in 6514cdd
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A method could be added to PulsarWebResource for getting the listener name from a parameter or a header. There's no need to add these explicitly for each method in the API as has been done for the Topic lookup.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Primary goal here is also to limit the changes to server side only, because there are cases where there is either no scope of adding a listener name as query param or header in the current system. Even in the systems which can add this config, think 100s of different applications needing either a client upgrade or a config change to add this listener name. And in case we do end up asking all clients to send a specific listener in their API calls or lookup requests, then isn't that a default for us? Thus, the addition of a default value for the incoming listener name. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In PIP-95 design I think that the idea is that this could be added in a reverse proxy that is in front of Pulsar. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is becoming more and more clear that at least you are preferring the bind address solution. It seems to me that the right approach in our situation is to patch pulsar ourselves and maintain that patch version after version internally. I wish there was an agreement here but from the start, you have been advocating for a different approach that what our production currently needs. I will discuss this internally and close this PIP later today. I appreciate your time and effort in the discussion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Could you please answer this question from the other comment #22039 (comment)
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
First of all, that would require fixing the bug where the listenerName is ignored, that's mentioned in #22039 (comment) . The HTTP lookup already supports passing the listener name. That would obviously require passing the listener name from clients. The alternative without making changes to Pulsar would be to put a HTTP reverse proxy such as nginx in front of Pulsar REST calls that sets the "X-Pulsar-ListenerName" header. I believe that adding a header is also possible with nginx ingress controller. It's not optimal, but that's how PIP-95 has been scoped. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
My opinion is that a design should not force users to set defaults from outside pulsar systems. Why was it not discussed that this should be done via a config rather than asking either every client to send a value or setup another component or config to add a "required" header at the load balancer layer... Can we agree that there are cases where things can be miss in old PIP and not all discussed and approved PIPs should be taken as bible :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It's part of PIP-95 design and it is mentioned there. There are also comments about the scope limitation. I agree that it's not currently convenient when there isn't out-of-the-box support without using a reverse proxy to add the header value. That could be addressed with the bindAddresses solution which sets a default for each binding address/port, in the similar way as it has been addressed for the Pulsar binary protocol. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just to add to this point that even when listener name is being passed by the clients, as per the current implementation it is not addressed if its corresponding to the http protocol broker's address. The PIP not only focussing on adding a default listener for clients/external communication but also to fix this bug. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This approach's purpose is to keep client-side simple and handle situations when not having listenerName information can lead to breaking scenarios. By having the default value, clients should be able to connect with the broker even if the request lands to the broker who is not the owner and redirects to some other broker. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Changes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1. Fix the lookup result formation with all broker addresses rather than only service url | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
in NamespaceService.java in following methods: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
```java | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
- CompletableFuture<Optional<LookupResult>> findBrokerServiceUrl(NamespaceBundle bundle, LookupOptions options) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a LookupResult is not needed for addressing the Pulsar Admin API's redirects. Please check the previous comment about PulsarWebResource.maybeRedirectToBroker. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The to name some. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, there's a need to make things consistent in the implementation as you have suggested. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
- void searchForCandidateBroker(NamespaceBundle bundle, CompletableFuture<Optional<LookupResult>> lookupFuture, LookupOptions options) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
- CompletableFuture<LookupResult> createLookupResult(String candidateBroker, boolean authoritativeRedirect, final String advertisedListenerName) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
2. The pulsar broker should return the right listener for a given topic according to the lookup listener name which the broker returns to the clients. So, we can have a check in the PulsarWebResource | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PulsarWebResource { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if(clientListener is not present) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
use lookupListener from broker conf | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
else return listener from client configs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This check can be applied in all the below-mentioned classes which are extending the PulsarWebResource. This can be added while creating the lookupOptionsBuilder: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TopicLookupBase.java | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TopicBase.java | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PulsarWebResource.java | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Since we are supplying the listener solely for creating the lookupOptionsBuilder, it will exclusively apply to lookup requests and can be used for admin API calls because admin calls can come from outside the network as well. To note here, This config will not be utilized or override the internalListener usage for internal communication. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
### Configuration | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Add one configuration in the broker.conf | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# The Default value is absent, the broker uses the internal listener as the lookup listener redirects. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# This config can be used to specify the listener needs to be considered from the list of advertisedListeners for lookups requests if clients are not passing the listener information. This will be overriden in case the listener value is provided by the clients. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# lookupListenerName= | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#for backward compatibility, by default the value will be set as false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
preferHttpClientListenerOverInternalListener=false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
### Test Plan | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The behaviour after adding a lookupListenerConfig and preferHttpClientListenerOverInternalListener can be validated by having a test cases where | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CASE I: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The lookup requests should return the broker address corresponding to the lookup listener if listener not passed in the lookup request. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CASE II: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The lookup requests with listenerName in the query parameter. It should return the broker address corresponding to the listener mentioned in the query param. This is to verify the bug fix and also priority of listener in the request over the config | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CASE III: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The lookup request with listener corresponding to the http and https protocol broker’s address, should return the same in the lookup result to validate the bug fix | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CASE IV: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
To test backward compatibility, we can have a test where the service url corresponding to the internal listener should be returned if the `preferHttpClientListenerOverInternalListener` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
is set as false and no listener is passed in the request. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Public-facing Changes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
### Backward Compatibility | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The above mentioned approach(2) of having a new config as lookupListener is backward compatible as there should not be any impact on the existing users if this configuration would not be set. Users can set this configuration for listener selection for the external communication as per their usecase. The same listener would be picked up for the admin APIs as well if admin calls are coming outside the network. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Approach(1), Fixing the code flow for http protocol might be a breaking change for the clients where we are returning the all broker addresses corresponding to the listener rather than just having service url. This can be breaking for the clients who are used to this inconsistency of having internal listener’s service url as lookup request. To deal with this, we can have a flag - `preferHttpClientListenerOverInternalListener` which would indicate whether this change should be enabled for clients or not. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
By Default, the value of this flag can be set as false for backward compatibility. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Revert | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This fix can be controlled by a config - `preferHttpClientListenerOverInternalListener` which we are adding for a backward compatibility. So there would not be a need to revert this change. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Links | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Mailing List discussion thread: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Mailing List voting thread: |
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.
PIP-91 -> PIP-61. Please also provide links to the PIP definitions.