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

Reuse tls sessions accross clients of a connection pool #588

Merged
merged 8 commits into from
Mar 10, 2021
Merged

Reuse tls sessions accross clients of a connection pool #588

merged 8 commits into from
Mar 10, 2021

Conversation

nherment
Copy link
Contributor

@nherment nherment commented Mar 5, 2021

Closes #556

This implements the reuse of TLS sessions across clients of a connection pool.

When reviewing this, please pay particular attention to:

  • The clearing of the TLS Sessions cache. I'm not 100% confident the logic is as good as it can be.
  • Whether a Map of all active sessions is that much more valuable than just keeping the last known valid session and reusing that for new clients.
  • The performance tests comparing session reuse vs no-reuse are reliable on my laptop but aren't on the test VMs. This is disappointing and I would appreciate if you could verify that reusing sessions is actually a net perf gain for most use cases.
  • There is a new option to the Pool to disable the session cache reuse tls.reuseSessions. I'm not sure if such option should be exposed but felt it is a good way to disable the feature if it causes some issues with some servers.

@codecov-io
Copy link

codecov-io commented Mar 5, 2021

Codecov Report

Merging #588 (55803b2) into master (252b676) will decrease coverage by 0.26%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #588      +/-   ##
==========================================
- Coverage   99.15%   98.88%   -0.27%     
==========================================
  Files          16       16              
  Lines        1418     1437      +19     
==========================================
+ Hits         1406     1421      +15     
- Misses         12       16       +4     
Impacted Files Coverage Δ
lib/core/symbols.js 100.00% <ø> (ø)
lib/core/client.js 99.13% <100.00%> (+<0.01%) ⬆️
lib/pool.js 99.34% <100.00%> (+0.08%) ⬆️
lib/node/http-parser.js 75.00% <0.00%> (-25.00%) ⬇️
lib/core/request.js 94.89% <0.00%> (-2.05%) ⬇️
lib/core/util.js 94.73% <0.00%> (-1.32%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 252b676...55803b2. Read the comment docs.

lib/core/client.js Outdated Show resolved Hide resolved
lib/pool.js Outdated Show resolved Hide resolved
@nherment nherment marked this pull request as ready for review March 5, 2021 15:10
@nherment nherment requested a review from ronag March 5, 2021 15:10
@nherment
Copy link
Contributor Author

nherment commented Mar 5, 2021

Also please let me know if some documentation should be added to the readme

lib/pool.js Outdated Show resolved Hide resolved
lib/pool.js Outdated Show resolved Hide resolved
Copy link
Member

@ronag ronag left a comment

Choose a reason for hiding this comment

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

I wish we could implement this without coupling Client and Pool with hidden symbols. But i guess we can improve that in the future.

@ronag
Copy link
Member

ronag commented Mar 6, 2021

Any chance we could implement this by passing a connect method in the client options?

@nherment
Copy link
Contributor Author

nherment commented Mar 8, 2021

Any chance we could implement this by passing a connect method in the client options?

What would it help with? It'll definitely be more powerful but also come with some drawbacks if I understand correctly (e.g. re implementing the function in the pool).

@nherment nherment mentioned this pull request Mar 8, 2021
@ronag
Copy link
Member

ronag commented Mar 8, 2021

What would it help with? It'll definitely be more powerful but also come with some drawbacks if I understand correctly (e.g. re implementing the function in the pool).

Removes coupling between classes.

@nherment
Copy link
Contributor Author

nherment commented Mar 8, 2021

@ronag I mainly bring 4 changes:

  1. The Map/TLSSessionCache is gone in favor of a single session stored at the pool level. Thanks to @mcollina for this.
  2. The Client now publicly exposes the 'session' event instead of a hidden one.
  3. The logic to set the TLS session on top of the tls options in the pool is updated and hopefully more readable
  4. There is a flag to enable/disable the perf test of TLS session reuse

If I understand your suggestion of passing in a connect function, I feel it would be over-complicated as the pool would need to re-implement some logic that the client owns.

I saw 2 options to address your following comment:

I wish we could implement this without coupling Client and Pool with hidden symbols.

  • Either implement some kind of hooks in the Client that would allow the Pool to override the default behaviour
  • Or publicly expose the Client session event instead of using a hidden symbol.

@ronag
Copy link
Member

ronag commented Mar 8, 2021

I think I'm happy with exposing the session event.

Copy link
Member

@ronag ronag left a comment

Choose a reason for hiding this comment

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

LGTM, good job!

@nherment
Copy link
Contributor Author

nherment commented Mar 8, 2021

@ronag or @mcollina could you please merge this for me? I don't have the access rights to do it.

@ronag
Copy link
Member

ronag commented Mar 8, 2021

Waiting for review from @mcollina

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.

Code is good to go!

lib/core/client.js Outdated Show resolved Hide resolved
@@ -130,3 +130,103 @@ test('TLS should reuse sessions', { skip: nodeMajor < 11 }, t => {

t.end()
})

test('A pool should be able to reuse TLS sessions between clients', { skip: nodeMajor < 11 }, t => {
Copy link
Member

Choose a reason for hiding this comment

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

What happens if this is used in node v10? Is it crashing? We should document and add a a test for it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

i don't immediately see why this wouldn't run in node 10 but i could easily be missing something obvious

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ronag @delvedor @mcollina do you remember why that was done for the Client's session reuse?
This code covers the same path so I decided to reuse the node version skip.

Copy link
Member

@delvedor delvedor Mar 9, 2021

Choose a reason for hiding this comment

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

The session event has been added in Node v11.
Feel free to add a comment for posterity!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you!

@mcollina
Copy link
Member

mcollina commented Mar 8, 2021

Have you done any performance testing by chance? Did you see any improvement?

@@ -159,11 +166,18 @@ class Pool extends EventEmitter {

if (!client) {
if (!this[kConnections] || this[kClients].length < this[kConnections]) {
client = new Client(this[kUrl], this[kOptions])
const options = { ...this[kOptions] }
Copy link
Collaborator

Choose a reason for hiding this comment

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

is a call to Object.assign({}, this[kOptions]) potentially faster?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

}

// User has chosen to opt out of TLS session reuse
if (tls.reuseSessions === false) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (tls.reuseSessions === false) {
if (!tls.reuseSessions) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion @Ethan-Arrowood. As mentioned in the PR's description this strict equality check is on purpose.
Changing to your code will change the behavior of the pool to not reuse sessions by default.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh I see now

.on('drain', this[kOnDrain])
.on('connect', this[kOnConnect])
.on('disconnect', this[kOnDisconnect])

if (!options.tls || (options.tls.reuseSessions !== false && !options.tls.session)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (!options.tls || (options.tls.reuseSessions !== false && !options.tls.session)) {
if (!options.tls || (options.tls.reuseSessions && !options.tls.session)) {

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 same comment as above regarding the default behavior applies here too.

@@ -130,3 +130,103 @@ test('TLS should reuse sessions', { skip: nodeMajor < 11 }, t => {

t.end()
})

test('A pool should be able to reuse TLS sessions between clients', { skip: nodeMajor < 11 }, t => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

i don't immediately see why this wouldn't run in node 10 but i could easily be missing something obvious

@nherment
Copy link
Contributor Author

nherment commented Mar 9, 2021

@mcollina regarding the perf tests I saw an improvement. You can test it locally. Unfortunately the perf tests dont always pass in the test VMs.

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

Copy link
Member

@ronag ronag left a comment

Choose a reason for hiding this comment

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

I'd like to understand the node 10 thing

@nherment
Copy link
Contributor Author

nherment commented Mar 9, 2021

Comment added regarding the node >= 11 version for the TLS session reuse tests.

Copy link
Member

@delvedor delvedor left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Collaborator

@Ethan-Arrowood Ethan-Arrowood left a comment

Choose a reason for hiding this comment

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

Great work!

@mcollina mcollina merged commit d27559e into nodejs:master Mar 10, 2021
crysmags pushed a commit to crysmags/undici that referenced this pull request Feb 27, 2024
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.

Reuse TLS sessions
6 participants