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

refactor: move message handling internals to use async/await #1656

Open
wants to merge 22 commits into
base: master
Choose a base branch
from

Conversation

arthurschreiber
Copy link
Collaborator

@arthurschreiber arthurschreiber commented Aug 27, 2024

This introduces some initial refactoring around MessageIO/IncomingMessageStream/OutgoingMessageStream.

Namely, the goal is to completely remove IncomingMessageStream/OutgoingMessageStream/Message and replace them with two straightforward functions instead:

  • MessageIO.readMessage to read the contents of a message as a stream using an AsyncIterable<Buffer> from a Readable stream.
  • MessageIO.writeMessage to write a stream of buffers (generated synchronously via a Iterable<Buffer> or asynchronously via a AsyncIterable<Buffer>) to a Writable stream.

Both these new methods are much simpler compared to the previous IncomingMessageStream/OutgoingMessageStream implementation, both from a logical complexity as well as an implementation complexity point.

They're also both significantly faster than the current implementations. I added benchmarks that try to compare either implementation (benchmarks run using Node.js v18.20.4):

@arthurschreiber ➜ /workspace/benchmarks (arthur/async-await) $ node message-io/incoming-message-stream.js 
message-io/incoming-message-stream.js n=100: 2,913.7299061537133 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
message-io/incoming-message-stream.js n=1000: 11,430.989311430583 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
message-io/incoming-message-stream.js n=10000: 32,195.67874171715 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
message-io/incoming-message-stream.js n=100000: 101,359.99006469377 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
@arthurschreiber ➜ /workspace/benchmarks (arthur/async-await) $ node message-io/read-message.js 
message-io/read-message.js n=100: 15,410.443287565346 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
message-io/read-message.js n=1000: 22,116.38940123348 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
message-io/read-message.js n=10000: 70,465.68105003222 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
message-io/read-message.js n=100000: 206,620.15053389582 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
@arthurschreiber ➜ /workspace/benchmarks (arthur/async-await) $ node message-io/outgoing-message-stream.js 
message-io/outgoing-message-stream.js n=100: 5,938.379808083441 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
message-io/outgoing-message-stream.js n=1000: 9,312.915449158187 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
message-io/outgoing-message-stream.js n=10000: 37,337.954398051086 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
message-io/outgoing-message-stream.js n=100000: 80,630.96081259915 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
@arthurschreiber ➜ /workspace/benchmarks (arthur/async-await) $ node message-io/write-message.js 
message-io/write-message.js n=100: 18,303.00403544633 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
message-io/write-message.js n=1000: 15,285.594130026144 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
message-io/write-message.js n=10000: 67,938.25567509481 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)
message-io/write-message.js n=100000: 127,125.10735604081 (minor: 0 - 0ms, major: 0 - 0ms, incremental: 0 - 0ms)

The current implementation spends a lot of time setting up new stream objects for each incoming and outgoing message (via the Message class that inherits from PassThrough transform stream), and that causes quite a dent in performance, especially when v8 optimizations haven't kicked in yet.

Copy link

codecov bot commented Aug 27, 2024

Codecov Report

Attention: Patch coverage is 91.20370% with 19 lines in your changes missing coverage. Please review.

Project coverage is 79.53%. Comparing base (fc2aa28) to head (39cbe28).

Files with missing lines Patch % Lines
src/message-io.ts 90.83% 3 Missing and 9 partials ⚠️
src/connection.ts 90.27% 4 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1656      +/-   ##
==========================================
+ Coverage   78.97%   79.53%   +0.56%     
==========================================
  Files          90       90              
  Lines        4855     5009     +154     
  Branches      929      947      +18     
==========================================
+ Hits         3834     3984     +150     
+ Misses        718      712       -6     
- Partials      303      313      +10     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@arthurschreiber
Copy link
Collaborator Author

@MichaelSun90 @mShan0 Mind taking a look? I only moved the PRELOGIN payload and parsing to use the new methods so far. I think this is probably large enough to warrant merging this before moving more things over - I don't think this will be reviewable if we move more things over inside of this PR.

@@ -48,6 +48,10 @@
});

afterEach(function(done) {
if (this.timedout) {
console.log({ ...connection, config: undefined });

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High test

This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
@@ -38,6 +38,10 @@
});

afterEach(function(done) {
if (this.timedout) {
console.log({ ...connection, config: undefined });

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High test

This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
@@ -49,6 +50,10 @@
});

afterEach(function(done) {
if (this.timedout) {
console.log({ ...connection, config: undefined });

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High test

This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
This logs sensitive data returned by
an access to password
as clear text.
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.

1 participant