From 79f420eb8e424f8a7eaa493f0b89dabc340551c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 6 Mar 2024 19:12:47 +0000 Subject: [PATCH] Add timeout to cache get (#221) * Add timeout to cache get * Reduce it to 200ms --- src/lib/async.ts | 29 +++++++++++++++++++++++++---- src/lib/cache/cache.ts | 14 ++++++++++---- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/lib/async.ts b/src/lib/async.ts index d4a9cb994..6d19fb5a4 100644 --- a/src/lib/async.ts +++ b/src/lib/async.ts @@ -8,7 +8,12 @@ import { waitUntil, getGlobalContext } from './waitUntil'; export async function race( inputs: I[], execute: (input: I, options: { signal: AbortSignal }) => Promise, + options: { + timeout?: number; + } = {}, ): Promise { + const { timeout } = options; + const abort = new AbortController(); const pendingReads: Array> = []; @@ -16,16 +21,26 @@ export async function race( const result = await new Promise((resolve, reject) => { let resolved = false; let pending = inputs.length; + let timeoutId: NodeJS.Timeout | null = null; + + const respondWith = (value: R | null) => { + if (!resolved) { + resolved = true; + if (timeoutId) { + clearTimeout(timeoutId!); + } + resolve(value); + abort.abort(); + } + }; inputs.forEach((input) => { pendingReads.push( execute(input, { signal: abort.signal }) .then( (inputResult) => { - if (!resolved && inputResult !== null) { - resolved = true; - resolve(inputResult); - abort.abort(); + if (inputResult !== null) { + respondWith(inputResult); } }, (error) => { @@ -40,6 +55,12 @@ export async function race( }), ); }); + + if (timeout) { + timeoutId = setTimeout(() => { + respondWith(null); + }, timeout); + } }); // Wait for all reads to finish after responding to the request diff --git a/src/lib/cache/cache.ts b/src/lib/cache/cache.ts index dda59c714..b03b74b77 100644 --- a/src/lib/cache/cache.ts +++ b/src/lib/cache/cache.ts @@ -251,10 +251,16 @@ async function getCacheEntry(key: string): Promise { - const result = await race(cacheBackends, async (backend, { signal }) => { - const entry = await backend.get(key, { signal }); - return entry ? ([entry, backend.name] as const) : null; - }); + const result = await race( + cacheBackends, + async (backend, { signal }) => { + const entry = await backend.get(key, { signal }); + return entry ? ([entry, backend.name] as const) : null; + }, + { + timeout: 200, + }, + ); trace?.setAttribute('cacheStatus', result ? 'hit' : 'miss');