Введение
Центр помощи может получать тему оформления и приветственный баннер из KV. Благодаря этому редактор сможет менять эти настройки без нового развёртывания кода. После обновления пользователи в разных местоположениях могут некоторое время видеть разные версии. Приложение должно оставаться работоспособным в этот переходный период и не рассчитывать, что каждое чтение возвращает самые новые настройки.
Вы создадите версионированный читатель конфигурации с безопасными значениями по умолчанию, протестируете намеренно смоделированную последовательность старых и новых значений, а затем выполните реальное обновление в облаке. Версия — это метка, сохранённая вместе с настройками; она помогает определить полученное значение. Версия не превращает KV в базу данных со строгой согласованностью и не гарантирует, что последовательные запросы будут видеть возрастающие номера версий.
Сначала завершите предыдущие лабораторные работы по KV. В этой независимой виртуальной машине установлены Node.js 22.22.0 и локальный Wrangler 4.131.1 в каталоге /home/labex/project/delayed-config. Используйте собственную учебную учётную запись с теми же разрешениями на чтение учётной записи, запись Worker и запись KV. В ходе упражнения создаются один временный Worker и одно пространство имён; в них публикуются только синтетические настройки отображения. Для небольшого набора данных платное обновление или приобретённый домен не требуются. Эти настройки не управляют авторизацией, платежами и другими решениями, требующими немедленного авторитетного обновления.
Подключение независимого хранилища конфигурации
На этом шаге вы подключите новое пространство имён для конфигурации отображения. Привязка CONFIG хранит ссылку на ресурс в стандартной конфигурации Wrangler. Используйте новые ресурсы лабораторной работы, чтобы намеренно некорректные настройки не повлияли на другое приложение.
Перейдите в подготовленный проект:
cd /home/labex/project/delayed-config
Один раз сгенерируйте уникальное имя. Команда openssl rand -hex 6 выводит случайный суффикс, а $(...) вставляет его в имя. Переменная оболочки сохранит это имя для следующих команд в данном терминале.
WORKER_NAME="labex-config-$(openssl rand -hex 6)"
printf '%s\n' "$WORKER_NAME"
Авторизуйте эту виртуальную машину. Помимо чтения идентификатора вашей учётной записи, разрешение Workers Scripts Write позволяет развёртывать и удалять ресурсы, а Workers KV Write — управлять пространством имён и ключами этой лабораторной работы.
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 стоит имя учебной учётной записи, даже если в списке присутствует только одна учётная запись. Скопируйте id этой учётной записи. Сохраните его в приведённой ниже конфигурации, заменив YOUR_ACCOUNT_ID перед выполнением команды. Здесь cat с heredoc записывает всё между двумя строками 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
Создайте пространство имён в этой учётной записи. Его название содержит уникальное имя Worker, чтобы позже вы могли легко найти эту пару ресурсов. Параметр --update-config=false оставляет изменение привязки на ваше усмотрение и не изменяет файл автоматически.
npx wrangler kv namespace create "$WORKER_NAME-config" --update-config=false
В выводе появится идентификатор нового пространства имён. Скопируйте его, затем замените YOUR_ACCOUNT_ID и YOUR_NAMESPACE_ID в этой полной конфигурации. Имя привязки CONFIG выбирается для вашего кода, а идентификатор указывает на реальный ресурс 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
Найдите пространство имён этой лабораторной работы и сравните его идентификатор с указанным в файле. Другие пространства имён могут присутствовать — не изменяйте их. Эта конфигурация указывает, какую учётную запись и какой ресурс должны использовать следующие команды. Привязка — это ссылка на пространство имён, а не копия его данных.
Чтение версионированных настроек с безопасными значениями по умолчанию
На этом шаге вы настроите обработчик так, чтобы обе корректные версии возвращали рабочие ответы. Если настройки отображения отсутствуют или повреждены, обработчик использует простую светлую тему и не показывает баннер. Благодаря этому необязательная настройка оформления не нарушает работу центра помощи.
Запишите обработчик. Фиксированный объект 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; приложение не сообщает ошибочно, что успешно загрузило значения по умолчанию из хранилища.
Создайте два небольших файла версий. Если каждое требуемое значение хранится в обычном файле, его легко проверить перед записью:
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
Запишите их в отдельные локальные ключи-заготовки, а также добавьте некорректное значение. Эти ключи делают возможные входные данные воспроизводимыми, но не имитируют сетевые задержки 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
Дождитесь сообщения о готовности. Затем сравните два явно выбранных ключа:
curl -i 'http://127.0.0.1:8080/settings?key=config:v1'
curl -i 'http://127.0.0.1:8080/settings?key=config:v2'
Оба запроса должны вернуть HTTP 200 с полем source: "stored". Версия 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"}. Не останавливайте локальный сервер до этапа очистки.
Проверка контролируемой последовательности устаревших чтений
На этом шаге вы протестируете обработчик на предсказуемой последовательности: старое значение, новое значение, снова старое, новое, отсутствующее и некорректное. Это тестовая заготовка — намеренно заданный вход, который делает повторяемой иначе непредсказуемую ситуацию. Она не доказывает, что реальный запрос Cloudflare был устаревшим.
Создайте небольшой тест 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. При невыполненном утверждении команда остановится с ошибкой. Повторное старое значение добавлено намеренно: не добавляйте глобальную для процесса переменную «последняя версия», чтобы скрыть этот результат. Worker может работать в разных экземплярах, поэтому такая переменная не способна установить последнюю версию для всей учётной записи.
Обновление конфигурации должно сохранять работоспособность старых корректных читателей в переходный период. Такой подход подходит для баннера или темы. Но он не делает KV пригодным для немедленного отзыва доступа пользователя. Далее вы проведёте реальный облачный тест; новое значение может появиться уже в первом запросе, и это будет корректным результатом.
Развёртывание и создание первой облачной версии
На этом шаге вы создадите реальное удалённое исходное состояние перед его изменением. В облачном пространстве имён этой лабораторной работы должны находиться только текущая конфигурация и заготовка для некорректного значения.
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, затем скопируйте фактический общедоступный URL:
WORKER_URL="https://YOUR_WORKER.YOUR_SUBDOMAIN.workers.dev"
curl -i "$WORKER_URL/settings"
Ожидайте версию 1, светлую тему (light), баннер Welcome и source: "stored". При необходимости дождитесь готовности имени узла и доступности KV; сетевая ошибка не является ответом конфигурации. Перед заменой версии 1 выполните независимую проверку этого шага. Она проверяет выбранную учётную запись, сохранённое значение, привязку развёрнутого Worker, значения по умолчанию и ответ 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, и не перезаписывайте ключ быстро и многократно, чтобы спровоцировать такой ответ. При необходимости повторяйте HTTP-запрос с интервалом 15 секунд в течение максимум пяти минут. Это ограниченное окно наблюдения для упражнения, а не обещание, что каждое глобальное местоположение сойдётся за пять минут.
Продолжайте, когда конечная точка вернёт {"version":2,"theme":"dark","banner":"New help center","source":"stored"}. Если за это время данные не сойдутся, проверьте учётную запись и привязку и сообщите о неокончательном результате. Независимая проверка требует совпадения реальной сохранённой версии и фактического ответа; локальный файл или тестовая заготовка не подходят.
curl -i "$WORKER_URL/settings?key=config:broken"
curl -i "$WORKER_URL/health"
Повреждённая конфигурация отображения по-прежнему должна использовать контролируемое значение по умолчанию, а health-маршрут — возвращать ok. В Dashboard выберите ту же учётную запись и откройте пространство имён этой лабораторной работы в разделе Storage & databases → Workers KV. Сравните config:current с версией 2 в вашем файле. Это представление только для чтения показывает управляемое значение, но не может доказать, что во всех удалённых местоположениях в данный момент находится именно оно.
Контролируемый тест проверял устойчивость к старым значениям, а реальное обновление — развёртывание и наблюдаемую сходимость на тестовой конечной точке. Не смешивайте эти выводы. Подробнее о модели согласованности KV см. в разделе как работает KV.
Выберите KV Pairs, затем View рядом с config:current. Нажмите Refresh, если список ещё не отражает обновление. В примере показаны версия 2, тема dark и баннер New help center; созданное у вас имя пространства имён будет другим. Не меняйте значение во время этой проверки.

Удаление временных облачных ресурсов
На этом шаге вы удалите оба ресурса, пока Wrangler всё ещё авторизован. Пространство имён может существовать дольше Worker, поэтому удаление приложения само по себе не очищает его данные.
Остановите локальный процесс разработки, запущенный в этом терминале:
kill "$DEV_PID"
Перед удалением проверьте сохранённые ссылки на ресурсы:
cat wrangler.jsonc
Убедитесь, что указаны имя Worker labex-config-... и идентификатор пространства имён CONFIG. Удалите Worker, выбранный этой конфигурацией:
npx wrangler delete
Если появится запрос подтверждения, убедитесь, что показанное имя соответствует этой лабораторной работе, и подтвердите удаление с помощью y. Затем удалите только пространство имён, на которое ссылается CONFIG:
npx wrangler kv namespace delete --binding CONFIG
Перед подтверждением проверьте пространство имён в соответствующем запросе. Не изменяйте wrangler.jsonc, чтобы независимая проверка могла определить ресурсы, которые должны отсутствовать.
npx wrangler kv namespace list
Пространство имён этой лабораторной работы должно отсутствовать, а несвязанные пространства имён должны сохраниться. Обновите списки в Dashboard и убедитесь, что Worker и пространство имён этой лабораторной работы исчезли. Неудачный запрос или истёкшая авторизация не доказывают удаление. Выполните проверку этого шага до выхода из системы, чтобы она могла просмотреть авторизованный список ресурсов.
Завершение авторизации виртуальной машины
На этом шаге вы отключите Wrangler после успешной проверки очистки. Выход из системы завершает сохранённую авторизацию Wrangler для этой виртуальной машины; он не удаляет облачные ресурсы и не завершает обычный сеанс Dashboard в браузере.
npx wrangler logout
npx wrangler whoami --json
Убедитесь, что структурированный результат содержит "loggedIn": false. Эта команда без авторизации может завершиться с ненулевым кодом выхода — в данном случае это ожидаемо. Если отображается только ошибка подключения без явного состояния авторизации, повторите команду после восстановления подключения.
Оставшиеся локальные файлы и локальное состояние KV относятся к этой временной виртуальной машине. Они не связаны с облачными ресурсами, которые вы уже удалили. Теперь лабораторную работу можно завершить.
Итоги
Вы создали читатель конфигурации, который принимает старые и новые корректные настройки, использует безопасные значения по умолчанию для отсутствующих или некорректных данных и поддерживает работу health-маршрута независимо от необязательных данных KV. Вы проверили контролируемую последовательность старых чтений, затем изменили реальный удалённый ключ и наблюдали, как ответ развёрнутого приложения сошёлся с новым значением.
Вы узнали, что метки версий описывают возвращённые данные, а длительность кэширования чтений, истечение срока действия и строгая согласованность — это разные понятия. В конце вы удалили временные ресурсы и вышли из системы. В следующем задании курса нужно будет одновременно правильно настроить привязку пространства имён и безопасно обработать уведомления.



