From 9c76bff16d3d633f27e8f04ae3424d49006eb536 Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Mon, 21 Oct 2024 17:09:01 +0200 Subject: [PATCH] Add options in ingress client to accept `timeout` or `signal` --- packages/restate-sdk-clients/src/api.ts | 18 +++++++++++++++++- packages/restate-sdk-clients/src/ingress.ts | 17 +++++++++++++++++ .../restate-sdk-examples/src/ingress_client.ts | 12 ++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/restate-sdk-clients/src/api.ts b/packages/restate-sdk-clients/src/api.ts index bd53eb93..3aace5d9 100644 --- a/packages/restate-sdk-clients/src/api.ts +++ b/packages/restate-sdk-clients/src/api.ts @@ -101,11 +101,27 @@ export interface IngresCallOptions { input?: Serde; output?: Serde; + + /** + * Timeout to be used when executing the request. In milliseconds. + * + * Same as {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal#aborting_a_fetch_with_timeout_or_explicit_abort | AbortSignal.timeout()}. + * + * This field is exclusive with `signal`, and using both of them will result in a runtime failure. + */ + timeout?: number; + + /** + * Signal to abort the underlying `fetch` operation. See {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal}. + * + * This field is exclusive with `timeout`, and using both of them will result in a runtime failure. + */ + signal?: AbortSignal; } export interface IngresSendOptions extends IngresCallOptions { /** - * If set, the invocation will be executed after the provided delay. In milliseconds. + * If set, the invocation will be enqueued now to be executed after the provided delay. In milliseconds. */ delay?: number; } diff --git a/packages/restate-sdk-clients/src/ingress.ts b/packages/restate-sdk-clients/src/ingress.ts index 53f681b1..48ca64f2 100644 --- a/packages/restate-sdk-clients/src/ingress.ts +++ b/packages/restate-sdk-clients/src/ingress.ts @@ -171,6 +171,22 @@ const doComponentInvocation = async ( headers[IDEMPOTENCY_KEY_HEADER] = idempotencyKey; attachable = true; } + + // Abort signal, if any + let signal: AbortSignal | undefined; + if ( + params.opts?.opts.signal !== undefined && + params.opts?.opts.timeout !== undefined + ) { + throw new Error( + "You can't specify both signal and timeout options at the same time" + ); + } else if (params.opts?.opts.signal !== undefined) { + signal = params.opts?.opts.signal; + } else if (params.opts?.opts.timeout !== undefined) { + signal = AbortSignal.timeout(params.opts?.opts.timeout); + } + // // make the call // @@ -179,6 +195,7 @@ const doComponentInvocation = async ( method: params.method ?? "POST", headers, body, + signal, }); if (!httpResponse.ok) { const body = await httpResponse.text(); diff --git a/packages/restate-sdk-examples/src/ingress_client.ts b/packages/restate-sdk-examples/src/ingress_client.ts index e3dd6632..70f007cb 100644 --- a/packages/restate-sdk-examples/src/ingress_client.ts +++ b/packages/restate-sdk-examples/src/ingress_client.ts @@ -51,6 +51,17 @@ const idempotentCall = async (name: string, idempotencyKey: string) => { console.log(greeting); }; +const callWithTimeout = async (name: string) => { + const greeter = ingress.serviceClient({ name: "greeter" }); + + const greeting = await greeter.greet( + name, + restate.rpc.opts({ timeout: 5 * 1000 }) + ); + + console.log(greeting); +}; + const customHeadersCall = async (name: string) => { const greeter = ingress.serviceClient(Greeter); @@ -178,6 +189,7 @@ Promise.resolve() .then(() => objectCall("bob")) .then(() => objectCall("mop")) .then(() => simpleCall("bob")) + .then(() => callWithTimeout("lateBob")) .then(() => sendAndCollectResultLater("boby")) .then(() => workflow("boby")) .then(() => idempotentCall("joe", "idemp-1"))