Skip to content
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

reusePort = false now default; honored regardless of numThreads #50

Conversation

timotheecour
Copy link
Contributor

@timotheecour timotheecour commented Jul 4, 2021

/cc @dom96

before PR

in terminal tab 1:
nim r --threads jester/tests/example.nim

in terminal tab 2:
nim r --threads jester/tests/example.nim # no failure
curl http://0.0.0.0:5000 # sends cmd to 1st server

after PR

in terminal tab 1:
nim r --threads jester/tests/example.nim

in terminal tab 2:
nim r --threads jester/tests/example.nim # fails with: Address already in use

after this PR, dom96/jester#278 can be merged which will allow forwarding reusePort param from jester to httpbeast and honor this flag

(see also nim-lang/Nim#18428 which improves errmsg on nim side)

@timotheecour timotheecour changed the title reusePort = false now default; honored regarless of numThreads reusePort = false now default; honored regardless of numThreads Jul 4, 2021
@timotheecour
Copy link
Contributor Author

ping @dom96

Comment on lines +480 to +483
if not settings.reusePort and numThreads > 1:
let server = newSocketAux(settings)
close(server) # binds to a temporary socket to honor `reusePort = false`
settings2.reusePort = true
Copy link
Owner

Choose a reason for hiding this comment

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

I don't like this. It completely ignores the user's wishes. If I ask for reuse port to be set to false then I want it to be set to false, not for httpbeast to assume it knows best and set it to true anyway.

reusePort should be true by default and we shouldn't be doing this magic. You can just as easily do this "bind trick" without messing with what the user wants. Just check whether binding to the port works always, that will fix the problem you are fixing too.

Copy link
Contributor Author

@timotheecour timotheecour Jul 8, 2021

Choose a reason for hiding this comment

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

If I ask for reuse port to be set to false then I want it to be set to false, not for httpbeast to assume it knows best and set it to true anyway.

this will guarantee it'll fail, I can't find any use case for that; the way I implemented it works with sane behavior with both reusePort:true|false (and I thought we had agreed on that earlier).

You can add an additional enforceReusePortMultithread: bool flag if you want for users that don't want the override, but it's of little utility as it's a guaranteed exception raised.

What you're suggesting goes against the default set in stdlib, and users would have the wrong behavior by default (and would be subject to TOCTOU for numthreads=1)

Copy link
Contributor Author

@timotheecour timotheecour Jul 8, 2021

Choose a reason for hiding this comment

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

I'd be ok with the following if needded:
replace reusePort: bool by reusePort: ReusePort = rpOff with
type ReusePort = enum rpOff, rpOffStrict, rpOn
where default is same as in PR, and rpOffStrict causes reusePort=false and doesn't get reset by httpbeast.

Copy link
Owner

Choose a reason for hiding this comment

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

What you're suggesting goes against the default set in stdlib, and users would have the wrong behavior by default (and would be subject to TOCTOU for numthreads=1)

httpbeast is it's own http library, I don't see why it needs to be consistent with stdlib.

With this PR you are giving reusePort a completely different meaning. It's no longer a simple setting for whether SO_REUSE_PORT should be set, we shouldn't be assigning different meanings to simple flags to handle some edge cases. If you want to avoid TOCTOU then you can surely just set this flag to false.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the tristate i suggested would make it clear and un-ambiguous; possibly with better name type ReusePortPolicy

Copy link
Owner

Choose a reason for hiding this comment

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

people usually stick to defaults and this is the wrong default for reasons already discussed (pre-existing server intercepts client connections), and inconsistent with what jester has been using and stdlib's very own httpserver

But this is the right default for httpbeast because it relies on it for performance. This is only the wrong default because it allows other software to bind a socket on the same port, unfortunately that's the price you pay for performance and I would argue POSIX's design choices.

in which case would this bind to a dummy socket happen? a user setting needs to control whether fail-fast can happen or not (regardless of threads:on|off or numThreads = 1 or > 1). Which user setting is that in your proposal?

The dummy socket bind would happen only to detect that another instance of httpbeast (or another program) is running on that port.

Why do we need a user setting for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But this is the right default for httpbeast because it relies on it for performance

neither this PR nor any of the alternatives I've suggested has any impact (positive or negative) on performance.

Why do we need a user setting for this?

I may want to run multithread httpbeast on port 1234 and then another instance of the same program (or a different program) on the same port 1234, for eg for resilience or if I want N process times P threads instead of N*P threads. This should be up to user setting whether to fail-fast if a port is already taken.

Copy link
Owner

Choose a reason for hiding this comment

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

neither this PR nor any of the alternatives I've suggested has any impact (positive or negative) on performance.

Yes, but you're doing that by ignoring the flag. Like I said, that's not acceptable.

I may want to run multithread httpbeast on port 1234 and then another instance of the same program (or a different program) on the same port 1234, for eg for resilience or if I want N process times P threads instead of N*P threads. This should be up to user setting whether to fail-fast if a port is already taken.

Alright, fine so let's have another flag for this: failOnExistingPort or something.

Copy link
Contributor Author

@timotheecour timotheecour Jul 15, 2021

Choose a reason for hiding this comment

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

Yes, but you're doing that by ignoring the flag. Like I said, that's not acceptable.

the irony is that jester is ignoring this flag when not using httpbeast, so I don't see how neither the status quo nor your proposal is better. In any case, for the sake of moving on, I've opened an alternative PR, see #52 which uses failOnExistingPort, which should be followed by dom96/jester#281 (in jester) which forwards jester.reusePort=false to httpbeast.failOnExistingPort=true (IMO less clean than what I was doing and subject to TOCTOU when numThreads=1, unlike what I was doing in this PR)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok here's a version I'm happy with: #53, avoids TOCTOU, avoids ignoring a reusePort flag, sane default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants