Skip to content

Commit 2244339

Browse files
author
Nathan Booker
committed
Remove redundant memory KV calls, allow disable memory KV for testing
1 parent b713a29 commit 2244339

3 files changed

Lines changed: 37 additions & 73 deletions

File tree

core/lib/kv/adapters/upstash.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,14 @@ import { Redis } from '@upstash/redis';
22

33
import { KvAdapter, SetCommandOptions } from '../types';
44

5-
import { MemoryKvAdapter } from './memory';
6-
7-
const memoryKv = new MemoryKvAdapter();
8-
95
export class UpstashKvAdapter implements KvAdapter {
106
private upstashKv = Redis.fromEnv();
11-
private memoryKv = memoryKv;
127

138
async mget<Data>(...keys: string[]) {
14-
const memoryValues = await this.memoryKv.mget<Data>(...keys);
15-
16-
return memoryValues.length ? memoryValues : this.upstashKv.mget<Data[]>(keys);
9+
return this.upstashKv.mget<Data[]>(keys);
1710
}
1811

1912
async set<Data>(key: string, value: Data, opts?: SetCommandOptions) {
20-
await this.memoryKv.set(key, value, opts);
21-
2213
const response = await this.upstashKv.set(key, value, opts);
2314

2415
if (response === 'OK') {

core/lib/kv/adapters/vercel-runtime-cache.ts

Lines changed: 14 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,60 @@
1-
import { KvAdapter, SetCommandOptions } from '../types';
2-
3-
import { MemoryKvAdapter } from './memory';
1+
import { getCache } from '@vercel/functions';
42

5-
const memoryKv = new MemoryKvAdapter();
3+
import { KvAdapter, SetCommandOptions } from '../types';
64

75
export class RuntimeCacheAdapter implements KvAdapter {
8-
private memoryKv = memoryKv;
6+
private cache = getCache();
97

108
async mget<Data>(...keys: string[]): Promise<Array<Data | null>> {
11-
// First check memory cache for immediate reads
12-
const memoryValues = await this.memoryKv.mget<Data>(...keys);
13-
14-
// Check if all values are found in memory cache
15-
const allFoundInMemory = memoryValues.every((value) => value !== null);
16-
17-
if (allFoundInMemory) {
18-
this.logger(
19-
`MGET - Keys: ${keys.toString()} - Source: MEMORY - Values found: ${memoryValues.length}`,
20-
);
21-
22-
return memoryValues;
23-
}
24-
25-
// Otherwise, fetch missing values from runtime cache
269
this.logger(
27-
`MGET - Keys: ${keys.toString()} - Source: RUNTIME_CACHE - Memory hits: ${memoryValues.filter((v) => v !== null).length}/${keys.length}`,
10+
`MGET - Keys: ${keys.toString()} - Source: RUNTIME_CACHE - Fetching ${keys.length} keys`,
2811
);
2912

3013
try {
31-
// Use dynamic import to load the getCache function only when needed
32-
const { getCache } = await import('@vercel/functions');
33-
const cache = getCache();
34-
3514
const values = await Promise.all(
36-
keys.map(async (key, index) => {
37-
// Use memory value if available, otherwise fetch from runtime cache
38-
if (memoryValues[index] !== null && memoryValues[index] !== undefined) {
39-
return memoryValues[index];
40-
}
41-
15+
keys.map(async (key) => {
4216
try {
4317
// Use the Runtime Cache API
44-
const cachedValue = await cache.get(key);
18+
const cachedValue = await this.cache.get(key);
4519

4620
if (cachedValue !== null) {
4721
this.logger(`RUNTIME_CACHE GET - Key: ${key} - Found: true`);
48-
4922
return cachedValue;
5023
}
5124

5225
this.logger(`RUNTIME_CACHE GET - Key: ${key} - Found: false`);
53-
5426
return null;
5527
} catch (error) {
5628
this.logger(
5729
`RUNTIME_CACHE GET ERROR - Key: ${key} - Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
5830
);
59-
6031
return null;
6132
}
6233
}),
6334
);
6435

65-
// Store the runtime cache values in memory cache for future reads
66-
await Promise.all(
67-
values.map(async (value, index) => {
68-
const key = keys[index];
69-
70-
if (!key || value === null || memoryValues[index] !== null) {
71-
return;
72-
}
73-
74-
await this.memoryKv.set(key, value);
75-
this.logger(`MEMORY SET - Key: ${key} - Cached from runtime`);
76-
}),
77-
);
78-
7936
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
8037
return values as Array<Data | null>;
8138
} catch (error) {
8239
this.logger(
83-
`RUNTIME_CACHE ACCESS ERROR - Falling back to memory only: ${error instanceof Error ? error.message : 'Unknown error'}`,
40+
`RUNTIME_CACHE ACCESS ERROR - Returning null values: ${error instanceof Error ? error.message : 'Unknown error'}`,
8441
);
8542

86-
return memoryValues;
43+
// Return null for all keys if Runtime Cache is unavailable
44+
return keys.map(() => null);
8745
}
8846
}
8947

90-
async set<Data>(key: string, value: Data, opts?: SetCommandOptions): Promise<Data | null> {
91-
this.logger(`SET - Key: ${key} - Setting in both memory and runtime cache`);
92-
93-
// Set in memory cache immediately for fast subsequent reads
94-
await this.memoryKv.set(key, value, opts);
48+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
49+
async set<Data>(key: string, value: Data, _opts?: SetCommandOptions): Promise<Data | null> {
50+
this.logger(`SET - Key: ${key} - Setting in runtime cache`);
9551

96-
// Set in runtime cache for persistence across requests
9752
try {
98-
// Use dynamic import to load the getCache function only when needed
99-
const { getCache } = await import('@vercel/functions');
100-
const cache = getCache();
101-
102-
await cache.set(key, value);
53+
await this.cache.set(key, value);
10354
this.logger(`RUNTIME_CACHE SET - Key: ${key} - Success`);
10455
} catch (error) {
10556
this.logger(
106-
`RUNTIME_CACHE SET ERROR - Key: ${key} - Error: ${error instanceof Error ? error.message : 'Unknown error'} - Value still cached in memory`,
57+
`RUNTIME_CACHE SET ERROR - Key: ${key} - Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
10758
);
10859
}
10960

core/lib/kv/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { KvAdapter, SetCommandOptions } from './types';
33

44
interface Config {
55
logger?: boolean;
6+
disableMemory?: boolean;
67
}
78

89
const memoryKv = new MemoryKvAdapter();
@@ -25,6 +26,19 @@ class KV<Adapter extends KvAdapter> implements KvAdapter {
2526
async mget<Data>(...keys: string[]) {
2627
const kv = await this.getKv();
2728

29+
// Skip memory cache if disabled (useful for testing)
30+
if (this.config.disableMemory) {
31+
this.logger(`MGET - Keys: ${keys.toString()} - MEMORY DISABLED - Direct adapter access`);
32+
33+
const directValues = await kv.mget<Data>(...keys);
34+
35+
this.logger(
36+
`MGET - Keys: ${keys.toString()} - Value: ${JSON.stringify(directValues, null, 2)}`,
37+
);
38+
39+
return directValues;
40+
}
41+
2842
const memoryValues = (await this.memoryKv.mget<Data>(...keys)).filter(Boolean);
2943

3044
if (memoryValues.length === keys.length) {
@@ -60,6 +74,13 @@ class KV<Adapter extends KvAdapter> implements KvAdapter {
6074

6175
this.logger(`SET - Key: ${key} - Value: ${JSON.stringify(value, null, 2)}`);
6276

77+
// Skip memory cache if disabled (useful for testing)
78+
if (this.config.disableMemory) {
79+
this.logger(`SET - Key: ${key} - MEMORY DISABLED - Direct adapter only`);
80+
81+
return kv.set(key, value, opts);
82+
}
83+
6384
await Promise.all([this.memoryKv.set(key, value, opts), kv.set(key, value, opts)]);
6485

6586
return value;
@@ -102,6 +123,7 @@ const adapterInstance = new KV(createKVAdapter, {
102123
logger:
103124
(process.env.NODE_ENV !== 'production' && process.env.KV_LOGGER !== 'false') ||
104125
process.env.KV_LOGGER === 'true',
126+
disableMemory: process.env.KV_DISABLE_MEMORY === 'true',
105127
});
106128

107129
export { adapterInstance as kv };

0 commit comments

Comments
 (0)