← Cloudflare Workers KV / kv / api
Запись пар «ключ-значение»
Чтобы создать новую пару ключ-значение или обновить значение для определённого ключа, вызовите put() метод объекта Привязка KV для любого Пространство имён KV привязанных к коду вашего Worker:
env.NAMESPACE.put(key, value);self.env.NAMESPACE.put(key, value)Пример
Пример записи пары «ключ-значение» из Worker:
export default {
async fetch(request, env, ctx) {
try {
await env.NAMESPACE.put("first-key", "This is the value for the key");
return new Response("Successful write", {
status: 201,
});
} catch (e) {
return new Response(e.message, { status: 500 });
}
},
};from workers import WorkerEntrypoint, Response
class Default(WorkerEntrypoint):
async def fetch(self, request):
try:
await self.env.NAMESPACE.put("first-key", "This is the value for the key")
return Response("Successful write", status=201)
except Exception as e:
return Response(str(e), status=500)Справочник
Ниже приведен метод для записи в KV:
put() метод
Чтобы создать новую пару ключ-значение или обновить значение для определённого ключа, вызовите put() метод для любого пространства имен KV, привязанного к коду вашего Worker:
env.NAMESPACE.put(key, value, options?);self.env.NAMESPACE.put(key, value, options)Параметры
-
key:string- Ключ, который будет связан со значением. Ключ не может быть пустым или точно равным
.или... Все остальные ключи допустимы. Максимальная длина ключа составляет 512 байт.
- Ключ, который будет связан со значением. Ключ не может быть пустым или точно равным
-
value:string|ReadableStream|ArrayBuffer- Значение для сохранения. Тип определяется автоматически. Максимальный размер значения составляет 25 МиБ.
-
options:{ expiration?: number, expirationTtl?: number, metadata?: object }- Необязательно. Объект, содержащий
expiration(необязательно),expirationTtl(необязательно), иmetadata(необязательные) атрибуты.expirationэто число, которое указывает момент истечения срока действия пары ключ-значение в секундах с начала эпохи.expirationTtlэто число, которое указывает, через сколько секунд от текущего момента истечёт срок действия пары ключ-значение. Минимальное значение 60.metadataпредставляет собой объект, который должен сериализоваться в JSON. Максимальный размер сериализованного JSON-представления объекта метаданных составляет 1024 байта.
- Необязательно. Объект, содержащий
Ответ
response:Promise<void>- A
Promiseкоторый разрешается, если обновление выполнено успешно.
- A
Метод put() возвращает Promise, который следует await чтобы убедиться в успешном обновлении.
Рекомендации
Параллельная запись в один ключ
Поскольку KV обеспечивает согласованность в конечном счёте, параллельная запись в один и тот же ключ может привести к тому, что одна запись перезапишет другую. Поэтому принято записывать данные из единственного процесса: через Wrangler, Durable Objects или API. Такой подход исключает конкуренцию параллельных записей за счёт единого потока данных. При этом все данные остаются доступны во всех Workers, привязанных к пространству имён.
Если к одному и тому же ключу выполняются параллельные операции записи, приоритет получает последняя запись.
Запись сразу становится видна другим запросам в том же дата-центре сети, но её появление в других дата-центрах может занять до 60 секунд (или значение cacheTtl параметр get() или getWithMetadata() методы) в других частях света.
См. Как работает KV с дополнительной информацией по этой теме.
Массовая запись данных
Запись нескольких пар «ключ-значение» за один раз с помощью Wrangler или через REST API.
Массовый API может принимать до 10,000 пар KV за один раз.
A key и value обязательны для каждой пары KV. Общий размер запроса должен быть менее 100 мегабайт. Массовая запись не поддерживается при использовании Привязка KV.
Истечение срока действия ключей
KV позволяет создавать ключи с автоматическим истечением срока действия. Истечение срока можно настроить либо на определённый момент времени (с помощью expiration параметр), либо после того как пройдёт определённое время с момента последнего изменения ключа (с помощью параметра expirationTtl опцию).
Когда истекает срок действия ключа, он удаляется из системы. После удаления попытки прочитать этот ключ будут вести себя так, как если бы ключ не существовал. Удалённый ключ не учитывается в использовании хранилища пространства имён KV для целей тарификации.
Есть два способа указать, когда ключ должен истечь:
-
Задайте срок действия ключа с помощью абсолютного времени, указанного в количестве секунды с начала эпохи UNIX ↗. Например, если нужно, чтобы срок действия ключа истекал 1 апреля 2019 года в 12:00AM UTC, установите значение истечения срока действия ключа равным
1554076800. -
Задайте время жизни ключа (TTL) в виде относительного количества секунд от текущего момента. Например, если нужно, чтобы ключ истек через 10 минут после создания, установите TTL равным
600.
Время истечения, которое наступает менее чем через 60 секунд, не поддерживается. Это ограничение действует для обоих способов задания срока действия.
Создание ключей с истекающим сроком действия
Чтобы создать ключи с истекающим сроком действия, задайте expiration в put() в число, представляющее количество секунд с начала эпохи, либо задайте expirationTtl в put() в число, представляющее количество секунд от текущего момента:
await env.NAMESPACE.put(key, value, {
expiration: secondsSinceEpoch,
});
await env.NAMESPACE.put(key, value, {
expirationTtl: secondsFromNow,
});await self.env.NAMESPACE.put(key, value, expiration=seconds_since_epoch)
await self.env.NAMESPACE.put(key, value, expirationTtl=seconds_from_now)Здесь предполагается, что secondsSinceEpoch/seconds_since_epoch и secondsFromNow/seconds_from_now это переменные, определённые в другом месте кода вашего Worker.
Метаданные
Чтобы связать метаданные с парой ключ-значение, задайте metadata в put() в объект (сериализуемый в JSON):
await env.NAMESPACE.put(key, value, {
metadata: { someMetadataKey: "someMetadataValue" },
});await self.env.NAMESPACE.put(key, value, metadata={"someMetadataKey": "someMetadataValue"})Ограничения на запись KV в один и тот же ключ
Workers KV допускает не более 1 записи в один и тот же ключ в секунду. Запись в один ключ чаще одного раза в 1 секунду приводит к ограничению скорости (429) к возникновению ошибок.
Не следует записывать в один и тот же ключ чаще одного раза в секунду. Постарайтесь объединять записи в ключ в рамках одного вызова Worker в одну запись или выдерживайте паузу не менее 1 секунды между записями.
Следующий пример показывает, как несколько одновременных записей одного и того же ключа в рамках одного вызова Worker могут приводить к ошибкам. Не используйте такой подход в продакшене.
export default {
async fetch(request, env, ctx): Promise<Response> {
// Rest of code omitted
const key = "common-key";
const parallelWritesCount = 20;
// Helper function to attempt a write to KV and handle errors
const attemptWrite = async (i: number) => {
try {
await env.YOUR_KV_NAMESPACE.put(key, `Write attempt #${i}`);
return { attempt: i, success: true };
} catch (error) {
// An error may be thrown if a write to the same key is made within 1 second with a message. For example:
// error: {
// "message": "KV PUT failed: 429 Too Many Requests"
// }
return {
attempt: i,
success: false,
error: { message: (error as Error).message },
};
}
};
// Send all requests in parallel and collect results
const results = await Promise.all(
Array.from({ length: parallelWritesCount }, (_, i) =>
attemptWrite(i + 1),
),
);
// Results will look like:
// [
// {
// "attempt": 1,
// "success": true
// },
// {
// "attempt": 2,
// "success": false,
// "error": {
// "message": "KV PUT failed: 429 Too Many Requests"
// }
// },
// ...
// ]
return new Response(JSON.stringify(results), {
headers: { "Content-Type": "application/json" },
});
},
};from workers import WorkerEntrypoint, Response
import asyncio
class Default(WorkerEntrypoint):
async def fetch(self, request):
key = "common-key"
parallel_writes_count = 20
async def attempt_write(i):
try:
await self.env.YOUR_KV_NAMESPACE.put(key, f"Write attempt #{i}")
return {"attempt": i, "success": True}
except Exception as error:
# An error may be thrown if a write to the same key is made
# within 1 second with a message like:
# "KV PUT failed: 429 Too Many Requests"
return {"attempt": i, "success": False, "error": {"message": str(error)}}
results = await asyncio.gather(
*[attempt_write(i + 1) for i in range(parallel_writes_count)]
)
# Results will look like:
# [
# {
# "attempt": 1,
# "success": True
# },
# {
# "attempt": 2,
# "success": False,
# "error": {
# "message": "KV PUT failed: 429 Too Many Requests"
# }
# },
# ...
# ]
return Response.json(list(results))Чтобы обрабатывать эти ошибки, рекомендуем реализовать логику повторных попыток с экспоненциальной задержкой. Ниже приведён простой способ добавить повторные попытки в приведённый выше код.
export default {
async fetch(request, env, ctx): Promise<Response> {
// Rest of code omitted
const key = "common-key";
const parallelWritesCount = 20;
// Helper function to attempt a write to KV with retries
const attemptWrite = async (i: number) => {
return await retryWithBackoff(async () => {
await env.YOUR_KV_NAMESPACE.put(key, `Write attempt #${i}`);
return { attempt: i, success: true };
});
};
// Send all requests in parallel and collect results
const results = await Promise.all(
Array.from({ length: parallelWritesCount }, (_, i) =>
attemptWrite(i + 1),
),
);
return new Response(JSON.stringify(results), {
headers: { "Content-Type": "application/json" },
});
},
};
async function retryWithBackoff(
fn: Function,
maxAttempts = 5,
initialDelay = 1000,
) {
let attempts = 0;
let delay = initialDelay;
while (attempts < maxAttempts) {
try {
// Attempt the function
return await fn();
} catch (error) {
// Check if the error is a rate limit error
if (
(error as Error).message.includes(
"KV PUT failed: 429 Too Many Requests",
)
) {
attempts++;
if (attempts >= maxAttempts) {
throw new Error("Max retry attempts reached");
}
// Wait for the backoff period
console.warn(`Attempt ${attempts} failed. Retrying in ${delay} ms...`);
await new Promise((resolve) => setTimeout(resolve, delay));
// Exponential backoff
delay *= 2;
} else {
// If it's a different error, rethrow it
throw error;
}
}
}
}from workers import WorkerEntrypoint, Response
import asyncio
class Default(WorkerEntrypoint):
async def fetch(self, request):
key = "common-key"
parallel_writes_count = 20
async def attempt_write(i):
return await retry_with_backoff(
lambda: self.env.YOUR_KV_NAMESPACE.put(key, f"Write attempt #{i}"),
success_result={"attempt": i, "success": True},
)
results = await asyncio.gather(
*[attempt_write(i + 1) for i in range(parallel_writes_count)]
)
return Response.json(list(results))
async def retry_with_backoff(fn, success_result, max_attempts=5, initial_delay=1.0):
attempts = 0
delay = initial_delay
while attempts < max_attempts:
try:
await fn()
return success_result
except Exception as error:
if "KV PUT failed: 429 Too Many Requests" in str(error):
attempts += 1
if attempts >= max_attempts:
raise Exception("Max retry attempts reached")
print(f"Attempt {attempts} failed. Retrying in {delay}s...")
await asyncio.sleep(delay)
delay *= 2
else:
raiseДругие способы доступа к KV
Вы также можете запись пар «ключ-значение» в командной строке с помощью Wrangler и запись данных через REST API.