Skip to content

Commit 4a11d8d

Browse files
authored
Share memory cache between middleware and request handler (#2347)
1 parent 009ed8c commit 4a11d8d

File tree

4 files changed

+32
-14
lines changed

4 files changed

+32
-14
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"format": "prettier ./ --ignore-unknown --write",
1212
"format:check": "prettier ./ --ignore-unknown --list-different",
1313
"typecheck": "tsc --noEmit",
14-
"unit": "bun test {src,packages}/**/*.test.ts",
14+
"unit": "bun test {src,packages}",
1515
"e2e": "playwright test",
1616
"postinstall": "rm -rf ./public/~gitbook/static/[email protected] && mkdir -p ./public/~gitbook/static/ && cp -R node_modules/mathjax/es5 ./public/~gitbook/static/[email protected]"
1717
},

src/lib/cache/cache.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,24 @@ describe('cache', () => {
77

88
let fn: CacheFunction<[string], string>;
99
let testId = 0;
10+
let getTtl: () => number;
1011

1112
beforeEach(() => {
1213
impl.mockClear();
1314

1415
testId += 1;
16+
getTtl = () => 1000;
1517

1618
fn = cache(`cache-${testId}`, async (arg: string, options: CacheFunctionOptions) => {
1719
await new Promise((resolve) => setTimeout(resolve, 20));
1820
return {
1921
data: impl(arg),
22+
ttl: getTtl(),
2023
};
2124
});
2225
});
2326

24-
it('should only execute once for same argument', async () => {
27+
it('should only execute once for same argument if there is a ttl', async () => {
2528
const result = await Promise.all([fn('a'), fn('a')]);
2629

2730
expect(result).toEqual(['test-a', 'test-a']);
@@ -34,6 +37,21 @@ describe('cache', () => {
3437
expect(impl).toHaveBeenCalledTimes(1);
3538
});
3639

40+
it('should execute multiple times for same argument if ttl is 0 or undefined', async () => {
41+
getTtl = () => 0;
42+
43+
const result = await Promise.all([fn('a'), fn('a')]);
44+
45+
expect(result).toEqual(['test-a', 'test-a']);
46+
47+
expect(impl).toHaveBeenCalled();
48+
expect(impl).toHaveBeenCalledTimes(1);
49+
50+
expect(await fn('a')).toEqual('test-a');
51+
52+
expect(impl).toHaveBeenCalledTimes(2);
53+
});
54+
3755
it('should execute for different arguments', async () => {
3856
const result = await Promise.all([fn('a'), fn('b')]);
3957

src/lib/cache/memory.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { CacheBackend, CacheEntry } from './types';
22
import { NON_IMMUTABLE_LOCAL_CACHE_MAX_AGE_SECONDS, isCacheEntryImmutable } from './utils';
3+
import { getGlobalContext } from '../waitUntil';
34

45
export const memoryCache: CacheBackend = {
56
name: 'memory',
67
replication: 'local',
78
async get(key) {
8-
const memoryCache = getMemoryCache();
9+
const memoryCache = await getMemoryCache();
910
const memoryEntry = memoryCache.get(key);
1011

1112
if (!memoryEntry) {
@@ -21,7 +22,7 @@ export const memoryCache: CacheBackend = {
2122
return null;
2223
},
2324
async set(key, entry) {
24-
const memoryCache = getMemoryCache();
25+
const memoryCache = await getMemoryCache();
2526
// When the entry is immutable, we can cache it for the entire duration.
2627
// Else we cache it for a very short time.
2728
const expiresAt =
@@ -41,11 +42,11 @@ export const memoryCache: CacheBackend = {
4142
memoryCache.set(key, { ...entry, meta });
4243
},
4344
async del(keys) {
44-
const memoryCache = getMemoryCache();
45+
const memoryCache = await getMemoryCache();
4546
keys.forEach((key) => memoryCache.delete(key));
4647
},
4748
async revalidateTags(tags) {
48-
const memoryCache = getMemoryCache();
49+
const memoryCache = await getMemoryCache();
4950
const keys: string[] = [];
5051

5152
memoryCache.forEach((entry, key) => {
@@ -66,13 +67,12 @@ export const memoryCache: CacheBackend = {
6667
* With next-on-pages, the code seems to be isolated between the middleware and the handler.
6768
* To share the cache between the two, we use a global variable.
6869
*/
69-
function getMemoryCache(): Map<string, CacheEntry> {
70-
// @ts-ignore
71-
if (!globalThis.gitbookMemoryCache) {
72-
// @ts-ignore
73-
globalThis.gitbookMemoryCache = new Map();
70+
async function getMemoryCache(): Promise<Map<string, CacheEntry>> {
71+
let globalThisForMemoryCache: any = await getGlobalContext();
72+
73+
if (!globalThisForMemoryCache.gitbookMemoryCache) {
74+
globalThisForMemoryCache.gitbookMemoryCache = new Map();
7475
}
7576

76-
// @ts-ignore
77-
return globalThis.gitbookMemoryCache;
77+
return globalThisForMemoryCache.gitbookMemoryCache;
7878
}

src/lib/waitUntil.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export async function getGlobalContext(): Promise<object> {
1212

1313
// We lazy-load the next-on-pages package to avoid errors when running tests because of 'server-only'.
1414
const { getOptionalRequestContext } = await import('@cloudflare/next-on-pages');
15-
return getOptionalRequestContext()?.cf ?? globalThis;
15+
return getOptionalRequestContext()?.ctx ?? globalThis;
1616
}
1717

1818
/**

0 commit comments

Comments
 (0)