Skip to content

Commit 163f47c

Browse files
teresalvespetebacondarwin
authored andcommitted
KV: get bulk - add content too long error
1 parent 09464a6 commit 163f47c

File tree

3 files changed

+37
-8
lines changed

3 files changed

+37
-8
lines changed

packages/miniflare/src/workers/kv/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const KVLimits = {
77
MAX_VALUE_SIZE: 25 * 1024 * 1024 /* 25MiB */,
88
MAX_VALUE_SIZE_TEST: 1024 /* 1KiB */,
99
MAX_METADATA_SIZE: 1024 /* 1KiB */,
10+
MAX_BULK_SIZE: 25 * 1024 * 1024 /* 25MiB */,
1011
} as const;
1112

1213
export const KVParams = {

packages/miniflare/src/workers/kv/namespace.worker.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ async function processKeyValue(
9090
}
9191

9292
let val = null;
93+
const size = decodedValue.length;
9394
try {
9495
val = !obj?.value
9596
? null
@@ -99,16 +100,16 @@ async function processKeyValue(
99100
} catch (err: any) {
100101
throw new HttpError(
101102
400,
102-
"At least one of the requested keys corresponds to a non-JSON value"
103+
`At least one of the requested keys corresponds to a non-${type} value`
103104
);
104105
}
105106
if (val && withMetadata) {
106-
return {
107+
return [{
107108
value: val,
108109
metadata: obj?.metadata ?? null,
109-
};
110+
}, size];
110111
}
111-
return val;
112+
return [val, size];
112113
}
113114

114115
export class KVNamespaceObject extends MiniflareDurableObject {
@@ -132,23 +133,35 @@ export class KVNamespaceObject extends MiniflareDurableObject {
132133
const keys: string[] = parsedBody.keys;
133134
const type = parsedBody?.type;
134135
if (type && type !== "text" && type !== "json") {
135-
return new Response(`Type ${type} is invalid`, { status: 400 });
136+
return new Response("Bad Request", { status: 400 });
136137
}
137138
const obj: { [key: string]: any } = {};
138139
if (keys.length > MAX_BULK_GET_KEYS) {
139-
return new Response(`Accepting a max of 100 keys, got ${keys.length}`, {
140+
return new Response("Bad Request", {
140141
status: 400,
141142
});
142143
}
144+
let totalBytes = 0;
143145
for (const key of keys) {
144146
validateGetOptions(key, { cacheTtl: parsedBody?.cacheTtl });
145147
const entry = await this.storage.get(key);
146-
const value = await processKeyValue(
148+
const [value, size] = await processKeyValue(
147149
entry,
148150
parsedBody?.type,
149151
parsedBody?.withMetadata
150152
);
153+
totalBytes += size;
151154
obj[key] = value;
155+
156+
}
157+
const maxValueSize = this.beingTested
158+
? KVLimits.MAX_VALUE_SIZE_TEST
159+
: KVLimits.MAX_BULK_SIZE;
160+
if (totalBytes > maxValueSize) {
161+
throw new HttpError(
162+
413,
163+
`Total size of request exceeds the limit of ${maxValueSize / 1024 / 1024}MB`
164+
);
152165
}
153166

154167
return new Response(JSON.stringify(obj));

packages/miniflare/test/plugins/kv/index.spec.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ test("bulk get: request json type", async (t) => {
168168
} catch (error: any) {
169169
t.is(
170170
error.message,
171-
"KV GET_BULK failed: 400 At least one of the requested keys corresponds to a non-JSON value"
171+
"KV GET_BULK failed: 400 At least one of the requested keys corresponds to a non-json value"
172172
);
173173
}
174174
});
@@ -224,6 +224,21 @@ test("bulk get: get with metadata for 404", async (t) => {
224224
t.deepEqual(result, expectedResult);
225225
});
226226

227+
test("bulk get: get over size limit", async (t) => {
228+
const { kv } = t.context;
229+
const bigValue = new Array(1024).fill("x").join("");
230+
await kv.put("key1", bigValue);
231+
await kv.put("key2", bigValue);
232+
try {
233+
await kv.getWithMetadata(["key1", "key2"]);
234+
} catch (error: any) {
235+
t.deepEqual(
236+
error.message,
237+
"KV GET_BULK failed: 413 Total size of request exceeds the limit of 0.0009765625MB" // 1024 Bytes for testing
238+
);
239+
}
240+
});
241+
227242
test("get: returns null for non-existent keys", async (t) => {
228243
const { kv } = t.context;
229244
t.is(await kv.get("key"), null);

0 commit comments

Comments
 (0)