-
Notifications
You must be signed in to change notification settings - Fork 29.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
http: Change free sockets behavior to LIFO from FIFO. #31526
http: Change free sockets behavior to LIFO from FIFO. #31526
Conversation
On the other hand sockets that have not been used in a while might unnecessarily timeout. I think LIFO is fine but we do need to remove sockets from the free list when they timeout (which we currently don't). I'm unsure whether LIFO or FIFO is better here... Also, this might be a good idea to apply when the free list is full, i.e. instead of throwing away the most recent socket (as we currently do), we should throw away the least recently used. |
Why would we want to remove then until we actually try them? That's the current behavior. You have a good idea on the second part about what should be thrown away, updated commit pending. |
Because they have timed out. I don't think that's the current behavior? |
Are there event handlers still reading from the socket once they are placed in the agent pool? I don't believe so. If there aren't any handlers waiting to read how will the code know the socket has timed out? |
By adding a handler. See, #23752. |
this.maxFreeSockets > 0 && | ||
count <= this.maxSockets) { | ||
if (freeLen >= this.maxFreeSockets) { | ||
const oldest = this.freeSockets[name].shift(); |
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.
nit, freeSockets.shift()
if (freeLen >= this.maxFreeSockets) { | ||
const oldest = this.freeSockets[name].shift(); | ||
oldest.destroy(); | ||
} 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.
nit, else if (!freeSockets)
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'm not quite following, could you explain a bit more.
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.
the block inside of the else
is for the case when !freeSockets, could be simplified
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's a nit, no biggie
Would be nice if you could make a test for this. |
Defensively marking this semver-major for now. It's possible this wouldn't break anyone but we need to verify. |
Ping @nodejs/http |
A benchmark would be good. We might already have a relevant benchmark. I'm not sure.
At least in theory, isn't this an implementation detail the user need not worry about? (But I wouldn't oppose a test either. Just not sure if it's necessary.) |
Agreed. Nice to have. |
Sockets are added to the free list with .push() but they were being removed with .shift(). This meant the sockets where being removed in FIFO order, but this changes it to LIFO. Since older sockets may be closed based due to inactivity on the server it is more likely that a socket that is recently used will be able to successfully process the next request. Rather than destroying the last used socket destroy the oldest socket in the free list in push() on the last recently used socket.
4ef4a2d
to
5af8b39
Compare
Although there's some ambiguity around the /ping @nodejs/tsc Please review! (@jasnell described marking this as |
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 would like to have a test for this behavior.
Very good work. This is one of the main changes I did in https://github.com/mcollina/undici to improve the handling of keep-alive connections. |
There is a related issue to this that we might want to consider before landing this. We have a problem with sockets in the freelist that timeout are not removed from the list. This change might make that worse, since the least recently used socket is less likely to be used and thus timeout, then when it is actually used the request using it would fail. |
@ronag Does this mean that the request isn't retried on another connection automatically or is the request simply failed with a connection reset error? |
@ronag is there a test case that stresses this condition? |
If the user doesn't abort the request in a
Nope, would be nice to have a tests for it. |
Just to clarify, I believe my concern is already a problem, however this PR might make it worse. |
@rustyconover: I openend a separate PR to address my concern. I think this PR is good as is once a test is added, though I would prefer to wait for #32000 to land before landing this. |
@ronag I just wrote a test for the LIFO behavior of this PR. Let me know what you think. |
@mcollina I just added a test. |
Add a test that ensures the HTTP agent reuses sockets in a LIFO fashion rather than FIFO.
9aad728
to
b43479f
Compare
@mcollina I believe I've addressed your suggestions in the updated commits. Please let me know what you think. Thank you! 🙏 |
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
@rustyconover this has conflicts |
Implemented in #33278 |
Great! 👍 |
Sockets are added to the free list with .push() but they were
being removed with .shift(). This meant the sockets where being
removed in FIFO order, but this changes it to LIFO. Since older
sockets may be closed based due to inactivity on the server it is
more likely that a socket that is recently used will be able to
successfully process the next request.
make -j4 test
(UNIX), orvcbuild test
(Windows) passes