@@ -2,6 +2,7 @@ import { DataSource, DataSourceConfig } from 'apollo-datasource'
22import { Pool } from 'undici'
33import { STATUS_CODES } from 'http'
44import QuickLRU from '@alloc/quick-lru'
5+ import pTimeout from 'p-timeout'
56import sjson from 'secure-json-parse'
67
78import { KeyValueCache } from 'apollo-server-caching'
@@ -15,9 +16,12 @@ type AbortSignal = unknown
1516
1617export 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