遅延する設定更新を扱う

CloudflareBeginner
オンラインで実践に進む

はじめに

ヘルプセンターでは、KV からテーマやウェルカムバナーを読み込むことで、エディターが新しいコードをデプロイせずに設定を変更できます。更新後は、場所によって一時的に異なるバージョンが表示されることがあります。この移行中も、すべての読み取りで最新の設定が返されると仮定せず、アプリケーションを利用できる状態に保つ必要があります。

この実験では、安全なデフォルト値を備えたバージョン付き設定リーダーを構築し、古い値と新しい値を意図的に組み合わせたシーケンスをテストしてから、実際のクラウド更新を行います。バージョンは設定とともに保存されるラベルで、受け取った値を識別するのに役立ちます。ただし、KV を強整合性データベースに変えるものではなく、連続するリクエストでバージョン番号が増加することを保証するものでもありません。

先に KV の実験を完了してください。この独立した VM には Node.js 22.22.0 と、プロジェクトローカルの Wrangler 4.131.1 が /home/labex/project/delayed-config に用意されています。同じ account-read、Worker-write、KV-write 権限を持つ自分の学習用アカウントを使用してください。この演習では、使い捨ての Worker と namespace を 1 つずつ作成し、合成された表示設定だけを扱います。データ量が少ないため、有料アップグレードやドメインの購入は必要ありません。これらの設定は、認可、支払い、その他すぐに権威のある更新が必要な判断を制御するものではありません。

独立した設定ストアに接続する

このステップでは、表示設定用の新しい namespace に接続します。CONFIG binding は、リソースへの参照を標準的な Wrangler 設定に保持します。意図的に無効な設定が別のアプリケーションに影響しないように、新しい実験用リソースを使用します。

用意されているプロジェクトに移動します。

cd /home/labex/project/delayed-config

一意な名前を一度だけ生成します。openssl rand -hex 6 はランダムなサフィックスを出力し、$(...) はその値を名前に挿入します。シェル変数に名前を保存すると、このターミナルで続けて実行するコマンドから利用できます。

WORKER_NAME="labex-config-$(openssl rand -hex 6)"
printf '%s\n' "$WORKER_NAME"

この VM を認証します。アカウントの識別情報の読み取りに加えて、Workers Scripts Write ではデプロイと削除が可能になり、Workers KV Write ではこの実験の namespace とキーを管理できるようになります。

npx wrangler login --device --browser=false --scopes account:read user:read workers_scripts:write workers_kv:write

表示されたデバイスリンクをブラウザーで開き、現在のコードを入力します。要求された権限と学習用アカウントを確認して、Wrangler を認証してください。同意ページにはバックグラウンドアクセスが表示される場合もあります。ターミナルに戻り、ログインが完了するまで待ちます。

このコースの前半で付与した Worker と KV の書き込み権限を確認し、学習用アカウントを確認します。

npx wrangler whoami --json

loggedIn: true と、学習用アカウントの name が表示されることを確認します。アカウントが 1 つだけ表示される場合も同様です。そのアカウントの id をコピーしてください。次の設定で YOUR_ACCOUNT_ID を置き換えてからコマンドを実行します。ここで使う cat のヒアドキュメントは、2 つの JSON 行の間にあるすべての内容をファイルに書き込みます。> はファイルを置き換えます。区切り文字が引用符で囲まれていないため、シェルは $WORKER_NAME を展開します。

cat > wrangler.jsonc <<JSON
{
  "name": "$WORKER_NAME",
  "main": "src/index.js",
  "compatibility_date": "2026-07-30",
  "account_id": "YOUR_ACCOUNT_ID",
  "workers_dev": true
}
JSON

そのアカウントに namespace を作成します。タイトルには Worker の一意な名前が含まれるため、後で対応するリソースを識別できます。--update-config=false を指定すると、ファイルは自動変更されず、binding の編集内容を自分で確認できます。

npx wrangler kv namespace create "$WORKER_NAME-config" --update-config=false

出力には新しい namespace ID が含まれます。これをコピーし、次の完全な設定内にある YOUR_ACCOUNT_ID と YOUR_NAMESPACE_ID を置き換えます。CONFIG binding の名前はコードで使用するためのもので、ID は実際の Cloudflare リソースを識別します。

cat > wrangler.jsonc <<JSON
{
  "name": "$WORKER_NAME",
  "main": "src/index.js",
  "compatibility_date": "2026-07-30",
  "account_id": "YOUR_ACCOUNT_ID",
  "workers_dev": true,
  "kv_namespaces": [
    { "binding": "CONFIG", "id": "YOUR_NAMESPACE_ID" }
  ]
}
JSON
npx wrangler kv namespace list

この実験で作成した namespace のタイトルを探し、ID がファイル内の値と一致することを確認します。他の namespace が表示されても、そのままにしてください。この設定には、後続のコマンドが使用するアカウントとリソースが記録されています。binding は namespace への参照であり、データのコピーではありません。

安全なデフォルト値でバージョン付き設定を読み取る

このステップでは、有効な 2 つのバージョンから利用可能なレスポンスを返せるようにします。表示設定がない場合や壊れている場合は、標準のライトテーマとバナーなしにフォールバックします。これにより、任意の表示設定がヘルプセンター全体を壊すことを防ぎます。

ハンドラーを作成します。固定の defaults オブジェクトにはリクエスト固有の状態が含まれておらず、変更されることもありません。各リクエストは、自分自身の KV の読み取り結果を取得します。

cat > src/index.js <<'JS'
const defaults = { version: 0, theme: "light", banner: "", source: "default" };

export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    if (url.pathname === "/health") return Response.json({ status: "ok" });
    const key = url.searchParams.get("key") ?? "config:current";
    if (url.pathname !== "/settings" || !/^config:[a-z0-9-]{1,20}$/.test(key)) {
      return new Response("Not found", { status: 404 });
    }
    let value;
    try {
      value = await env.CONFIG.get(key, { type: "text", cacheTtl: 60 });
    } catch {
      return Response.json({ error: "Settings temporarily unavailable" }, { status: 503 });
    }
    if (value === null) return Response.json(defaults);
    let settings;
    try {
      settings = JSON.parse(value);
    } catch {
      return Response.json(defaults);
    }
    if (!settings || !Number.isSafeInteger(settings.version) || settings.version < 1 ||
        !["light", "dark"].includes(settings.theme) ||
        typeof settings.banner !== "string" || settings.banner.length > 80) {
      return Response.json(defaults);
    }
    return Response.json({
      version: settings.version, theme: settings.theme,
      banner: settings.banner, source: "stored"
    });
  }
};
JS

KV へのリクエストでは、秒単位の読み取りキャッシュ期間である cacheTtl: 60 を使用しています。これは保存されたキーを期限切れにする設定ではありません。また、すべての場所で最新の値を取得するよう指示するものでもありません。保存された値と、キーが存在しない場合の結果は、どちらもキャッシュされる可能性があります。書き込みは頻繁に行わず、古くても有効な設定をアプリケーションが受け入れられるように設計してください。

ハンドラーは、値を使用する前にバージョン、サポート対象のテーマ、バナーの長さを検証します。/health ルートは、任意の設定を読み取らずにレスポンスを返します。ストレージ障害が発生した場合、/settings は明示的に 503 を返します。アプリケーションがストレージからデフォルト値を正常に読み込めたかのように誤って報告することはありません。

小さなバージョンファイルを 2 つ作成します。意図した値を通常のファイルに保存しておくと、書き込む前に簡単に確認できます。

cat > config-v1.json <<'JSON'
{"version":1,"theme":"light","banner":"Welcome"}
JSON
cat > config-v2.json <<'JSON'
{"version":2,"theme":"dark","banner":"New help center"}
JSON

これらを、壊れた値とともに別々のローカル fixture キーへ書き込みます。これらのキーによって入力候補を再現できますが、Cloudflare のネットワークタイミングをシミュレートするものではありません。

npx wrangler kv key put config:v1 --path config-v1.json --binding CONFIG --local
npx wrangler kv key put config:v2 --path config-v2.json --binding CONFIG --local
npx wrangler kv key put config:broken broken-json --binding CONFIG --local
npx wrangler dev --local --ip 0.0.0.0 --port 8080 > local.log 2>&1 &
DEV_PID=$!
cat local.log

準備完了のメッセージが表示されるまで待ちます。その後、2 つのキーを明示的に指定して比較します。

curl -i 'http://127.0.0.1:8080/settings?key=config:v1'
curl -i 'http://127.0.0.1:8080/settings?key=config:v2'

どちらも source: "stored" を含む HTTP 200 を返します。バージョン 1 はライトテーマで Welcome、バージョン 2 はダークテーマで New help center になります。どちらも、前のリクエストの結果には依存しません。

curl -i 'http://127.0.0.1:8080/settings?key=config:missing'
curl -i 'http://127.0.0.1:8080/settings?key=config:broken'

どちらも {"version":0,"theme":"light","banner":"","source":"default"} を返すはずです。バージョン 0 はアプリケーションのデフォルトラベルであり、保存された KV のリビジョンではありません。

curl -i http://127.0.0.1:8080/health

{"status":"ok"} が返ることを確認します。クリーンアップまでローカルサーバーを起動したままにしてください。

制御された古い読み取りのシーケンスを試す

このステップでは、古い値、新しい値、再び古い値、新しい値、値なし、無効な値という予測可能なシーケンスに対してハンドラーをテストします。これは test fixture、つまり本来は予測できない状況を再現可能にするために意図的に与えた入力です。実際の Cloudflare リクエストが古い値を返した証拠ではありません。

標準の assertion ライブラリを使って小さな Node.js テストを作成します。作成したハンドラーをインポートし、制御された戻り値を使って同じ CONFIG.get() インターフェースを提供します。

cat > test-config.mjs <<'JS'
import assert from "node:assert/strict";
import worker from "./src/index.js";

const older = JSON.stringify({ version: 1, theme: "light", banner: "Welcome" });
const newer = JSON.stringify({ version: 2, theme: "dark", banner: "New help center" });
// A controlled fixture: these values simulate different reads, not a cloud outage.
const values = [older, newer, older, newer, null, "broken-json"];
const expectedVersions = [1, 2, 1, 2, 0, 0];
for (let i = 0; i < values.length; i += 1) {
  const env = { CONFIG: { get: async () => values[i] } };
  const response = await worker.fetch(new Request("https://example.test/settings"), env);
  assert.equal(response.status, 200);
  const body = await response.json();
  assert.equal(body.version, expectedVersions[i]);
  assert.ok(["light", "dark"].includes(body.theme));
  assert.equal(typeof body.banner, "string");
}
console.log("Controlled old/new/missing/invalid reads stayed usable.");
JS
node test-config.mjs

Controlled old/new/missing/invalid reads stayed usable. が表示されることを確認します。assertion に失敗すると、コマンドはエラーで停止します。古い値が再び返されるのは意図的な動作です。それを隠すために、プロセス全体で共有する「最新バージョン」変数を追加しないでください。Workers は異なるインスタンスで実行される可能性があるため、そのような変数でアカウント全体の最新バージョンを確立することはできません。

設定を更新している間も、古い有効な値を読み取るクライアントが利用できる状態を保つ必要があります。この方法は、バナーやテーマに適しています。誰かのアクセスを直ちに取り消す用途に KV を適したものにするわけではありません。次は実際のクラウドテストです。最初のリクエストから新しい値が表示される場合もありますが、それも有効な結果です。

デプロイして最初のクラウド上のバージョンを確立する

このステップでは、設定を変更する前に、実際のリモート環境に基準となる値を保存します。この実験のクラウド namespace には、現在の設定と壊れた値のフォールバック用 fixture だけを配置します。

npx wrangler kv key put config:current --path config-v1.json --binding CONFIG --remote
npx wrangler kv key put config:broken broken-json --binding CONFIG --remote
npx wrangler deploy

一意な Worker 名と CONFIG binding を確認し、実際の公開 URL をコピーします。

WORKER_URL="https://YOUR_WORKER.YOUR_SUBDOMAIN.workers.dev"
curl -i "$WORKER_URL/settings"

バージョン 1、テーマ light、バナー Welcome、source: "stored" が返ることを確認します。必要であれば、ホスト名の準備と KV の可視化に時間を取ります。ネットワークエラーは設定のレスポンスではありません。このステップの独立したチェックは、バージョン 1 を置き換える前に実行してください。選択したアカウント、保存された値、デプロイ済み binding、デフォルト値、health レスポンスを検証します。

古い読み取りを要求せずに実際の更新を確認する

このステップでは、Worker のコードを変更せずに保存済みの設定を更新します。意図した新しい値を確認してから、同じリモートキーに書き込みます。

cat config-v2.json
npx wrangler kv key put config:current --path config-v2.json --binding CONFIG --remote
npx wrangler kv key get config:current --binding CONFIG --remote --text

管理用の読み取り結果にバージョン 2 が含まれていることを確認します。次に、アプリケーションから見える値を確認します。

curl -i "$WORKER_URL/settings"

すぐにバージョン 2 が表示される場合もあれば、読み取りが収束するまで有効な古いバージョンが表示される場合もあります。eventual consistency を「証明」するために古いレスポンスを必須にしないでください。また、古いレスポンスを発生させるためにキーを短時間で繰り返し書き換えないでください。必要に応じて、15 秒間隔で最大 5 分間、HTTP リクエストを繰り返します。これは実験で定めた観察時間であり、すべてのグローバルな場所が 5 分以内に収束することを保証するものではありません。

エンドポイントが {"version":2,"theme":"dark","banner":"New help center","source":"stored"} を返したら次に進みます。その観察時間内に収束しない場合は、アカウントと binding を確認し、結果が判定不能であることを報告してください。独立したチェックでは、実際に保存されたバージョンと実際のレスポンスが一致している必要があります。ローカルファイルやテスト fixture では条件を満たしません。

curl -i "$WORKER_URL/settings?key=config:broken"
curl -i "$WORKER_URL/health"

壊れた表示設定には制御されたデフォルト値が適用され、health は引き続き ok になります。Dashboard で同じアカウントを選択し、Storage & databases → Workers KV の下にあるこの実験の namespace を開きます。config:current の値が、ファイル内のバージョン 2 と一致することを確認します。この読み取り専用ビューには管理対象の値が表示されますが、すべてのリモート環境が現在どの値をキャッシュしているかを証明することはできません。

制御されたテストでは古い値への耐性を確認し、実際の更新ではデプロイとテスト用エンドポイントでの収束を確認しました。この 2 つの結論を混同しないでください。整合性モデルについては、KV の仕組みを参照してください。

KV Pairs を選択し、config:current の横にある View をクリックします。更新前の一覧が表示されている場合は Refresh を使います。次の例にはバージョン 2、テーマ dark、バナー New help center が表示されています。生成される名前空間名は実行ごとに異なります。この確認では値を変更しないでください。

Dashboard に表示されたバージョン 2 の設定

使い捨てのクラウドリソースを削除する

このステップでは、Wrangler の認証が有効なうちに 2 つのリソースを削除します。namespace は Worker より長く残る可能性があるため、アプリケーションだけを削除してもデータはクリーンアップされません。

このターミナルで起動したローカル開発プロセスを停止します。

kill "$DEV_PID"

削除する前に、保存したリソース参照を確認します。

cat wrangler.jsonc

labex-config-... という Worker 名と CONFIG namespace ID を確認します。この設定で選択されている Worker を削除します。

npx wrangler delete

確認を求められたら、表示された名前がこの実験のものと一致することを確認し、y で確定します。次に、CONFIG が参照している namespace だけを削除します。

npx wrangler kv namespace delete --binding CONFIG

確認プロンプトが表示されたら、受け入れる前に namespace を確認してください。独立したチェックで削除対象のリソースが存在しないことを確認できるように、wrangler.jsonc はそのまま残します。

npx wrangler kv namespace list

この実験の namespace が表示されず、関係のない namespace は残っていることを確認します。Dashboard の一覧を更新し、この実験の Worker と namespace が消えていることも確認してください。リクエストの失敗やログイン期限切れは、削除の証拠にはなりません。ログアウトする前にこのステップのチェックを実行し、認証済みのリソース一覧を確認できるようにしてください。

VM の認証を終了する

このステップでは、クリーンアップのチェックが成功した後に Wrangler を切断します。ログアウトすると、この VM に保存された Wrangler の認証が終了します。クラウドリソースが削除されることも、通常の Dashboard ブラウザーセッションからログアウトすることもありません。

npx wrangler logout
npx wrangler whoami --json

構造化された結果で "loggedIn": false と報告されることを確認します。認証されていない状態でのこのコマンドは、ゼロ以外の終了ステータスで完了する場合がありますが、ここでは想定された動作です。接続エラーだけが表示され、明示的な認証状態が示されない場合は、接続が回復してから再試行してください。

残ったローカルファイルとローカル KV の状態は、この使い捨て VM に属するものです。すでに削除したクラウドリソースとは別のものです。これで実験を終了できます。

まとめ

古い有効な設定と新しい有効な設定を受け入れ、値が存在しない場合や無効な場合には安全なデフォルト値を使い、任意の KV データから独立した health エンドポイントを維持する設定リーダーを構築しました。古い値を含む制御されたシーケンスを試した後、実際のリモートキーを変更し、デプロイ済みエンドポイントのレスポンスが収束する様子を確認しました。

バージョンラベルは返されたデータを説明するものであり、読み取りキャッシュ期間、期限切れ、強整合性とは別の概念であることを学びました。最後に、使い捨てのリソースを削除してログアウトしました。コースのチャレンジでは、正しい namespace binding と安全な通知処理を組み合わせます。