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

tls: group chunks into TLS segments #27861

Closed
wants to merge 1 commit into from

Conversation

mildsunrise
Copy link
Member

TLSWrap::DoWrite() now concatenates data chunks and makes a single call to SSL_write(). Grouping data into a single segment:

  • reduces network overhead: by factors of even 2 or 3 in usages like http2 or form-data
  • improves security: segment lengths can reveal lots of info, i.e. with form-data, how many fields are sent and the approximate length of every individual field and its headers
  • reduces encryption overhead: a quick benchmark showed a ~30% CPU time decrease for an extreme case, see request body sent in separate SSL segment (bug at 9.6.0) #27573 (comment)

Fixes: #27573

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included ← are tests needed?
  • documentation is changed or added ← does this apply?
  • commit message follows commit guidelines

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. tls Issues and PRs related to the tls subsystem. labels May 24, 2019
@mildsunrise
Copy link
Member Author

test-http2-large-write-destroy is broken... I checked and there seems to be a bug in http2, I've opened #27863.

@mildsunrise
Copy link
Member Author

mildsunrise commented May 25, 2019

Benchmarks seem good!

                                                             confidence improvement accuracy (*)    (**)    (***)
 tls/convertprotocols.js n=1                                                 4.43 %       ±9.84% ±13.14%  ±17.18%
 tls/convertprotocols.js n=50000                                             0.05 %       ±7.38%  ±9.83%  ±12.80%
 tls/secure-pair.js size=1024 securing='SecurePair' dur=5           ***     92.54 %       ±3.08%  ±4.13%   ±5.44%
 tls/secure-pair.js size=1024 securing='TLSSocket' dur=5            ***    105.94 %       ±3.16%  ±4.26%   ±5.64%
 tls/secure-pair.js size=1048576 securing='SecurePair' dur=5                -0.01 %       ±1.53%  ±2.04%   ±2.67%
 tls/secure-pair.js size=1048576 securing='TLSSocket' dur=5                  0.45 %       ±1.63%  ±2.17%   ±2.83%
 tls/secure-pair.js size=2 securing='SecurePair' dur=5              ***   2236.19 %      ±20.65% ±27.83%  ±36.94%
 tls/secure-pair.js size=2 securing='TLSSocket' dur=5               ***   2779.25 %      ±26.17% ±35.26%  ±46.81%
 tls/throughput.js size=1024 type='asc' dur=5                       ***     58.21 %       ±2.84%  ±3.82%   ±5.07%
 tls/throughput.js size=1024 type='buf' dur=5                       ***     64.49 %       ±3.20%  ±4.30%   ±5.70%
 tls/throughput.js size=1024 type='utf' dur=5                       ***     63.08 %       ±0.82%  ±1.10%   ±1.43%
 tls/throughput.js size=1048576 type='asc' dur=5                             0.27 %       ±0.43%  ±0.57%   ±0.75%
 tls/throughput.js size=1048576 type='buf' dur=5                      *      0.43 %       ±0.37%  ±0.50%   ±0.65%
 tls/throughput.js size=1048576 type='utf' dur=5                      *      0.37 %       ±0.30%  ±0.40%   ±0.52%
 tls/throughput.js size=2 type='asc' dur=5                          ***    446.80 %       ±4.08%  ±5.49%   ±7.27%
 tls/throughput.js size=2 type='buf' dur=5                          ***   1506.14 %       ±9.31% ±12.55%  ±16.65%
 tls/throughput.js size=2 type='utf' dur=5                          ***    467.60 %       ±3.63%  ±4.89%   ±6.46%
 tls/tls-connect.js dur=5 concurrency=1                                      0.50 %       ±4.82% ±21.82% ±188.62%
 tls/tls-connect.js dur=5 concurrency=10                             NA     -2.18 %           NA      NA       NA

Maybe too good? I'm worried I'm missing something...
Edit: They're all mostly throughput tests, so it makes sense.

@Trott
Copy link
Member

Trott commented May 25, 2019

I suppose this is blocked until the bug in http2 is fixed?

@Trott
Copy link
Member

Trott commented May 25, 2019

Benchmark results: 😮

@mildsunrise
Copy link
Member Author

I suppose this is blocked until the bug in http2 is fixed?

Hmm, we can mark the test as flaky (which seems to be the case, i.e. it depends on a race condition) until the bug is fixed in http2. Or we can wait until it's fixed, and then rebase this PR to make CI green.

@Trott
Copy link
Member

Trott commented May 26, 2019

/ping @nodejs/crypto

Copy link
Member

@indutny indutny left a comment

Choose a reason for hiding this comment

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

LGTM, if CI is green. Great PR!

@mildsunrise
Copy link
Member Author

I've done some changes (basically, add a debug message if there's no data).
Once #27891 has landed, I'll rebase and this should be ready.

Copy link
Contributor

@ryzokuken ryzokuken left a comment

Choose a reason for hiding this comment

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

Thanks

Copy link
Member

@Trott Trott left a comment

Choose a reason for hiding this comment

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

(My main hesitation has been that the benchmark results seem too good to be true!)

@nodejs-github-bot
Copy link
Collaborator

@Trott
Copy link
Member

Trott commented May 28, 2019

parallel/test-http2-large-write-destroy seems to be timing out a lot now? Optimistically, perhaps improved performance here is exposing a race condition in the test? Pessimistically, perhaps this means there's a bug in this change?

@mildsunrise
Copy link
Member Author

mildsunrise commented May 28, 2019

It's a race in the test, see the discussion at #27863.

The test (at the server) writes data and then destroys the stream, and (at the client) it just waits for close without starting data flow.

before -> stream is destroyed before written data can be sent, so the client emits close and we're done.

after -> write is done synchronously, then the stream is destroyed. client stream can't be destroyed if there's pending data to be read, so it times out

@nodejs-github-bot
Copy link
Collaborator

TLSWrap::DoWrite() now concatenates data chunks and makes a single
call to SSL_write(). Grouping data into a single segment:

- reduces network overhead: by factors of even 2 or 3 in usages
  like `http2` or `form-data`

- improves security: segment lengths can reveal lots of info, i.e.
  with `form-data`, how many fields are sent and the approximate length
  of every individual field and its headers

- reduces encryption overhead: a quick benchmark showed a ~30% CPU time
  decrease for an extreme case, see
  nodejs#27573 (comment)

Fixes: nodejs#27573
@mildsunrise mildsunrise force-pushed the feature/tls-writev branch from 2b2bcba to 4ef156b Compare May 28, 2019 18:21
@mildsunrise
Copy link
Member Author

I've just rebased, this should now pass CI ^^

@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

CI: https://ci.nodejs.org/job/node-test-pull-request/23445/

@ryzokuken
Copy link
Contributor

Landed in 28ec14f 🎉

@ryzokuken ryzokuken closed this May 29, 2019
ryzokuken pushed a commit that referenced this pull request May 29, 2019
TLSWrap::DoWrite() now concatenates data chunks and makes a single
call to SSL_write(). Grouping data into a single segment:

- reduces network overhead: by factors of even 2 or 3 in usages
  like `http2` or `form-data`

- improves security: segment lengths can reveal lots of info, i.e.
  with `form-data`, how many fields are sent and the approximate length
  of every individual field and its headers

- reduces encryption overhead: a quick benchmark showed a ~30% CPU time
  decrease for an extreme case, see
  #27573 (comment)

Fixes: #27573

PR-URL: #27861
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
@addaleax addaleax added lts-watch-v10.x performance Issues and PRs related to the performance of Node.js. labels May 29, 2019
@addaleax
Copy link
Member

I think this has also addressed #20263 – i.e., we may need/want to backport this to v10.x, so I’m adding the label.

@jmendeth Awesome work!

@mildsunrise mildsunrise deleted the feature/tls-writev branch May 29, 2019 19:42
@mildsunrise
Copy link
Member Author

@addaleax I wasn't aware of #20263, I'll look into it. I noticed yesterday that Node 8 does some kind of buffering for TLSSocket, but not for HTTP/2 for example. Backporting to v10 would be great

@addaleax
Copy link
Member

@jmendeth Yeah, I only made the connection after this landed – #20263 isn’t about TLSSocket, but about us switching to implementing SecurePair on top of TLSSocket (because they ultimately provide the same functionality).

And regarding the backport, there seems to be quite a large number of conflicts when cherry-picking the commit here directly, so it’s probably going to require a manual one. (There is a guide for how to do that here, if you would like to do so yourself – the tl;dr is, cherry-picking the commit, resolving conflicts and opening a PR against v10.x-staging.) We do have a general rule of a commit being released in the Current release line for 2 weeks before it gets released in LTS, so there’s no rush and it might be a good idea to wait and see if we can backport other TLS-related changes first (mostly @sam-github’s #25713 and my #26843).

@mildsunrise
Copy link
Member Author

Oh, nice! I volunteer to open a backport PR of these three PRs if it's useful?

@addaleax
Copy link
Member

@jmendeth I’ve opened #27967, but it would be awesome if you could do a backport for this one after that PR lands? :)

@mildsunrise
Copy link
Member Author

Okay!

targos pushed a commit that referenced this pull request May 31, 2019
TLSWrap::DoWrite() now concatenates data chunks and makes a single
call to SSL_write(). Grouping data into a single segment:

- reduces network overhead: by factors of even 2 or 3 in usages
  like `http2` or `form-data`

- improves security: segment lengths can reveal lots of info, i.e.
  with `form-data`, how many fields are sent and the approximate length
  of every individual field and its headers

- reduces encryption overhead: a quick benchmark showed a ~30% CPU time
  decrease for an extreme case, see
  #27573 (comment)

Fixes: #27573

PR-URL: #27861
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
@targos targos mentioned this pull request Jun 3, 2019
mildsunrise added a commit to mildsunrise/node that referenced this pull request Jul 30, 2019
TLSWrap::DoWrite() now concatenates data chunks and makes a single
call to SSL_write(). Grouping data into a single segment:

- reduces network overhead: by factors of even 2 or 3 in usages
  like `http2` or `form-data`

- improves security: segment lengths can reveal lots of info, i.e.
  with `form-data`, how many fields are sent and the approximate length
  of every individual field and its headers

- reduces encryption overhead: a quick benchmark showed a ~30% CPU time
  decrease for an extreme case, see
  nodejs#27573 (comment)

Fixes: nodejs#27573

PR-URL: nodejs#27861
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
BethGriggs pushed a commit that referenced this pull request Oct 7, 2019
TLSWrap::DoWrite() now concatenates data chunks and makes a single
call to SSL_write(). Grouping data into a single segment:

- reduces network overhead: by factors of even 2 or 3 in usages
  like `http2` or `form-data`

- improves security: segment lengths can reveal lots of info, i.e.
  with `form-data`, how many fields are sent and the approximate length
  of every individual field and its headers

- reduces encryption overhead: a quick benchmark showed a ~30% CPU time
  decrease for an extreme case, see
  #27573 (comment)

Fixes: #27573

Backport-PR-URL: #28904
PR-URL: #27861
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
@BethGriggs BethGriggs mentioned this pull request Oct 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. performance Issues and PRs related to the performance of Node.js. tls Issues and PRs related to the tls subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

request body sent in separate SSL segment (bug at 9.6.0)
7 participants