KV Store
KV Store — key-value storage, scoped по окружению проекта. В текущем
публичном runtime contract KV доступен внутри ONREZA Functions
через ctx.kv. Публичного HTTP API для KV нет.
Используйте KV для сессий, короткого cache, rate limiting counters и небольших динамических настроек, которым не нужна реляционная модель.
Где доступен KV
Заголовок раздела «Где доступен KV»| Surface | Статус |
|---|---|
| ONREZA Functions | Поддерживается через ctx.kv |
| Dashboard | Просмотр и ручное редактирование через KV Browser |
| Compute Apps | Пакетный SDK для Compute не является текущим стабильным публичным contract |
| Public HTTP API | Нет |
Runtime API
Заголовок раздела «Runtime API»ctx.kv поддерживает шесть операций:
| Метод | Описание |
|---|---|
get(key) |
Возвращает `Uint8Array |
set(key, value, options?) |
Записывает string или Uint8Array |
put(key, value, options?) |
Алиас set |
incr(key, amountOrOptions?, options?) |
Атомарно увеличивает integer counter |
delete(key) |
Удаляет ключ |
list(options?) |
Возвращает { keys, cursor } по prefix/limit/cursor |
export default { async fetch(request, ctx) { const decoder = new TextDecoder();
await ctx.kv.set("hello", "world", { ttl: 300 });
const raw = await ctx.kv.get("hello"); const value = raw ? decoder.decode(raw) : null;
return Response.json({ value }); },};ttl задаётся в секундах и должен быть неотрицательным integer:
ttlне указан — значение не истекает автоматически;ttl: 0— значение не истекает автоматически;ttl: 60— значение истечёт примерно через 60 секунд.
await ctx.kv.set("session:123", JSON.stringify({ userId: "u_123" }), { ttl: 86400,});Binary values
Заголовок раздела «Binary values»ctx.kv принимает string и Uint8Array. При чтении всегда возвращается
Uint8Array | null, поэтому JSON и text декодируйте явно:
const encoder = new TextEncoder();const decoder = new TextDecoder();
await ctx.kv.set("profile:42", JSON.stringify({ name: "Alice" }));await ctx.kv.put("avatar:42", encoder.encode("binary-safe value"));
const raw = await ctx.kv.get("profile:42");const profile = raw ? JSON.parse(decoder.decode(raw)) : null;List pagination
Заголовок раздела «List pagination»let cursor: string | undefined;
do { const page = await ctx.kv.list({ prefix: "cache:user:", limit: 100, cursor }); for (const key of page.keys) { await ctx.kv.delete(key); } cursor = page.cursor ?? undefined;} while (cursor);Runtime list-запросы ограничены backend cap 1000 ключей за запрос. Dashboard KV Browser показывает до 200 записей на страницу, чтобы UI оставался быстрым.
Управление через UI
Заголовок раздела «Управление через UI»- Откройте проект и выберите окружение.
- Перейдите во вкладку KV Store.
- Используйте поиск по prefix, чтобы найти группу ключей.
- Добавляйте, редактируйте или удаляйте entries вручную.
UI полезен для диагностики и точечных правок. Для массовых операций используйте
runtime code с пагинацией по ctx.kv.list().
Тарифные лимиты
Заголовок раздела «Тарифные лимиты»| Метрика | Hobby | Pro | Enterprise |
|---|---|---|---|
| Storage/workspace | 16 MB | 10 GB | 100 GB |
KV usage покрывается общим usage credit. Ставки Pro: storage — 300 ₽/ГБ-мес, reads — 5 ₽ за 1M read units, writes — 250 ₽ за 1M write units. Подробнее: Лимиты и квоты.
Технические лимиты
Заголовок раздела «Технические лимиты»| Параметр | Лимит |
|---|---|
| Размер ключа | 512 bytes |
| Размер значения | 1 MiB |
| Runtime list | до 1000 ключей за запрос |
| Dashboard list | до 200 записей на страницу |
| TTL | неотрицательное число секунд; 0 = без автоистечения |
Практические примеры
Заголовок раздела «Практические примеры»Session lookup
Заголовок раздела «Session lookup»const decoder = new TextDecoder();
export default { async request(request, ctx) { const sessionId = request.headers.get("cookie")?.match(/session=([^;]+)/)?.[1]; if (!sessionId) { return Response.redirect(new URL("/login", request.url), 307); }
const raw = await ctx.kv.get(`session:${sessionId}`); if (!raw) { return Response.redirect(new URL("/login", request.url), 307); }
ctx.locals.user = JSON.parse(decoder.decode(raw)); return request; },};Cache-aside
Заголовок раздела «Cache-aside»const decoder = new TextDecoder();
async function getCachedJson<T>( ctx: { kv: { get(key: string): Promise<Uint8Array | null>; set(key: string, value: string, options?: { ttl?: number }): Promise<void>; }; }, key: string, fetcher: () => Promise<T>, ttl = 300,): Promise<T> { const cached = await ctx.kv.get(key); if (cached) { return JSON.parse(decoder.decode(cached)) as T; }
const value = await fetcher(); await ctx.kv.set(key, JSON.stringify(value), { ttl }); return value;}Rate limiting
Заголовок раздела «Rate limiting»async function rateLimit(ctx, identifier: string, limit = 60, windowSeconds = 60) { const key = `rate:${identifier}`; const count = await ctx.kv.incr(key, { ttl: windowSeconds });
return { allowed: count <= limit, remaining: Math.max(0, limit - count), resetIn: windowSeconds, };}incr атомарен в пределах региона. Для глобальных лимитов между регионами
ожидайте небольшое окно eventual consistency.
Best practices
Заголовок раздела «Best practices»- Используйте префиксы:
session:,cache:,rate:,config:. - Храните JSON как string и декодируйте явно через
TextDecoder. - Задавайте TTL для временных ключей.
- Не храните большие файлы: value cap — 1 MiB.
- Для строгих транзакций и сложных запросов используйте Managed PostgreSQL.
Troubleshooting
Заголовок раздела «Troubleshooting»ctx.kv get failed
Заголовок раздела «ctx.kv get failed»Проверьте, что функция выполняется в ONREZA Functions и окружение имеет KV
binding. Для локального кода вне Functions ctx.kv недоступен.
ctx.kv ttl must be a non-negative integer number of seconds
Заголовок раздела «ctx.kv ttl must be a non-negative integer number of seconds»Передан отрицательный, дробный или нечисловой TTL. Используйте integer seconds
или не передавайте ttl.
ctx.kv list limit must be a positive integer
Заголовок раздела «ctx.kv list limit must be a positive integer»limit должен быть positive integer. Используйте небольшие страницы и cursor.