-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add docs for handling timeouts on the server (#156)
This adds docs to the Connect for Node.js section for handling timeouts on the server-side. In addition, it updates the Server Plugins page to include the up-to-date options allowed. --------- Signed-off-by: Steve Ayers <[email protected]> Signed-off-by: Timo Stamm <[email protected]> Signed-off-by: dependabot[bot] <[email protected]> Signed-off-by: Carol Gunby <[email protected]> Signed-off-by: hirasawayuki <[email protected]> Signed-off-by: Nick Snyder <[email protected]> Signed-off-by: Philip K. Warren <[email protected]> Signed-off-by: Dan Rice <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Timo Stamm <[email protected]> Co-authored-by: Stefan VanBuren <[email protected]> Co-authored-by: Derek Perez <[email protected]> Co-authored-by: Nick Snyder <[email protected]> Co-authored-by: Carol Gunby <[email protected]> Co-authored-by: Yuki Hirasawa <[email protected]> Co-authored-by: Philip K. Warren <[email protected]> Co-authored-by: Dan Rice <[email protected]> Co-authored-by: SimulShift <[email protected]> Signed-off-by: Steve Ayers <[email protected]>
- Loading branch information
1 parent
99be3e2
commit b5d887c
Showing
4 changed files
with
111 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
--- | ||
title: Timeouts | ||
sidebar_position: 8 | ||
--- | ||
|
||
Timeouts can be used to limit the time a server may take to process a response. | ||
In Connect-ES, timeout values are set by the client via the `timeoutMs` option | ||
when issuing a requests. If handling the response takes longer than the timeout, | ||
they will respond with the error code `deadline_exceeded`. In gRPC, the concept | ||
is also known as [deadlines](https://grpc.io/docs/guides/deadlines/). | ||
|
||
## Using `HandlerContext` | ||
|
||
Servers can interact with this timeout via the [handler context](https://connectrpc.com/docs/node/implementing-services#context). | ||
Depending on your needs, there are a few ways to approach it: | ||
|
||
The first way is via an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) | ||
on the context. Using this signal, route handlers can then tell if the timeout specified | ||
by the client was reached and abort their processes accordingly. The `AbortSignal` can be found | ||
via the property name `signal`. | ||
|
||
The signal can be passed to other functions or used to gracefully stop processes when the timeout is reached. | ||
Using `signal` works for any operation you might want to call as long as the API supports it. | ||
|
||
```ts | ||
import type { HandlerContext } from "@bufbuild/connect"; | ||
|
||
const say = async (req: SayRequest, ctx: HandlerContext) => { | ||
|
||
ctx.signal.aborted; // true if timed out | ||
ctx.signal.reason; // an error with code deadline_exceeded if timed out | ||
|
||
// raises an error with code deadline_exceeded if timed out | ||
ctx.signal.throwIfAborted(); | ||
|
||
// the AbortSignal can be passed to other functions | ||
await longRunning(ctx.signal); | ||
|
||
return new SayResponse({sentence: `You said: ${req.sentence}`}); | ||
}; | ||
``` | ||
|
||
A second way to interact with the timeout value is via the `timeoutMs()` function | ||
on the handler context. If the current request has a timeout, this function | ||
returns the remaining time. | ||
|
||
Using the `timeoutMs()` function is preferable when invoking upstream RPC calls | ||
because it is more efficient and robust - you have a guarantee that the peer is | ||
aware of the deadline, regardless of network issues. In gRPC, the concept is also | ||
known as [deadline propagation](https://grpc.io/docs/guides/deadlines/#deadline-propagation). | ||
|
||
```ts | ||
import type { HandlerContext } from "@bufbuild/connect"; | ||
|
||
const say = async (req: SayRequest, ctx: HandlerContext) => { | ||
|
||
// If a timeout was set on the call to this service, the timeoutMs() method | ||
// returns the remaining time in milliseconds. | ||
|
||
// Passing the value to an upstream client call propagates the timeout. | ||
await upstreamClient.someCall({}, { timeoutMs: ctx.timeoutMs() }); | ||
|
||
return new SayResponse({sentence: `You said: ${req.sentence}`}); | ||
}; | ||
``` | ||
|
||
In addition, to server-side support for timeouts, there is also a related option on `ConnectRouter` | ||
that helps constraining timeout values: `maxTimeoutMs`. For an explanation of this option, | ||
see the docs on [Server Plugins](server-plugins#common-options) | ||
|
||
Also note that while this page discusses timeouts in the context of a server, Connect-ES clients | ||
honor timeout values and will raise a `ConnectError` with code `DeadlineExceeded`. Even if a connection | ||
becomes unresponsive, the client call will still abort at the configured timeout. | ||
|
||
```ts | ||
try { | ||
// If this call takes more than 200 milliseconds, it is canceled | ||
await client.say({sentence: "Hello"}, { timeoutMs: 200 }); | ||
} catch (err) { | ||
if (err instanceof ConnectError && err.code === Code.DeadlineExceeded) { | ||
// handle the timeout error | ||
} | ||
} | ||
``` |