INTEGRITY Dokumentace

Zápis párů klíč-hodnota

Chcete-li vytvořit nový pár klíč-hodnota nebo aktualizovat hodnotu pro konkrétní klíč, zavolejte put() metoda v rámci Vazba KV na libovolném KV namespace kterou jste navázali na svůj kód Workeru:

env.NAMESPACE.put(key, value);
self.env.NAMESPACE.put(key, value)

Příklad

Příklad zápisu páru klíč-hodnota z Workeru:

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)

Referenční informace

K zápisu do KV slouží následující metoda:

put() metoda

Chcete-li vytvořit nový pár klíč-hodnota nebo aktualizovat hodnotu pro konkrétní klíč, zavolejte put() metodu na libovolném KV namespace, který jste navázali na kód svého Workeru:

env.NAMESPACE.put(key, value, options?);
self.env.NAMESPACE.put(key, value, options)

Parametry

Odpověď

Metoda put() vrací Promise, který byste měli await k ověření úspěšné aktualizace.

Pokyny

Souběžné zápisy do stejného klíče

Vzhledem k tomu, že KV nabízí pouze eventuální konzistenci, mohou se souběžné zápisy do stejného klíče navzájem přepsat. Osvědčeným postupem je zapisovat data z jediného procesu, například pomocí Wrangleru, Durable Objects nebo API. Díky jedinému toku dat se tak souběžným zápisům vyhnete. Všechna data přitom zůstávají snadno dostupná ve všech Workerech připojených k danému namespace.

Pokud jsou do stejného klíče provedeny souběžné zápisy, má přednost poslední zápis.

Zápisy jsou okamžitě viditelné pro ostatní požadavky ve stejné lokalitě globální sítě, ale mohou trvat až 60 sekund (nebo hodnotu cacheTtl parametru get() nebo getWithMetadata() metod), než budou viditelné v ostatních částech světa.

Viz Jak KV funguje pro další informace k tomuto tématu.

Hromadný zápis dat

Zapisujte více párů klíč-hodnota najednou pomocí Wrangler nebo prostřednictvím REST API.

Hromadné API dokáže najednou zpracovat až 10 000 párů KV.

A key a value jsou vyžadovány pro každý pár KV. Celková velikost požadavku musí být menší než 100 megabajtů. Hromadné zápisy nejsou podporovány pomocí Vazba KV.

Vypršující klíče

KV umožňuje vytvářet klíče, které automaticky vyprší. Vypršení platnosti můžete nastavit buď k určitému okamžiku (pomocí expiration option), nebo po uplynutí určité doby od poslední úpravy klíče (pomocí expirationTtl možnost).

Jakmile nastane čas expirace klíče, bude ze systému odstraněn. Po odstranění se pokusy o přečtení klíče chovají, jako by klíč neexistoval. Odstraněný klíč se nezapočítává do využití úložiště namespace KV pro účely fakturace.

Existují dva způsoby, jak určit, kdy má klíč vypršet:

Cíle expirace kratší než 60 sekund od aktuálního okamžiku nejsou podporovány. Platí to pro obě metody nastavení expirace.

Vytváření vypršujících klíčů

Chcete-li vytvořit klíče s omezenou platností, nastavte expiration v put() options na číslo představující počet sekund od epochy, nebo nastavte expirationTtl v put() options na číslo představující počet sekund od teď:

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)

Předpokládá se přitom, že secondsSinceEpoch/seconds_since_epoch a secondsFromNow/seconds_from_now jsou proměnné definované jinde ve vašem kódu Workeru.

Metadata

Chcete-li k páru klíč-hodnota přiřadit metadata, nastavte metadata v put() options na objekt (serializovatelný do JSON):

await env.NAMESPACE.put(key, value, {
	metadata: { someMetadataKey: "someMetadataValue" },
});
await self.env.NAMESPACE.put(key, value, metadata={"someMetadataKey": "someMetadataValue"})

Limity pro zápisy KV do stejného klíče

Workers KV umožňuje maximálně 1 zápis do stejného klíče za sekundu. Zápisy do stejného klíče provedené během 1 sekundy způsobí omezení rychlosti (429) vyvolání chyb.

Do stejného klíče byste neměli zapisovat víckrát než jednou za sekundu. Zvažte sloučení zápisů do klíče v rámci jednoho volání Workeru do jediného zápisu, nebo mezi jednotlivými zápisy čekejte alespoň 1 sekundu.

Následující příklad ukazuje, jak může více zápisů do stejného klíče vracet chyby, pokud v rámci jednoho volání Workeru vynutíte souběžné zápisy. Tento postup byste neměli používat v produkčním prostředí.

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))

K ošetření těchto chyb doporučujeme implementovat logiku opakovaných pokusů s exponenciálním prodlužováním intervalu (exponential backoff). Zde je jednoduchý postup, jak do výše uvedeného kódu přidat opakované pokusy.

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

Další způsoby přístupu ke KV

Můžete také zapisovat páry klíč-hodnota v příkazovém řádku pomocí Wrangler a zapisovat data prostřednictvím REST API.