-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Assign random ports in Config #43220
base: main
Are you sure you want to change the base?
Conversation
🎊 PR Preview 5c73d3c has been successfully built and deployed to https://quarkus-pr-main-43220-preview.surge.sh/version/main/guides/
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This is really nice, but the CI failures seem related |
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.
This approach is not going to work, there are several things missing:
- 0 and negative ports are random, BUT every server starting on 0/-1/-2 has the same one (like if you use 0 in gRPC and gRPC client, it's the same port
- the whole rest assured support has been dropped - that's why we have these system properties.
- the socker open and immediately closed followed by a bind is likely going to fail or be very brittle.
@@ -40,7 +40,10 @@ public boolean equals(Object obj) { | |||
@Deprecated(forRemoval = true) | |||
@JsonIgnore | |||
public boolean isMixedModule() { | |||
return "io.quarkus".equals(groupId) && ("quarkus-core".equals(artifactId) || "quarkus-messaging".equals(artifactId)); | |||
return "io.quarkus".equals(groupId) && ("quarkus-core".equals(artifactId) || | |||
"quarkus-vertx-http".equals(artifactId) || |
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.
Could you open issues for all these extensions to ask them to switch to the new interface-based model?
(Damned, I'm involved in all 3 :-))
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.
Sure, but I'm happy to do that.
I have all the vertx-http work already done, but I haven't pushed it yet because many extensions rely on the config object to obtain quarkus.http.port
, and this will be a big breaking change.
To move that forward, I think we need to agree on this random port implementation, and then come up with a specific API (that I've intended to proposed separately), to retrieve useful things. Something like:
class QuarkusRuntime {
static Integer httpPort()
...
}
Ideally, we should offer this API before the vert-x config changes so that users who have to move can use the new API instead.
|
||
String host = config.host(); | ||
if (LaunchMode.current().equals(LaunchMode.TEST)) { | ||
if (config.testPort() == 0) { |
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.
No that is not correct,
Port 0 is a random port, but everything with port 0 uses the same
Port -1 is a random port, but everything with port -1 uses the same
...
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.
and that's across all extensions.
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.
I didn't notice that the client was in a separate group. Let me fix that.
}); | ||
} | ||
|
||
private static String assignRandomPort(SocketAddress socketAddress) { |
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.
You cannot do that, that's super brittle.
OS may close things asynchronously, the bind on the same port may fail.
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.
Locally and in the CI, things seem to have worked.
What do you suggest to do instead?
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 I proposed in the issue somewhere:
- a config source that provides the properties for 0, -1, -2...
- they get the port as you do but add a grace period (configurable)
- the config source does not recompute for a given "random id"
Another tricky thing is that I would like to have a way to keep the current behavior somehow because it got hardened enough.
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.
Ok, I didn't notice that we have some special meaning with port numbers here:
Lines 48 to 61 in b41b6ed
/** | |
* When the http port is set to 0, replace it by this value to let Vert.x choose a random port | |
*/ | |
public static final int RANDOM_PORT_MAIN_HTTP = -1; | |
/** | |
* When the https port is set to 0, replace it by this value to let Vert.x choose a random port | |
*/ | |
public static final int RANDOM_PORT_MAIN_TLS = -2; | |
/** | |
* When the management port is set to 0, replace it by this value to let Vert.x choose a random port | |
*/ | |
public static final int RANDOM_PORT_MANAGEMENT = -3; |
I can implement that behaviour. What threw me off, is that when I was trying the current version and set 0
to port
and ssl-port
I would get two different values.
Another tricky thing is that I would like to have a way to keep the current behavior somehow because it got hardened enough.
Hum... we could add a flag to use new / old behaviour.
extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpConfig.java
Show resolved
Hide resolved
Yes, but not related to the main changes. One is an inject I added that does not work in native, and the other is a config update in the rest client test because I've removed the workaround we had for the URL reload. I'll fix those. |
It still works. REST Assured relies on quarkus/test-framework/common/src/main/java/io/quarkus/test/common/RestAssuredURLManager.java Lines 67 to 119 in be7d0e7
The system properties trick was just a way to mutate system properties and have |
cc8a740
to
bc85cc0
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
public Integer apply(final Integer socketAddress) { | ||
try (ServerSocket serverSocket = new ServerSocket(0, 0, getByName(host))) { | ||
serverSocket.setReuseAddress(false); | ||
return serverSocket.getLocalPort(); |
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.
We may need to add a grace period here.
I remember that windows can be tricky.
(optional, user runtime config)
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.
My understanding is that if you set serverSocket.setReuseAddress(true);
if will allow you to bind it again even if the socket is in timeout: https://docs.oracle.com/javase/8/docs/api/java/net/ServerSocket.html#setReuseAddress-boolean-
Or are you thinking about something else?
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.
It starts to look good.
We need to see if we can have a way to restore the previous behavior (with a flag or something, to ease migration).
bc85cc0
to
2c3e19e
Compare
From the user / consumer side there is no migration required. Is the concern that this may not work and that we have something to fall back to? My concern is that some of the fixes that we need are incompatible with the current implementation, and we can't do the fixes if we have such flag :) |
Status for workflow
|
Status for workflow
|
Unfortunately, I'm pretty sure that the new way can fail in some environments. So, we need a way to go back to the previous behavior where the OS gives the port during the bind and not "leased". It's just a safety net. |
Do you know which environments may fail so we can have a look? I understand the concern. On the other hand, if we are required to have such a safety net, then I feel there is no point in this PR. We would have to keep all our workarounds with this on top. Plus, any fixes that we need to do require awareness of both modes, and unfortunately, the current mode is incompatible with the fixes we need, hence the PR. Other options to explore:
|
Windows has always been the tricky one, of course. It may have improved, but we cannot discard the fact that old windows would still need seconds after the close to release the resources. |
Windows CI runs, seem happy :) Did you check https://docs.oracle.com/javase/8/docs/api/java/net/ServerSocket.html#setReuseAddress-boolean-? |
Yes, I already tried that when I had the issue, no luck. But that was a long time ago. I would still enable it, as it may prevent the issue. If we merge this, we need to be aware that we might have to revert abruptly if it makes random port unusable on some platforms. |
We can add a temporary configuration to revert to the old behavior if something goes wrong and hold some additional fixes. If there are issues, users set that configuration, and we avoid having to revert abruptly. The goal would be to have this mode only if there are no issues. Is this what you were advocating? I was under the impression that you preferred to keep the current mode permanently. |
This is definitely the wrong approach. Configuration is a one-way street for good reason. Also, Clement is completely correct that the OS is assigning these ports taking into account multiple factors that we cannot predict, and we therefore cannot safely emulate this behavior without introducing problems that will require workarounds which in turn will lead to more problems. This is all an end-run around what seems to be the fundamental problem of not having a good way to get information out of the running system in a uniform way. Let's solve that actual problem instead. Configuration is not the right tool for this. Perhaps we need an analogous management API instead. I'll also add one comment on this:
I hope we are all very, very clear that "running in CI" is not the same as "this is correct" or even "this is a good idea". There's no reason we can't come up with a solution here that is guaranteed to work 100% of the time. |
We did discuss this some time ago, and I agree. On the other hand, I think it is reasonable to expect that All is fine when I set it to a fixed value. We may argue that we are not getting the correct runtime value, but I'm explicitly saying: "I want to use this port" and I'm configuring my server this way, so any check for the configuration must return the same values as the server. When I delegate the configuration to the server (as in the case of Even with a management API, we have two problems to solve:
We could disallow For the second part is more or less solvable by another config phase (yacks!). Alternatively, we would have to selectively reload configurations that may contain the port (like we did for the REST Client and other places), knowing that the port can be referenced in other configurations that we are not reloading. |
It does! But the confusion is the meaning of this. What it means - in fact - is "the port number that was configured". It does not mean "the port number being used".
The expectation is what is wrong. The configured value should always be returned.
You don't.
If there is a configuration which can either be an explicit port number (i.e. socket address) or the port number (i.e. socket address) of an existing service, then the configuration property type should not be
It works fine when you're referencing other configuration, which is what expressions are for; but in this case we don't want to reference configuration, we want to reference the run time reality. It may well be that the run time reality reflects the configuration in, say, 80% of cases (which I wouldn't call "perfect"). But that doesn't make it the right tool. It's the right tool when it works in 100% of cases. |
If we agree to stop using |
It assigns a random port during configuration and overrides the ports configuration (when they are
0
) with the assigned random port, meaning that any access to the port configuration reflects the requested port without any workarounds.