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

readable event not emitted after net.Socket reconnects #25969

Open
morkai opened this issue Feb 6, 2019 · 19 comments
Open

readable event not emitted after net.Socket reconnects #25969

morkai opened this issue Feb 6, 2019 · 19 comments
Labels
confirmed-bug Issues with confirmed bugs. net Issues and PRs related to the net subsystem. stream Issues and PRs related to the stream subsystem.

Comments

@morkai
Copy link
Contributor

morkai commented Feb 6, 2019

  • Version: v10.15.1
  • Platform: Windows 10 Pro 64-bit
  • Subsystem: net

If net.Socket loses connection (close is emitted) and the same socket instance is used to reconnect to the same server, no more readable events are emitted (data events are still emitted).

Doesn't work in v10.14/v10.15.1. Works in v8.15.0.

Repro: https://gist.github.com/morkai/fa175bd0104443e6142f3d0e22805653

  1. Run server.js
  2. Run client.js
  3. Client prints client#readable
  4. Kill the server
  5. Run the server again
  6. Client reconnects
  7. Client doesn't print any client#readable lines
  8. Kill the client
  9. Uncomment the data event handler and comment the readable handler in client.js
  10. Run the client
  11. Client prints client#data
  12. Kill the server
  13. Run the server again
  14. Client reconnects
  15. Client resumes printing client#data lines
@morkai
Copy link
Contributor Author

morkai commented Feb 6, 2019

Stopped working in v10.10.0. Calling socket.read(0) in the connect event handler fixes the issue.

@lpinca lpinca added net Issues and PRs related to the net subsystem. stream Issues and PRs related to the stream subsystem. labels Feb 7, 2019
@lpinca
Copy link
Member

lpinca commented Feb 7, 2019

Reverting 53fb7af1b2 makes 'readable' events to be emitted again upon reconnection.

cc: @mcollina

@lpinca
Copy link
Member

lpinca commented Feb 7, 2019

@morkai btw, read() should be called until it returns null.

@morkai
Copy link
Contributor Author

morkai commented Feb 7, 2019

@lpinca even if called without any arguments?

https://nodejs.org/dist/latest-v10.x/docs/api/stream.html#stream_readable_read_size
If the size argument is not specified, all of the data contained in the internal buffer will be returned.

@lpinca
Copy link
Member

lpinca commented Feb 7, 2019

Looking at the code it seems that that sentence is correct. However a new sentence has been added

https://nodejs.org/dist/latest-v11.x/docs/api/stream.html#stream_readable_read_size
Note that the while loop is necessary when processing data with readable.read(). Only after readable.read() returns null, 'readable' will be emitted.

See #25375 for context.

@mcollina
Copy link
Member

mcollina commented Feb 7, 2019

This is does not look like a bug in Readable.read. The following behaves correctly:

const { Readable } = require('stream')

const r = new Readable({
  read() {
  }
})

r.push(Buffer.from('hello'))
r.push(Buffer.from(' '))
r.push(Buffer.from('world'))
r.push(null)

r.on('readable', () => {
  console.log('readable')
  let chunk
  while ((chunk = r.read()) !== null) {
    console.log('chunk', chunk.toString())
  }
})

I think this is something specific to how socket.connect works

@addaleax
Copy link
Member

addaleax commented Feb 7, 2019

@mcollina My guess would be that Readable.prototype._undestroy doesn’t fully reset stream state?

@mcollina
Copy link
Member

mcollina commented Feb 7, 2019

@mcollina My guess would be that Readable.prototype._undestroy doesn’t fully reset stream state?

That might be the case.

@morkai
Copy link
Contributor Author

morkai commented Feb 8, 2019

Readable.isPaused() checks whether _readableState.flowing is strictly false.

Before v10.10, _readableState.flowing was always null if in paused mode, so isPaused() returns false (null !== false).

Since v10.10, _readableState.flowing is false if in paused mode, so isPaused() returns true (false === false).

This makes afterConnect() not call read(0): https://github.com/nodejs/node/blob/master/lib/net.js#L1049

See: https://gist.github.com/morkai/a6e8d696f11aa2fb903a369c775e8c76#file-log-txt

@lpinca
Copy link
Member

lpinca commented Feb 8, 2019

It seems there is also a behavior change between Node.js 8 and Node.js 10, probably wanted but I'm not sure. Consider the following example:

'use strict';

const { Readable } = require('stream');

const buf = Buffer.alloc(8192);

const readable = new Readable({
  read() {
    this.push(buf);
  }
});

readable.on('readable', function() {
  const data = readable.read();
  console.log(data.length);
});

On Node.js 8, it creates (as expected I would say) an endless loop. On Node.js 10 it exits after a few reads. Not sure what happens and if it is expected but at first glance it seems a race condition caused by 'readable' events being emitted on next tick.

@lpinca lpinca added the confirmed-bug Issues with confirmed bugs. label Feb 11, 2019
@lpinca
Copy link
Member

lpinca commented Feb 11, 2019

Added confirmed-bug label because I think state.flowing should not be set to false when using only the 'readable' events and readable.read(). As @morkai pointed out, 53fb7af1b2 changed readable.isPaused() behavior under these circumstances.

@ronag
Copy link
Member

ronag commented May 1, 2020

@lpinca Was this resolved through #26097. Can we close this issue?

@lpinca
Copy link
Member

lpinca commented May 1, 2020

If the original issue is fixed then yes.

@ronag
Copy link
Member

ronag commented May 1, 2020

@morkai: Is this still an issue?

@morkai
Copy link
Contributor Author

morkai commented May 1, 2020

Yes. The issue is still present in v12.13.1 and v14.1.0.

@ronag
Copy link
Member

ronag commented May 1, 2020

@morkai: Is it possible for you to instead of re-using the socket, simply create a new one?

@mcollina: I think we briefly discussed at some point that the _undestroy re-use flow is subtly broken. Is this something we should look into fixing? I would much rather deprecate this use-case and ask users to create a new socket instance.

@mcollina
Copy link
Member

mcollina commented May 1, 2020

Fixing would be good. Otherwise deprecating is also an option. It seems this issue is not getting enough attention - or folks willing to work on it.

@morkai
Copy link
Contributor Author

morkai commented May 2, 2020

@morkai: Is it possible for you to instead of re-using the socket, simply create a new one?

I fixed it in the library that was affected by this issue by calling the readable event handler after each connect event to force at least one call to read().

Instead of:

socket.on('connect', () => {
  console.log('connected!');
});

socket.on('readable', () => {
  while (true) {
    const data = socket.read();
    if (!data) break;
    console.log('data', data);
  }
});

this:

socket.on('connect', () => {
  console.log('connected!');
  onReadable();
});

socket.on('readable', onReadable);

function onReadable()
{
  while (true) {
    const data = socket.read();
    if (!data) break;
    console.log('data', data);
  }
}

@mcollina
Copy link
Member

mcollina commented May 2, 2020

I think we can add that to core.

ronag added a commit that referenced this issue May 8, 2020
PR-URL: #33204
Refs: #25969
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
codebytere pushed a commit that referenced this issue May 11, 2020
PR-URL: #33204
Refs: #25969
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
codebytere pushed a commit that referenced this issue Jun 7, 2020
PR-URL: #33204
Refs: #25969
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
CGQAQ added a commit to CGQAQ/node that referenced this issue Jul 21, 2023
fix readable event not emitted  after reconnect
CGQAQ added a commit to CGQAQ/node that referenced this issue Jul 21, 2023
fix readable event not emitted  after reconnect

Fixes: nodejs#25969
CGQAQ added a commit to CGQAQ/node that referenced this issue Jul 21, 2023
fix readable event not emitted  after reconnect

Fixes: nodejs#25969
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug Issues with confirmed bugs. net Issues and PRs related to the net subsystem. stream Issues and PRs related to the stream subsystem.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants