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

http: Change free sockets behavior to LIFO from FIFO. #31526

Closed

Conversation

rustyconover
Copy link
Contributor

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), or vcbuild test (Windows) passes
  • commit message follows

@nodejs-github-bot nodejs-github-bot added the http Issues or PRs related to the http subsystem. label Jan 26, 2020
@ronag
Copy link
Member

ronag commented Jan 27, 2020

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.

@rustyconover
Copy link
Contributor Author

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.

@ronag
Copy link
Member

ronag commented Jan 27, 2020

Why would we want to remove then until we actually try them? That's the current behavior.

Because they have timed out. I don't think that's the current behavior?

@rustyconover
Copy link
Contributor Author

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?

@ronag
Copy link
Member

ronag commented Jan 27, 2020

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.

lib/_http_agent.js Outdated Show resolved Hide resolved
this.maxFreeSockets > 0 &&
count <= this.maxSockets) {
if (freeLen >= this.maxFreeSockets) {
const oldest = this.freeSockets[name].shift();
Copy link
Member

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 {
Copy link
Member

Choose a reason for hiding this comment

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

nit, else if (!freeSockets)

Copy link
Contributor Author

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.

Copy link
Member

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

Copy link
Member

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

@ronag
Copy link
Member

ronag commented Jan 27, 2020

Would be nice if you could make a test for this.

@jasnell jasnell added the semver-major PRs that contain breaking changes and should be released in the next major version. label Jan 27, 2020
@jasnell
Copy link
Member

jasnell commented Jan 27, 2020

Defensively marking this semver-major for now. It's possible this wouldn't break anyone but we need to verify.

@jasnell
Copy link
Member

jasnell commented Jan 27, 2020

Ping @nodejs/http

@Trott
Copy link
Member

Trott commented Jan 28, 2020

A benchmark would be good. We might already have a relevant benchmark. I'm not sure.

Would be nice if you could make a test for this.

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.)

@ronag
Copy link
Member

ronag commented Jan 28, 2020

At least in theory, isn't this an implementation detail the user need not worry about?

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.
@rustyconover
Copy link
Contributor Author

@ronag @Trott could you please mark this PR as author ready?

@Trott
Copy link
Member

Trott commented Feb 27, 2020

@ronag @Trott could you please mark this PR as author ready?

Although there's some ambiguity around the author ready label, I would say this is not yet author ready because it is semver-major and therefore requires two approvals from the TSC.

/ping @nodejs/tsc Please review! (@jasnell described marking this as semver-major as being done "defensively" so it might not be semver-major after all. Opinions one way or the other are welcome.)

Copy link
Member

@mcollina mcollina left a 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.

@mcollina
Copy link
Member

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.

@ronag
Copy link
Member

ronag commented Feb 27, 2020

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.

@rustyconover
Copy link
Contributor Author

@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?

@rustyconover
Copy link
Contributor Author

@ronag is there a test case that stresses this condition?

@ronag
Copy link
Member

ronag commented Feb 28, 2020

@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?

If the user doesn't abort the request in a 'timeout' event handler (this is optional at the moment), the socket might be incorrectly re-used and the corresponding request would potentially fail, probably with ECONNRESET.

@ronag is there a test case that stresses this condition?

Nope, would be nice to have a tests for it.

@ronag
Copy link
Member

ronag commented Feb 28, 2020

Just to clarify, I believe my concern is already a problem, however this PR might make it worse.

@ronag ronag mentioned this pull request Feb 28, 2020
4 tasks
@ronag
Copy link
Member

ronag commented Feb 28, 2020

@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.

@rustyconover
Copy link
Contributor Author

@ronag I just wrote a test for the LIFO behavior of this PR. Let me know what you think.

@rustyconover
Copy link
Contributor Author

I would like to have a test for this behavior.

@mcollina I just added a test.

Add a test that ensures the HTTP agent reuses sockets
in a LIFO fashion rather than FIFO.
@rustyconover
Copy link
Contributor Author

@mcollina I believe I've addressed your suggestions in the updated commits. Please let me know what you think. Thank you! 🙏

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

lgtm

@ronag
Copy link
Member

ronag commented Mar 11, 2020

@rustyconover this has conflicts

@mcollina
Copy link
Member

Implemented in #33278

@mcollina mcollina closed this May 20, 2020
@rustyconover
Copy link
Contributor Author

Great! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
http Issues or PRs related to the http subsystem. semver-major PRs that contain breaking changes and should be released in the next major version.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants