Skip to content

Commit afd93b9

Browse files
KV: get bulk - add content too long error (#8808)
* KV: get bulk - add content too long error * update changeset * fix formatting --------- Co-authored-by: talves <[email protected]> Co-authored-by: Peter Bacon Darwin <[email protected]>
1 parent 62df08a commit afd93b9

File tree

4 files changed

+46
-10
lines changed

4 files changed

+46
-10
lines changed

.changeset/common-worms-worry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"miniflare": patch
3+
---
4+
5+
KV: improve error messages for bulk gets

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: 24 additions & 9 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,19 @@ 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-
value: val,
108-
metadata: obj?.metadata ?? null,
109-
};
107+
return [
108+
{
109+
value: val,
110+
metadata: obj?.metadata ?? null,
111+
},
112+
size,
113+
];
110114
}
111-
return val;
115+
return [val, size];
112116
}
113117

114118
export class KVNamespaceObject extends MiniflareDurableObject {
@@ -132,24 +136,35 @@ export class KVNamespaceObject extends MiniflareDurableObject {
132136
const keys: string[] = parsedBody.keys;
133137
const type = parsedBody?.type;
134138
if (type && type !== "text" && type !== "json") {
135-
return new Response(`Type ${type} is invalid`, { status: 400 });
139+
return new Response("Bad Request", { status: 400 });
136140
}
137141
const obj: { [key: string]: any } = {};
138142
if (keys.length > MAX_BULK_GET_KEYS) {
139-
return new Response(`Accepting a max of 100 keys, got ${keys.length}`, {
143+
return new Response("Bad Request", {
140144
status: 400,
141145
});
142146
}
147+
let totalBytes = 0;
143148
for (const key of keys) {
144149
validateGetOptions(key, { cacheTtl: parsedBody?.cacheTtl });
145150
const entry = await this.storage.get(key);
146-
const value = await processKeyValue(
151+
const [value, size] = await processKeyValue(
147152
entry,
148153
parsedBody?.type,
149154
parsedBody?.withMetadata
150155
);
156+
totalBytes += size;
151157
obj[key] = value;
152158
}
159+
const maxValueSize = this.beingTested
160+
? KVLimits.MAX_VALUE_SIZE_TEST
161+
: KVLimits.MAX_BULK_SIZE;
162+
if (totalBytes > maxValueSize) {
163+
throw new HttpError(
164+
413,
165+
`Total size of request exceeds the limit of ${maxValueSize / 1024 / 1024}MB`
166+
);
167+
}
153168

154169
return new Response(JSON.stringify(obj));
155170
}

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)