Skip to content

Commit dd1a8ed

Browse files
committed
feat: add option to skip cache when it doesn't respond in appropriate time
1 parent 6ac892d commit dd1a8ed

File tree

4 files changed

+273
-17
lines changed

4 files changed

+273
-17
lines changed

package-lock.json

Lines changed: 14 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"apollo-server-errors": "^2.5.0",
5656
"apollo-server-types": "^0.9.0",
5757
"graphql": "^15.5.1",
58+
"p-timeout": "^4.1.0",
5859
"secure-json-parse": "^2.4.0",
5960
"undici": "^4.1.0"
6061
},

src/http-data-source.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { DataSource, DataSourceConfig } from 'apollo-datasource'
22
import { Pool } from 'undici'
33
import { STATUS_CODES } from 'http'
44
import QuickLRU from '@alloc/quick-lru'
5+
import pTimeout from 'p-timeout'
56
import sjson from 'secure-json-parse'
67

78
import { KeyValueCache } from 'apollo-server-caching'
@@ -15,9 +16,12 @@ type AbortSignal = unknown
1516

1617
export type CacheTTLOptions = {
1718
requestCache?: {
18-
// The maximum time an item is cached (seconds)
19+
// In case of the cache does not respond for any reason. This defines the max duration (ms) until the operation is aborted.
20+
maxCacheTimeout: number
21+
// The maximum time an item is cached in seconds.
1922
maxTtl: number
20-
// The maximum time an item fetched from the cache is case of an error (seconds). This value must be greater than `maxTtl`
23+
// The maximum time an item fetched from the cache is case of an error in seconds.
24+
// This value must be greater than `maxTtl`.
2125
maxTtlIfError: number
2226
}
2327
}
@@ -287,6 +291,8 @@ export abstract class HTTPDataSource<TContext = any> extends DataSource {
287291
if (request.requestCache && this.isResponseCacheable<TResult>(request, response)) {
288292
response.maxTtl = Math.max(request.requestCache.maxTtl, request.requestCache.maxTtlIfError)
289293
const cachedResponse = JSON.stringify(response)
294+
295+
// respond with the result immedialty without waiting for the cache
290296
this.cache
291297
.set(cacheKey, cachedResponse, {
292298
ttl: request.requestCache?.maxTtl,
@@ -304,7 +310,12 @@ export abstract class HTTPDataSource<TContext = any> extends DataSource {
304310
this.onError?.(error, request)
305311

306312
if (request.requestCache) {
307-
const cacheItem = await this.cache.get(`staleIfError:${cacheKey}`)
313+
// short circuit in case of the cache does not fail fast enough for any reason
314+
const cacheItem = await pTimeout(
315+
this.cache.get(`staleIfError:${cacheKey}`),
316+
request.requestCache.maxCacheTimeout,
317+
)
318+
308319
if (cacheItem) {
309320
const response: Response<TResult> = sjson.parse(cacheItem)
310321
response.isFromCache = true
@@ -343,12 +354,23 @@ export abstract class HTTPDataSource<TContext = any> extends DataSource {
343354
if (options.method === 'GET') {
344355
// try to fetch from shared cache
345356
if (request.requestCache) {
346-
const cacheItem = await this.cache.get(cacheKey)
347-
if (cacheItem) {
348-
const cachedResponse: Response<TResult> = sjson.parse(cacheItem)
349-
cachedResponse.memoized = false
350-
cachedResponse.isFromCache = true
351-
return cachedResponse
357+
try {
358+
// short circuit in case of the cache does not fail fast enough for any reason
359+
const cacheItem = await pTimeout(
360+
this.cache.get(cacheKey),
361+
request.requestCache.maxCacheTimeout,
362+
)
363+
if (cacheItem) {
364+
const cachedResponse: Response<TResult> = sjson.parse(cacheItem)
365+
cachedResponse.memoized = false
366+
cachedResponse.isFromCache = true
367+
return cachedResponse
368+
}
369+
const response = this.performRequest<TResult>(options, cacheKey)
370+
this.memoizedResults.set(cacheKey, response)
371+
return response
372+
} catch (error) {
373+
this.logger?.error(`Cache item '${cacheKey}' could be loaded: ${error.message}`)
352374
}
353375
}
354376

0 commit comments

Comments
 (0)