-
-
Notifications
You must be signed in to change notification settings - Fork 956
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
Use events to wire upload tracking instead of racing with setImmediate #525
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -91,13 +91,11 @@ module.exports = (options = {}) => { | |
return; | ||
} | ||
|
||
setImmediate(() => { | ||
try { | ||
getResponse(response, options, emitter, redirects); | ||
} catch (error) { | ||
emitter.emit('error', error); | ||
} | ||
}); | ||
try { | ||
getResponse(response, options, emitter, redirects); | ||
} catch (error) { | ||
emitter.emit('error', error); | ||
} | ||
}); | ||
|
||
cacheReq.on('error', error => { | ||
|
@@ -110,7 +108,7 @@ module.exports = (options = {}) => { | |
|
||
cacheReq.once('request', req => { | ||
let aborted = false; | ||
req.once('abort', _ => { | ||
req.once('abort', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refactoring should be done in a separate PR. Just Kidding :P 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I considered it, but when I looked at the diff, I didn't think the one line change raised the cognitive burden enough to warrant a separate commit. 😅 |
||
aborted = true; | ||
}); | ||
|
||
|
@@ -133,9 +131,7 @@ module.exports = (options = {}) => { | |
timedOut(req, options.gotTimeout); | ||
} | ||
|
||
setImmediate(() => { | ||
emitter.emit('request', req); | ||
}); | ||
emitter.emit('request', req); | ||
}); | ||
}; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect this was added to control the call stack, ensuring that the method ran to completion before any errors were emitted. This changes that behavior. There doesn't appear to be any tests failing from the change, but I would guess that this introduces behavioral changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Originally added in #117.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@brandon93s Thanks for the reference to #117.
It looks like the intent was to ensure the response data was handled after the event listeners that clear timeouts were run.
If a callback is provided for
request
, it will be the first handler to run.I can make another pass at this, to evaluate when the timeout clearing handlers are being run.
Does my analysis sound right? See any other issues?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You both are right. The main intent was to emit response ASAP, so @floatdrop used
setImmediate
as it executes immediately after I/O operations. I'd leavesetImmediate
unchanged here.I think @jstewmon is right here. We don't need
setImmediate
in this case (line 198).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not how I read it. There are 3 emitters involved, so I'm not sure which one you mean. The
emitter
response event is emitted fromgetResponse
, sosetImmediate
here delays that. Thereq
emitter's event isn't affected by thissetImmediate
.This is the background information I found:
The version of
timed-out
being used at the time of #117 listened for theresponse
event of the request to clear the timeout:https://github.com/floatdrop/timed-out/blob/v2.0.0/index.js#L31
The
request
object was passed to timed-out, which would setup that listener.The statement that was deferred with
setImmediate
calledunzipResponse
from thehttp.request
callback, which would have run before timed-out's listener.So my assessment was that the intent was to allow timed-out's listener to run before the response stream was connected to unzipResponse.
Now that
timeout.request
is waiting forresponse.end
, I think deferring the call togetResponse
actually increases the possibility that the timeout will breach.Since it is quite common to use a request callback, I think a better solution to checking a timeout against the
response
event would be to use setImmediate in the timer callback to defer the error emission to thecheck
phase of the event loop in case the response had been received but was still queued due to load. At a minimum, the listener that clears the timeout could be subscribed withprependOnceListener
to ensure it runs earlier.I thought there might be some practical purpose other than the ordering of the response handlers (like allowing subscriptions to some event emitter), but I don't see any such purpose, and I think we can clearly see that the
response
event listener order is no longer applicable.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the connection was established, because there's a response.
You were trying to illustrate the first sentence using the
connect
timeout, though it had been connected already. Awesome.Is it possible to receive
connect
event andresponse
(with some queued data) in the same loop?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I have read it several times, not only today.
Then the connection is not established. From the docs:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a tricky topic because of the changes in behavior over time.
When I've referred to the
response
event, I've been talking about the behavior and intent at the time (circa 2015) that the setImmediate deferral was added in #117. Theresponse
event is not currently used to clear any timeouts, so it is not relevant to this change.No, the statement you quoted was in my original analysis. I was suggesting that there was a more precise way of mitigating #117 than deferring the response piping with setImmediate.
I didn't want to use the response event to illustrate how that suggestion would be implemented because none of the current timeouts depend on the timing of that event. I used the
connect
event as an example in the current code because it is used to clear the connect timeout.I don't see any reason why that would not be possible, but their listeners would be notified in FIFO order (connect before response). Does it matter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The underlying system operation completes before we're notified. That's the purpose of the poll phase - to notify us that the IO operations we listened for are complete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not true. Before I've copied
timed-out
into this repo there waspTimeout
handlingtimeout.request
, so it started counting immediately after the promise was called (no connection has been made yet). That's just only a note.I still disagree here. It has its own time to establish the connection. But this change is not significant I think. Let's skip this subject.
So if these are two different things, I've got only one question: what is the better way to avoid bottleneck with emitting the response and handling it? (Line 107) What else if not setImmediate? You haven't implemented it yet. It just delays establishing another connections for now (if there's big amount of requests).