INTEGRITY Документация

Запись пар «ключ-значение»

Чтобы создать новую пару ключ-значение или обновить значение для определённого ключа, вызовите 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)

Параметры

Ответ

Метод 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 для целей тарификации.

Есть два способа указать, когда ключ должен истечь:

Время истечения, которое наступает менее чем через 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.