Skip to content

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.


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.

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.

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