Aujourd’hui, nous sommes heureux d’annoncer que Workers KV est mis à votre disposition et est prêt à être utilisé pour la production !

Qu'est-ce que Workers KV ?

Workers KV est un magasin largement distribué, voire cohérent, de valeurs clés qui s'étend à la périphérie mondiale de Cloudflare. Il vous permet de stocker des milliards de paires clé-valeur et de les lire avec une latence ultra-faible partout dans le monde. Vous pouvez désormais créer des applications entières avec les performances d'un cache statique CDN.

Comment l'avons-nous construit ?

Workers est une plateforme qui vous permet d'exécuter JavaScript sur la périphérie mondiale de plus de 175 centres de données Cloudflare. Avec seulement quelques lignes de programmation, vous pouvez acheminer des requêtes HTTP, modifier des réponses ou même créer de nouvelles réponses sans serveur d'origine.

// A Worker that handles a single redirect,
// such a humble beginning...
addEventListener("fetch", event => {
  event.respondWith(handleOneRedirect(event.request))
})

async function handleOneRedirect(request) {
  let url = new URL(request.url)
  let device = request.headers.get("CF-Device-Type")
  // If the device is mobile, add a prefix to the hostname.
  // (eg. example.com becomes mobile.example.com)
  if (device === "mobile") {
    url.hostname = "mobile." + url.hostname
    return Response.redirect(url, 302)
  }
  // Otherwise, send request to the original hostname.
  return await fetch(request)
}

Les clients sont rapidement venus nous voir avec des cas d'utilisation qui nécessitent un moyen de stocker des données persistantes. Suivant notre exemple ci-dessus, il est facile de gérer une seule redirection, mais que faire si vous voulez en gérer des milliards ? Vous devez les « coder en dur » dans votre script Workers, les ranger dans moins de 1 Mo et les redéployer à chaque fois que vous souhaitez apporter un changement. Oh la la ! C’est pourquoi nous avons mis sur pied Workers KV.

// A Worker that can handle billions of redirects,
// now that's more like it!
addEventListener("fetch", event => {
  event.respondWith(handleBillionsOfRedirects(event.request))
})

async function handleBillionsOfRedirects(request) {
  let prefix = "/redirect"
  let url = new URL(request.url)
  // Check if the URL is a special redirect.
  // (eg. example.com/redirect/<random-hash>)
  if (url.pathname.startsWith(prefix)) {
    // REDIRECTS is a custom variable that you define,
    // it binds to a Workers KV "namespace." (aka. a storage bucket)
    let redirect = await REDIRECTS.get(url.pathname.replace(prefix, ""))
    if (redirect) {
      url.pathname = redirect
      return Response.redirect(url, 302)
    }
  }
  // Otherwise, send request to the original path.
  return await fetch(request)
}

Avec seulement quelques changements par rapport à notre exemple précédent, nous sommes passés d'une redirection à des milliards. Ce n'est qu'un avant-goût de ce que vous pouvez créer avec Workers KV.

Comment ça marche ?

Les entrepôts de données répartis sont souvent conçus à l'aide du théorème de CAP, qui indique que les systèmes distribués ne peuvent choisir qu'entre 2 des 3 garanties suivantes :

  • Cohérence : mes données sont-elles les mêmes partout ?
  • Disponibilité : mes données sont-elles accessibles tout le temps ?
  • Tolérance aux partitions : mes données sont-elles résistantes face aux pannes régionales ?

Workers KV choisit de garantir la disponibilité et la tolérance de partitionnement. Cette combinaison est connue sous le nom de cohérence éventuelle, ce qui offre à Workers KV deux avantages concurrentiels uniques :

  • Les lectures sont ultra rapides (médiane de 12 ms) car elles sont alimentées par notre technologie de mise en cache.
  • Les données sont disponibles dans plus de 175 centres de données périphériques et résistent aux pannes régionales.

Bien que la cohérence finale soit compromise. Si deux clients écrivent des valeurs différentes sur la même clé en même temps, le dernier client à écrire « gagne » probablement et sa valeur devient globalement cohérente. Cela signifie également que si un client écrit sur une clé et que ce même client lit cette même clé, les valeurs peuvent être incohérentes pendant une courte période.

Pour vous aider à visualiser ce scénario, voici un exemple concret parmi trois amis :

  • Supposons que Matthew, Michelle et Lee planifient leur déjeuner hebdomadaire.
  • Matthew décide qu'ils sortiront manger des sushis.
  • Matthew dit à Michelle ses plans de sushis, Michelle accepte.
  • Lee, ne connaissant pas les plans, dit à Michelle qu'ils vont manger une pizza.

Une heure plus tard, Michelle et Lee attendent à la pizzeria pendant que Matthew est assis seul au restaurant de sushi. Qu'est-ce qui a mal tourné ? Nous pouvons considérer cela comme une cohérence potentielle, car après avoir attendu quelques minutes, Matthew consulte son calendrier actualisé et découvre éventuellement la nouvelle vérité : ils vont plutôt à la pizzeria.

Même si cela peut prendre quelques minutes dans la vie réelle, Workers KV est beaucoup plus rapide. Il peut atteindre une cohérence globale en moins de 60 secondes. De plus, lorsqu'un Worker écrit sur une clé, puis lit immédiatement cette même clé, il peut s'attendre à ce que les valeurs soient cohérentes si les deux opérations proviennent du même emplacement.

Quand dois-je l'utiliser ?

Maintenant que vous comprenez les avantages et les inconvénients de l’utilisation de la cohérence éventuelle, comment déterminez-vous s’il s’agit de la solution de stockage adaptée à votre application ? Dit simplement, si vous voulez une disponibilité mondiale avec des lectures ultra-rapides, Workers KV est fait pour vous.

Toutefois, si votre application écrit fréquemment sur la même clé, un élément supplémentaire est à prendre en compte. Nous l'appelons « la question de Matthew » : Êtes-vous d'accord avec les Matthews du monde d'aller parfois au mauvais restaurant ?

Vous pouvez imaginer des cas d'utilisation (comme notre exemple de Worker de redirection) dans lesquels ceci ne fait aucune grande différence. Toutefois, si vous décidez de suivre le solde du compte bancaire d’un utilisateur, vous ne voudriez pas qu’il existe deux soldes en même temps, car il pourrait acheter quelque chose avec l’argent qu’il a déjà dépensé.

Que puis-je construire avec ?

Voici quelques exemples d'applications construites avec KV :

  • Redirections de masse : gérer des milliards de redirections HTTP.
  • Authentification utilisateur : valider les requêtes des utilisateurs auprès de votre API.
  • Clés de traduction : localiser dynamiquement vos pages Web.
  • Données de configuration : gérer qui peut accéder à votre source.
  • Fonctions d'étape : synchroniser les données d'état entre plusieurs fonctions d'API.
  • Magasin de fichiers périphérique : héberger de grandes quantités de petits fichiers.

Nous avons souligné plusieurs de ces cas d'utilisation dans notre précédent article de blog. Nous avons également quelques revues générales plus détaillées du code, y compris un article de blog récemment publié sur la création d'une liste de tâches en ligne avec Workers KV.

Quoi de neuf depuis bêta ?

Notre demande la plus pressante et la plus courante était de faciliter l’écriture de données sur Workers KV. C'est pourquoi nous proposons trois nouvelles méthodes pour rendre cette expérience encore meilleure :

1. Écriture en masse

Si vous souhaitez importer vos données existantes dans Workers KV, vous ne voudriez pas avoir à envoyer une requête HTTP pour chaque paire clé-valeur. C'est pourquoi nous avons ajouté un point de terminaison de masse à l'API de Cloudflare. Vous pouvez maintenant télécharger jusqu'à 10 000 paires (jusqu'à 100 Mo de données) en une seule requête PUT.

curl "https://api.cloudflare.com/client/v4/accounts/ \
     $ACCOUNT_ID/storage/kv/namespaces/$NAMESPACE_ID/bulk" \
  -X PUT \
  -H "X-Auth-Key: $CLOUDFLARE_AUTH_KEY" \
  -H "X-Auth-Email: $CLOUDFLARE_AUTH_EMAIL" \
  -d '[
    {"key": "built_by",    value: "kyle, alex, charlie, andrew, and brett"},
    {"key": "reviewed_by", value: "joaquin"},
    {"key": "approved_by", value: "steve"}
  ]'

Étudions un exemple d'utilisation : vous souhaitez transférer la traduction de votre site Web vers Workers. Étant donné que vous lisez fréquemment les clés de traduction et que vous ne les mettez à jour que de temps en temps, cette application fonctionne bien avec le modèle de cohérence éventuel de Workers KV.

Dans cet exemple, nous nous sommes connectés à Crowdin, une plateforme populaire de gestion des données de traduction. Ce Worker répond à un point de terminaison de /traduction, télécharge toutes vos clés de traduction et les écrit en bloc dans Workers KV, afin que vous puissiez les lire plus tard sur notre périphérie :

addEventListener("fetch", event => {
  if (event.request.url.pathname === "/translate") {
    event.respondWith(uploadTranslations())
  }
})

async function uploadTranslations() {
  // Ask crowdin for all of our translations.
  var response = await fetch(
    "https://api.crowdin.com/api/project" +
    "/:ci_project_id/download/all.zip?key=:ci_secret_key")
  // If crowdin is responding, parse the response into
  // a single json with all of our translations.
  if (response.ok) {
    var translations = await zipToJson(response)
    return await bulkWrite(translations)
  }
  // Return the errored response from crowdin.
  return response
}

async function bulkWrite(keyValuePairs) {
  return fetch(
    "https://api.cloudflare.com/client/v4/accounts" +
    "/:cf_account_id/storage/kv/namespaces/:cf_namespace_id/bulk",
    {
      method: "PUT",
      headers: {
        "Content-Type": "application/json",
        "X-Auth-Key": ":cf_auth_key",
        "X-Auth-Email": ":cf_email"
      },
      body: JSON.stringify(keyValuePairs)
    }
  )
}

async function zipToJson(response) {
  // ... omitted for brevity ...
  // (eg. https://stuk.github.io/jszip)
  return [
    {key: "hello.EN", value: "Hello World"},
    {key: "hello.ES", value: "Hola Mundo"}
  ]
}

Maintenant, lorsque vous voulez traduire une page, tout ce que vous avez à faire est de lire Workers KV :

async function translate(keys, lang) {
  // You bind your translations namespace to the TRANSLATIONS variable.
  return Promise.all(keys.map(key => TRANSLATIONS.get(key + "." + lang)))
}

2. Clés d’expiration

Par défaut, les paires clé-valeur stockées dans Workers KV durent indéfiniment. Cependant, vous souhaitez parfois que vos données soient supprimées automatiquement après un certain temps. C'est pourquoi nous introduisons les options expiration et expirationTtl pour les opérations d'écriture.

// Key expires 60 seconds from now.
NAMESPACE.put("myKey", "myValue", {expirationTtl: 60})

// Key expires if the UNIX epoch is in the past.
NAMESPACE.put("myKey", "myValue", {expiration: 1247788800})
# You can also set keys to expire from the Cloudflare API.
curl "https://api.cloudflare.com/client/v4/accounts/ \
     $ACCOUNT_ID/storage/kv/namespaces/$NAMESPACE_ID/ \
     values/$KEY?expiration_ttl=$EXPIRATION_IN_SECONDS"
  -X PUT \
  -H "X-Auth-Key: $CLOUDFLARE_AUTH_KEY" \
  -H "X-Auth-Email: $CLOUDFLARE_AUTH_EMAIL" \
  -d "$VALUE"

Supposons que vous souhaitiez bloquer l’accès de votre site Web à des utilisateurs qui ont été signalés comme inappropriés, mais seulement pendant une semaine. Avec une clé d’expiration, vous pouvez définir le délai d’expiration sans avoir à vous soucier de le supprimer ultérieurement.

Dans cet exemple, nous supposons que les utilisateurs et les adresses IP sont identiques. Si votre application dispose d'une authentification, vous pouvez utiliser des jetons d'accès comme identificateur de clé.

addEventListener("fetch", event => {
  var url = new URL(event.request.url)
  // An internal API that blocks a new user IP.
  // (eg. example.com/block/1.2.3.4)
  if (url.pathname.startsWith("/block")) {
    var ip = url.pathname.split("/").pop()
    event.respondWith(blockIp(ip))
  } else {
    // Other requests check if the IP is blocked.
   event.respondWith(handleRequest(event.request))
  }
})

async function blockIp(ip) {
  // Values are allowed to be empty in KV,
  // we don't need to store any extra information anyway.
  await BLOCKED.put(ip, "", {expirationTtl: 60*60*24*7})
  return new Response("ok")
}

async function handleRequest(request) {
  var ip = request.headers.get("CF-Connecting-IP")
  if (ip) {
    var blocked = await BLOCKED.get(ip)
    // If we detect an IP and its blocked, respond with a 403 error.
    if (blocked) {
      return new Response({status: 403, statusText: "You are blocked!"})
    }
  }
  // Otherwise, passthrough the original request.
  return fetch(request)
}

3. Grandes valeurs

Nous avons augmenté notre taille limite des valeurs de 64 Ko à 2 Mo. Ceci est très utile si vous devez stocker des données tampons ou fichiers dans Workers KV.

Considérez cette éventualité : vous voulez permettre à vos utilisateurs de charger leur GIF préféré dans leur profil sans avoir à stocker ces GIF en tant que fichiers binaires dans votre base de données ou à gérer un autre compartiment de stockage dans le cloud.

Workers KV convient parfaitement à ce cas d'utilisation ! Vous pouvez créer un espace de noms Workers KV pour les GIF de vos utilisateurs, qui soit rapide et fiable où que vos clients se trouvent.

Dans cet exemple, les utilisateurs téléchargent un lien vers leur GIF préféré, puis un travailleur le télécharge et le stocke à Workers KV.

addEventListener("fetch", event => {
  var url = event.request.url
  var arg = request.url.split("/").pop()
  // User sends a URI encoded link to the GIF they wish to upload.
  // (eg. example.com/api/upload_gif/<encoded-uri>)
  if (url.pathname.startsWith("/api/upload_gif")) {
    event.respondWith(uploadGif(arg))
    // Profile contains link to view the GIF.
    // (eg. example.com/api/view_gif/<username>)
  } else if (url.pathname.startsWith("/api/view_gif")) {
    event.respondWith(getGif(arg))
  }
})

async function uploadGif(url) {
  // Fetch the GIF from the Internet.
  var gif = await fetch(decodeURIComponent(url))
  var buffer = await gif.arrayBuffer()
  // Upload the GIF as a buffer to Workers KV.
  await GIFS.put(user.name, buffer)
  return gif
}

async function getGif(username) {
  var gif = await GIFS.get(username, "arrayBuffer")
  // If the user has set one, respond with the GIF.
  if (gif) {
    return new Response(gif, {headers: {"Content-Type": "image/gif"}})
  } else {
    return new Response({status: 404, statusText: "User has no GIF!"})
  }
}

Enfin, nous souhaitons remercier tous nos clients bêta. Ce sont vos précieux commentaires qui nous ont amenés à mettre au point ces changements sur Workers KV. Assurez-vous de rester en contact avec nous, nous attendons toujours la prochaine étape et nous aimons recevoir vos messages !

Prix

Nous sommes également prêts à annoncer nos prix GA. Si vous êtes un de nos clients Enterprise, votre prix reste évidemment inchangé.

  • 0,50 $/Go de données stockées, 1 Go inclus
  • 0,50 $/million de lectures, 10 millions inclus
  • 5 $ / million d'opérations d'écriture, de liste et de suppression, 1 million inclus

Au cours de la période bêta, nous avons appris que les clients ne voulaient pas simplement lire les valeurs de notre périphérie, ils voulaient également écrire des valeurs de notre périphérie. Comme la demande pour ces opérations de périphérie, qui sont plus coûteuses, est forte, nous avons commencé à facturer les opérations sans lecture chaque mois.

Limites

Comme mentionné précédemment, nous avons augmenté notre limite de taille de valeur de 64 Ko à 2 Mo. Nous avons également supprimé le nombre maximal de clés par espace de noms. Ce nombre est désormais illimité. Voici nos limites GA :

  • Jusqu'à 20 espaces de noms par compte, chacun avec un nombre illimitée de clés
  • Clés de 512 octets maximum et valeurs de 2 Mo maximum
  • Nombre illimité d'écritures par seconde pour différentes clés
  • Une écriture par seconde pour la même clé
  • Nombre illimité de lectures par seconde et par clé

Essayez-le maintenant !

Désormais ouvert à tous les clients, vous pouvez commencer à utiliser Workers KV dès aujourd’hui à partir de votre tableau de bord Cloudflare sous l'onglet Workers . Vous pouvez également consulter notre documentation mise à jour.

Nous sommes vraiment impatients de voir ce que vous pouvez tous construire avec Workers KV !Sans serveur Workers Workers KV JavaScript Bash