Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

キーと値のペアを書き込む

最終更新 Markdown で表示Agent セットアップ

新しいキーと値のペアを作成する、または特定のキーの値を更新するには、Worker コードにバインドした任意の KV 名前空間 に対して、KV バインディングput() メソッドを呼び出します。

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() メソッド

新しいキーと値のペアを作成する、または特定のキーの値を更新するには、Worker コードにバインドした任意の KV 名前空間で put() メソッドを呼び出します。

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

パラメーター

  • key: string

    • 値に関連付けるキーです。キーを空にしたり、. または .. と完全に一致させたりすることはできません。それ以外のキーはすべて有効です。キーの最大長は 512 バイトです。
  • value: string | ReadableStream | ArrayBuffer

    • 保存する値です。型は推論されます。値の最大サイズは 25 MiB です。
  • options: { expiration?: number, expirationTtl?: number, metadata?: object }

    • 省略可能です。expiration(省略可)、expirationTtl(省略可)、metadata(省略可)属性を含むオブジェクトです。
      • expiration は、エポックからの秒数でキーと値のペアの有効期限を表す数値です。
      • expirationTtl は、現在から何秒後にキーと値のペアを期限切れにするかを表す数値です。最小値は 60 です。
      • metadata は JSON にシリアライズできるオブジェクトです。メタデータオブジェクトのシリアライズ済み JSON の最大サイズは 1024 バイトです。

レスポンス

  • response: Promise<void>
    • 更新が成功すると解決する Promise です。

put() メソッドは Promise を返します。更新の成功を確認するには await してください。

ガイダンス

同じキーへの同時書き込み

KV は結果整合性のため、同じキーへの同時書き込みは互いに上書きすることがあります。一般的なパターンは、Wrangler、Durable Objects、または API から、単一のプロセスでデータを書き込むことです。ストリームが 1 つなので、競合する同時書き込みを避けられます。データは、その名前空間にバインドされたすべての Workers からすぐに利用できます。

同じキーに同時書き込みした場合、最後の書き込みが優先されます。

書き込みは、同じグローバルネットワーク拠点内の他のリクエストからはすぐに見えます。世界の他の地域で見えるようになるまで、最大 60 秒(または get() / getWithMetadata() メソッドの cacheTtl パラメーターの値)かかることがあります。

このトピックの詳細は KV の仕組み を参照してください。

データを一括で書き込む

Wrangler または REST API で、一度に複数のキーと値のペアを書き込めます。

一括 API は、一度に最大 10,000 個の KV ペアを受け取れます。

各 KV ペアには keyvalue が必要です。リクエスト全体のサイズは 100 メガバイト未満である必要があります。一括書き込みは KV バインディング では使えません。

期限付きキー

KV では、自動的に期限切れになるキーを作成できます。特定の時点で期限切れにする(expiration オプション)か、キーが最後に変更されてから一定時間が経過したあとに期限切れにする(expirationTtl オプション)かを設定できます。

期限付きキーの有効期限に達すると、システムから削除されます。削除後にそのキーを読むと、キーが存在しない場合と同じ動作になります。削除されたキーは、課金上の KV 名前空間のストレージ使用量には含まれません。

キーの期限切れを指定する方法は 2 つあります。

  • UNIX エポックからの秒数 で表した絶対時刻を使い、キーの有効期限を設定します。たとえば、2019 年 4 月 1 日 0:00 UTC に期限切れにしたい場合は、キーの expiration を 1554076800 にします。

  • 現在からの相対秒数で、キーの TTL(Time To Live)を設定します。たとえば、作成から 10 分後に期限切れにしたい場合は、expiration TTL を 600 にします。

60 秒未満の未来を対象にした有効期限は、どちらの方法でもサポートされません。

期限付きキーを作成する

期限付きキーを作成するには、put() のオプションで expiration にエポックからの秒数を設定するか、expirationTtl に現在からの秒数を設定します。

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_epochsecondsFromNow / seconds_from_now が、Worker コードの別の場所で定義された変数であることを前提としています。

メタデータ

キーと値のペアにメタデータを関連付けるには、put() のオプションで metadata にオブジェクト(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 回に制限しています。1 秒以内に同じキーへ書き込むと、レート制限(429)エラーが送出されます。

同じキーへ 1 秒に 2 回以上書き込まないでください。1 回の Worker 呼び出し内の書き込みを 1 回にまとめるか、書き込みのあいだに少なくとも 1 秒待ってください。

次の例は、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 経由でデータを書き込む 方法もあります。

役に立ちましたか?