mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-21 01:53:26 +00:00
Stop using shared promises (#114)
* Try cf.cacheTags * Remove shared promises * Use a pendings set per context
This commit is contained in:
Vendored
+11
-2
@@ -2,7 +2,7 @@ import hash from 'object-hash';
|
||||
|
||||
import { cacheBackends } from './backends';
|
||||
import { CacheEntry } from './types';
|
||||
import { waitUntil } from './waitUntil';
|
||||
import { getGlobalContext, waitUntil } from './waitUntil';
|
||||
|
||||
export type CacheFunction<Args extends any[], Result> = ((...args: Args) => Promise<Result>) & {
|
||||
/**
|
||||
@@ -106,12 +106,21 @@ export function cache<Args extends any[], Result>(
|
||||
// During development, for now it fetches data twice between the middleware and the handler.
|
||||
// TODO: find a way to share the cache between the two.
|
||||
|
||||
const pendings = new Map<string, Promise<any>>();
|
||||
// On Cloudflare Workers, we can't share promises between requests:
|
||||
// > Cannot perform I/O on behalf of a different request. I/O objects (such as streams, request/response bodies, and others)
|
||||
// > created in the context of one request handler cannot be accessed from a different request's handler.
|
||||
//
|
||||
// To avoid this limitation and still avoid concurrent requests, we use a WeakMap to store the pending requests.
|
||||
const contextPendings = new WeakMap<object, Map<string, Promise<any>>>();
|
||||
|
||||
const cacheFn = async (...args: Args) => {
|
||||
const cacheArgs = options.extractArgs ? options.extractArgs(args) : args;
|
||||
const key = getCacheKey(cacheName, cacheArgs);
|
||||
|
||||
const context = getGlobalContext();
|
||||
const pendings = contextPendings.get(context) ?? new Map<string, Promise<any>>();
|
||||
contextPendings.set(context, pendings);
|
||||
|
||||
// If a pending request exists, wait for it
|
||||
if (pendings.has(key)) {
|
||||
return await pendings.get(key);
|
||||
|
||||
Vendored
+5
-1
@@ -79,17 +79,21 @@ async function serializeKey(key: string): Promise<string> {
|
||||
function serializeEntry(entry: CacheEntry): WorkerResponse {
|
||||
const headers = new Headers();
|
||||
headers.set('Content-Type', 'application/json');
|
||||
const cacheTags = ['gitbook-open', ...entry.meta.tags];
|
||||
|
||||
// Limit the cloudflare cache to 5 minutes
|
||||
headers.set(
|
||||
'Cache-Control',
|
||||
`public, max-age=${Math.min((entry.meta.expiresAt - Date.now()) / 1000, 5 * 60)}`,
|
||||
);
|
||||
headers.set('Cache-Tag', ['gitbook-open', ...entry.meta.tags].join(','));
|
||||
headers.set('Cache-Tag', cacheTags.join(','));
|
||||
|
||||
// @ts-ignore
|
||||
return new Response(JSON.stringify(entry), {
|
||||
headers,
|
||||
cf: {
|
||||
cacheTags,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Vendored
+8
@@ -1,5 +1,13 @@
|
||||
import { getRequestCloudflareContext } from '@cloudflare/next-on-pages/helpers';
|
||||
|
||||
/**
|
||||
* Get the global context object for the current request.
|
||||
*/
|
||||
export function getGlobalContext(): object {
|
||||
const cloudflare = getRequestCloudflareContext();
|
||||
return cloudflare ?? globalThis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the lifetime of the event handler until the promise is resolved.
|
||||
* https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/#contextwaituntil
|
||||
|
||||
Reference in New Issue
Block a user