Zpět na hlavní stránku

Novinkový souhrn #002

Přidal Filip Kalousek dne

Úvod

Jsou to dnes čtyři dny, co Ryan Dahl představil Deno KV -> je nestabilní (1. 5. 2023) key-value databáze, kterou lze použít pro ukládání a načítání dat. Data jsou ukládána jako páry klíč-hodnota, kde klíč je objekt typu Deno.Klíče jsou jedinečné uvnitř databáze a poslední hodnota nastavená pro daný klíč se vrátí při čtení klíče. Klíče lze z databáze smazat, v takovém případě již nebudou vráceny při čtení klíčů.

Hodnoty mohou být jakákoliv strukturovaná serializovatelná hodnota v JavaScriptu (objekty, pole, řetězce, čísla atd.). Speciální hodnota Deno.KvU64 může být použita k ukládání 64bitových neoznačených celých čísel v databázi. Tato speciální hodnota nemůže být vnořena do jiných objektů nebo polí.

Klíče jsou verzovány při zápisu přidělením "verzovací známky" klíči, která se postupně zvyšuje. Verzovací známka reprezentuje verzi daného klíče-hodnoty v databázi v určitém čase a lze ji použít k provádění transakčních operací na databázi bez potřeby uzamčení. To je umožněno atomickými operacemi, které mohou mít podmínky, které zajistí, že operace uspěje pouze v případě, že verzovací známka klíče-hodnoty odpovídá očekávané verzi.

Klíče mají maximální délku 2048 bytů po serializaci. Hodnoty mají maximální délku 64 KiB po serializaci. Serializace klíčů a hodnot je trochu neprůhledná, ale obvykle lze předpokládat, že serializace jakékoli hodnoty má stejnou délku jako výsledný řetězec z JSON serializace téže hodnoty. Pokud jsou tyto limity překročeny, dojde k vyhození výjimky.

Metody

Pro začátek bych asi vypíchnul ty hlavní a těmi jsou:

Set

const db = await Deno.openKv();
const pageViews = await db.get(["pageViews"]); // 121
await db.set(["pageViews"], pageViews.value + 1); // 122


// Nebo

await db.atomic()
    .mutate({type: "sum", ["pageViews"], value: new Deno.KvU64(1n)})
    .commit(); // 123

Get

Získá hodnotu a razítko verze pro daný klíč z databáze. Pokud pro daný klíč neexistuje žádná hodnota, vrácená položka bude mít nulovou hodnotu a razítko verze.

const db = await Deno.openKv();
const result = await db.get(["pageViews"]);
result.key; // ["pageViews"]
result.value; // 123
result.versionstamp; // "00000000000000010000"

Get Many

Stejný jako metoda Get akorát celé zabalené v poli.

const db = await Deno.openKv();
const result = await db.getMany([["pageViews"], ["statistics"]]);
result[0].key; // ["pageViews"]
result[0].value; // 123
result[1].key; // ["statistics"]
result[1].value; // [{ip: "77.75.79.222", visits: 45}, {ip: "34.120.54.55", visits: 78}]

Delete

Odstraní hodnotu zadaného klíče z databáze. Pokud pro daný klíč neexistuje žádná hodnota, je tato operace neúspěšná.

const db = await Deno.openKv();
await db.delete(["pageViews"]);

Závěr

Deno KV mě celkem nadchlo, teším se až budu tvořit na produkci na nějakém projektu, celý tenhle článek měl v ukázkách pageViews, zkusil jsem si tedy implementovat na vlastní příklad, kde dostupné metody jsou set a reset, je to celé zaobalené ještě jako http client.

import { serve } from "https://deno.land/std@0.177.0/http/server.ts";

const db = await Deno.openKv();

interface RouteHandler {
  [key: string]: (url: URL) => Promise<{ body: string; status?: number }>;
}

const template = `\n\rtry /reset or /set?value=[your value]`

const routes: RouteHandler = {
  "/": async () => {
    const pageViews = await db.get(["pageViews"]);
    await db.atomic()
        .mutate({type: "sum", ["pageViews"], value: new Deno.KvU64(1n)})
        .commit();
    return { body: `Total views: ${pageViews.value + 1}` + template };
  },
  "/set": async (url: URL) => {
    const value = url.searchParams.get("value");
    if (value === null || /^\d+$/.test(value)) {
      await db.set(["pageViews"], value ? parseInt(value) : 0);
      return { body: `${value}` };
    }
    return { body: "NOT OK", status: 400 };
  },
  "/reset": async () => {
    await db.set(["pageViews"], 0);
    return { body: "OK" };
  },
  "*": async () => {
    return { body: "I'm a teapot", status: 418 };
  },
};

serve(async (req: Request) => {
  const url = new URL(req.url);
  const handler = routes[url.pathname] ?? routes["*"];
  const res = await handler(url);
  return new Response(res.body, { status: res.status ?? 200 });
});