Skip to content

Commit a5a8582

Browse files
thomasgauvin: add limit writes per second (#18041)
* thomasgauvin: add limit writes per second * Update write-key-value-pairs.mdx * Update src/content/docs/kv/api/write-key-value-pairs.mdx * Update src/content/docs/kv/api/write-key-value-pairs.mdx * Update src/content/docs/kv/api/write-key-value-pairs.mdx Co-authored-by: Matt Silverlock <[email protected]> * Update src/content/docs/kv/api/write-key-value-pairs.mdx --------- Co-authored-by: Matt Silverlock <[email protected]>
1 parent d75c0fc commit a5a8582

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed

src/content/docs/kv/api/write-key-value-pairs.mdx

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,138 @@ await env.NAMESPACE.put(key, value, {
136136
});
137137
```
138138
139+
### Limits to KV writes to the same key
140+
141+
Workers KV has a maximum of 1 write to the same key per second. Writes made to the same key within 1 second will cause rate limiting (`429`) errors to be thrown.
142+
143+
You should not write more than once per second to the same key. Consider consolidating your writes to a key within a Worker invocation to a single write, or wait at least 1 second between writes.
144+
145+
The following example serves as a demonstration of how multiple writes to the same key may return errors by forcing concurrent writes within a single Worker invocation. This is not a pattern that should be used in production.
146+
147+
```typescript
148+
export default {
149+
async fetch(request, env, ctx): Promise<Response> {
150+
// Rest of code omitted
151+
const key = "common-key";
152+
const parallelWritesCount = 20;
153+
154+
// Helper function to attempt a write to KV and handle errors
155+
const attemptWrite = async (i: number) => {
156+
try {
157+
await env. YOUR_KV_NAMESPACE.put(key, `Write attempt #${i}`);
158+
return { attempt: i, success: true };
159+
} catch (error) {
160+
// An error may be thrown if a write to the same key is made within 1 second with a message. For example:
161+
// error: {
162+
// "message": "KV PUT failed: 429 Too Many Requests"
163+
// }
164+
165+
return {
166+
attempt: i,
167+
success: false,
168+
error: { message: (error as Error).message },
169+
};
170+
}
171+
};
172+
173+
// Send all requests in parallel and collect results
174+
const results = await Promise.all(
175+
Array.from({ length: parallelWritesCount }, (_, i) =>
176+
attemptWrite(i + 1),
177+
),
178+
);
179+
// Results will look like:
180+
// [
181+
// {
182+
// "attempt": 1,
183+
// "success": true
184+
// },
185+
// {
186+
// "attempt": 2,
187+
// "success": false,
188+
// "error": {
189+
// "message": "KV PUT failed: 429 Too Many Requests"
190+
// }
191+
// },
192+
// ...
193+
// ]
194+
195+
return new Response(JSON.stringify(results), {
196+
headers: { "Content-Type": "application/json" },
197+
});
198+
},
199+
};
200+
```
201+
202+
To handle these errors, we recommend implementing a retry logic, with exponential backoff. Here is a simple approach to add retries to the above code.
203+
204+
```typescript
205+
export default {
206+
async fetch(request, env, ctx): Promise<Response> {
207+
// Rest of code omitted
208+
const key = "common-key";
209+
const parallelWritesCount = 20;
210+
211+
// Helper function to attempt a write to KV with retries
212+
const attemptWrite = async (i: number) => {
213+
return await retryWithBackoff(async () => {
214+
await env.YOUR_KV_NAMESPACE.put(key, `Write attempt #${i}`);
215+
return { attempt: i, success: true };
216+
});
217+
};
218+
219+
// Send all requests in parallel and collect results
220+
const results = await Promise.all(
221+
Array.from({ length: parallelWritesCount }, (_, i) =>
222+
attemptWrite(i + 1),
223+
),
224+
);
225+
226+
return new Response(JSON.stringify(results), {
227+
headers: { "Content-Type": "application/json" },
228+
});
229+
},
230+
};
231+
232+
async function retryWithBackoff(
233+
fn: Function,
234+
maxAttempts = 5,
235+
initialDelay = 1000,
236+
) {
237+
let attempts = 0;
238+
let delay = initialDelay;
239+
240+
while (attempts < maxAttempts) {
241+
try {
242+
// Attempt the function
243+
return await fn();
244+
} catch (error) {
245+
// Check if the error is a rate limit error
246+
if (
247+
(error as Error).message.includes(
248+
"KV PUT failed: 429 Too Many Requests",
249+
)
250+
) {
251+
attempts++;
252+
if (attempts >= maxAttempts) {
253+
throw new Error("Max retry attempts reached");
254+
}
255+
256+
// Wait for the backoff period
257+
console.warn(`Attempt ${attempts} failed. Retrying in ${delay} ms...`);
258+
await new Promise((resolve) => setTimeout(resolve, delay));
259+
260+
// Exponential backoff
261+
delay *= 2;
262+
} else {
263+
// If it's a different error, rethrow it
264+
throw error;
265+
}
266+
}
267+
}
268+
}
269+
```
270+
139271
## Other methods to access KV
140272
141273
You can also [write key-value pairs from the command line with Wrangler](/kv/reference/kv-commands/#create) and [write data via the API](/api/operations/workers-kv-namespace-write-key-value-pair-with-metadata).

0 commit comments

Comments
 (0)