章と記事の一覧
動かすストレージ
KV
速い読み取りと結果整合。キャッシュや設定の置き場
世界中の数百都市にあるエッジのキャッシュから、よく読まれるキーを低いレイテンシで返す——そう聞くと万能のデータストアに見える。だが、この読み出しの速さは、結果整合性(Eventual Consistency)というトレードオフの上に成り立っている。
KV への書き込みは、まず中央のデータストアに入る。各ロケーションのキャッシュへ自動で送られるわけではなく、読み取りに応じてキャッシュに載る(プッシュとプルを組み合わせた複製)。そのため別のロケーションでは、キャッシュの有効期限(cacheTtl、既定 60 秒)が切れるまで古い値が見え、反映に 60 秒以上かかることもある。東京のエッジで書き込んだ最新の値が、同じ瞬間にロンドンのエッジから読んだときに見えている保証はない。
適した用途と不適な用途
- 適しているもの: 設定値、機能フラグ、URL リダイレクト表、セッション情報のキャッシュ、OGP メタデータのキャッシュ(書き込みが極めて稀で、読み取り頻度が圧倒的に高いデータ)。
- 破綻するもの: 在庫の引き当てカウンタ、残高管理、リアルタイムの参加人数カウント(直前の更新が反映されていないと事故になる用途)。
基本操作と仕様上の限界
get(key)/put(key, value)/delete(key)/list({ prefix })- 1 つのキーに格納できる値のサイズ上限は 25MB。JSON 文字列やバイナリのキャッシュ用途には十分な広さを持つ。
- 一覧の取得は前方一致(
prefix)検索のみをサポートする。全件走査や複雑な絞り込みは得意としない。 - 課金構造は読み取りに比べて書き込みが割高に設定されている(100 万回書き込みあたり 5 ドル)。書き込み頻度が高い設計はコストを圧迫する。
エディタ上の env.KV は、実際の KV を RPC で中継したもので、本番のバインディングとは引数と戻り値が異なる。put() はオブジェクトを JSON 文字列にして保存する(本番の put() が受け取るのは文字列・ArrayBuffer・ストリームだけ)。get() は常に文字列を返し、type は指定できない。put() のオプションと getWithMetadata() も使えない。list() は接頭辞(文字列か { prefix })を受け取り、キー名の配列を返す(本番では { keys, list_complete, cursor })。保存先は全員で共有している。
await env.KV.put("user:1", "Alice");
const user = await env.KV.get("user:1"); // 'Alice'
const list = await env.KV.list({ prefix: "user:" }); // エディタでは ["user:1", ...]、本番では { keys: [...], list_complete, cursor }export default {
async fetch(request, env) {
// 書き込み(結果整合性: 別のエッジへ伝播するまで遅延が生じることがある)
// Write (eventual consistency: changes take time to propagate globally)
await env.KV.put("user:1", "Alice");
await env.KV.put("user:2", "Bob");
// エディタの put() はオブジェクトを JSON 文字列にして保存する(本番の KV は文字列・ArrayBuffer・ストリームのみ)
// In the editor, put() stores an object as a JSON string (production KV accepts only a string, ArrayBuffer, or stream)
await env.KV.put("user:3", { name: "Carol", plan: "pro" });
console.log("list:", await env.KV.list("user:"));
// 読み出し(get() は常に文字列を返す。オブジェクトは JSON 文字列のまま返る)
// Read (get() always returns a string; the object comes back as a JSON string)
const user1 = await env.KV.get("user:1");
const user3 = await env.KV.get("user:3");
console.log("user:1 =", user1);
console.log("user:3 =", user3);
// 削除 / Delete
await env.KV.delete("user:2");
return Response.json({
user1,
user3,
afterDelete: await env.KV.list("user:"),
});
},
};
実行結果
実行するとここに結果が出ます。ネットワークは塞がれていますが、D1・KV・R2・Durable Object の状態は訪問者全員で共有されます。