-
Notifications
You must be signed in to change notification settings - Fork 22
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
time out stalled streams, emit stream abort events #2074
Conversation
🦋 Changeset detectedLatest commit: bf58548 The changes in this PR will be included in the next version bump. This PR includes changesets to release 5 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
bc338d1
to
33cab5f
Compare
5dcd3e5
to
9d32ff2
Compare
33cab5f
to
568a57e
Compare
{ signal }, | ||
), | ||
), | ||
{ signal: AbortSignal.any([signal, chunkDeadline]) }, |
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 maintains the present behavior of signalling the response stream with the original request's abort controller, but also adds the chunk deadline.
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.
to clarify, the AbortSignal.any([signal, chunkDeadline])
will combine the main abort signal with the chunk-level abort signal, and if either is triggered, it cancels the stream and posts an abort message to the port?
packages/transport-dom/src/create.ts
Outdated
const chunkAc = new AbortController(); | ||
const chunkDeadline = chunkAc.signal; | ||
const chunkDeadlineExceeded = () => { | ||
console.debug('chunkDeadlineExceeded'); | ||
if (timeoutMs) { | ||
chunkAc.abort(ConnectError.from('Stream stalled', Code.DeadlineExceeded)); | ||
} | ||
}; |
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.
once passed to pipeThrough
an abort signal cannot be replaced. so i'm using a signal controlled by timeout instead of a bare signal from AbortSignal.timeout
, so the timer can be reset at each chunk arrival.
alternatively, an abort signal/controller could be avoided, and setTimeout
could be used to call the transformer's cont.error
directly.
@@ -149,7 +149,7 @@ export const createChannelTransport = ({ | |||
async unary<I extends Message<I> = AnyMessage, O extends Message<O> = AnyMessage>( | |||
service: ServiceType, | |||
method: MethodInfo<I, O>, | |||
signal: AbortSignal | undefined, | |||
signal: AbortSignal | undefined = new AbortController().signal, |
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 unary
and stream
methods are updated in the same way.
this just creates a signal that will never abort, if an abort signal is not provided by the caller.
signal.addEventListener('abort', () => | ||
port?.postMessage({ requestId, abort: true } satisfies TransportAbort), | ||
); |
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.
like unary requests, this will now also transmit an abort event for streams. this adds abort of pending streams, and could be used to externally abort an established stream.
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.
handling stalled streams is a good improvement 👍
{ signal }, | ||
), | ||
), | ||
{ signal: AbortSignal.any([signal, chunkDeadline]) }, |
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.
to clarify, the AbortSignal.any([signal, chunkDeadline])
will combine the main abort signal with the chunk-level abort signal, and if either is triggered, it cancels the stream and posts an abort message to the port?
@@ -160,10 +160,10 @@ export const createChannelTransport = ({ | |||
const requestId = crypto.randomUUID(); | |||
|
|||
const requestFailure = new AbortController(); | |||
const deadline = timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined; | |||
const requestDeadline = timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined; |
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.
timeoutMs
is the same as the default transport timeout in createChannelTransport
right?
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.
by default, yes, but it may be zero (indefinite) or a custom value for any individual request
@turbocrime general question, how do these stream timeouts impact the reliability of background syncing? |
1824abe
to
2c1fc91
Compare
155418d
to
fa86717
Compare
0d95457
to
c549a75
Compare
fa86717
to
d68e5fe
Compare
replied in DM, but repeating here for clarity of discussion: background syncing itself is not affected. the block processor has its own retry logic, and doesn't rely on this transport. the exposure of background syncing state in the frontend becomes more fragile, but also the displayed state is more accurate and truthful. the minifront PR enabling status stream retry is to address the minifront experience #2080 |
c549a75
to
3aa02f6
Compare
d68e5fe
to
98cdcea
Compare
3aa02f6
to
cc760d9
Compare
98cdcea
to
a024bb1
Compare
cc760d9
to
7cba666
Compare
a024bb1
to
4fd6ac3
Compare
7cba666
to
50e0c6b
Compare
4fd6ac3
to
de77c7c
Compare
38a38ff
to
ece8b54
Compare
de77c7c
to
1952085
Compare
if (transportFailure.signal.aborted) { | ||
throw transportFailure.signal.reason; | ||
} |
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.
retried streams on a dead transport would create an ever-expanding error message, due to throwIfAborted
creating a new error message for each failure. this change will allow the same error to be rethrown.
Description of Changes
Streaming responses now use the per-request or transport default timeout configuration to abort a stalled stream.
Related Issue
@penumbra-zone/transport-dom
never times out a stalled stream #2073Checklist Before Requesting Review