1 Punkte von GN⁺ 2023-09-29 | 1 Kommentare | Auf WhatsApp teilen
  • Deno Queues ist eine serverlose verteilte Queue auf Basis von Deno KV, mit der sich lang laufende Aufgaben asynchron auslagern oder Ausführungen für einen späteren Zeitpunkt planen lassen
  • Entwickler können Nachrichten mit .enqueue() einreihen und sie mit .listenQueue() verarbeiten, ohne separate Queue-Infrastruktur einzurichten oder zu betreiben
  • Lokal wird SQLite verwendet, auf Deno Deploy FoundationDB; Deploy startet zum Zeitpunkt der Nachrichtenverarbeitung bei Bedarf ein V8 isolate und führt es aus
  • Die Zustellung erfolgt per at-least-once delivery, daher kann dieselbe Nachricht bei Fehlern oder Wiederholungen mehrfach verarbeitet werden
  • Es gibt keine separate Queue-Abrechnung, aber enqueue zählt als KV-Schreibvorgang, der Nachrichtenempfang als KV-Schreibvorgang plus Deno-Deploy-Request; daher sollten doppelte Verarbeitung und Kostenstruktur gemeinsam berücksichtigt werden

Wofür Deno Queues zuständig ist

  • Deno Queues ist eine Queue-Funktion auf Basis von Deno KV, mit der Anwendungen einen Teil ihrer Verarbeitung als asynchrone Aufgabe auslagern oder Aufgaben für eine spätere Ausführung planen können
  • Deno zielt darauf ab, wiederkehrende Einrichtung und Infrastrukturkonfiguration in der Cloud-Softwareentwicklung zu reduzieren und Entwicklern grundlegende Bausteine bereitzustellen, mit denen sie schneller entwerfen, verbessern und veröffentlichen können
  • Deno KV ist eine Persistenzoption auf Basis von FoundationDB und befindet sich derzeit in der offenen Beta
  • Queues stellt auf Deno KV skalierbares Messaging und Verwaltung für Hintergrundverarbeitung bereit

Basis-API und Ausführungsmodell

  • Die Kern-API von Deno Queues besteht aus zwei Teilen
    • .enqueue(): legt eine neue Nachricht in die Queue und sorgt dafür, dass sie sofort oder zu einem späteren Zeitpunkt zugestellt wird
    • .listenQueue(): dient als Handler, der neue Nachrichten aus der Queue empfängt und verarbeitet
  • Der grundlegende Ablauf besteht darin, KV mit Deno.openKv() zu öffnen, in listenQueue eine Slack-Nachricht zu senden und bei enqueue mit { delay: 60000 } festzulegen, dass die Verarbeitung nach 60 Sekunden erfolgen soll
  • Je nach Ausführungsumgebung wird ein anderer Speicher verwendet
    • Bei lokaler Ausführung SQLite
    • Bei Ausführung auf Deno Deploy FoundationDB
  • Auf Deno Deploy startet zur möglichen Verarbeitungszeit einer Nachricht bei Bedarf ein V8 isolate und dispatcht die Nachricht
  • Der Anwendungscode muss lediglich im listenQueue-Handler neue Nachrichten empfangen; die übrige Ausführungsverwaltung übernimmt Deno Deploy

Zustellgarantien und Wiederholungen

  • Deno Queues garantiert at-least-once delivery
  • Die meisten per enqueue eingereihten Nachrichten rufen den listenQueue-Handler nur einmal auf, in manchen Fehlersituationen kann derselbe Handler zur Einhaltung der Zustellgarantie jedoch mehrfach aufgerufen werden
  • Daher muss die Anwendung doppelte Nachrichtenverarbeitung sicher handhaben
  • Wenn der listenQueue-Handler eine Exception wirft, versucht die Runtime den Aufruf automatisch erneut
  • Wiederholungen laufen weiter, bis der Aufruf erfolgreich ist oder die maximale Anzahl an Wiederholungen erreicht ist; der aktuelle Standardwert liegt bei 5
  • Nachrichten, die die maximale Anzahl an Wiederholungen erreicht haben, werden gelöscht

Nutzung zusammen mit KV atomic transactions

  • Deno Queues kann mit KV atomic transactions kombiniert werden
  • Wird eine Nachricht als Teil einer KV-Transaktion eingereiht, gelangt sie nur dann in die Queue, wenn die gesamte Transaktion erfolgreich ist
  • Ein Beispielablauf prüft und aktualisiert die Kontostände von Bob und Liz und reiht anschließend Benachrichtigungen an beide ein
    • Der Zustand wird mit check(bob, liz) geprüft
    • Bobs Kontostand wird verringert und Liz’ Kontostand erhöht
    • Mit enqueue werden jeweils Benachrichtigungen für Liz und Bob hinzugefügt
    • Erst wenn commit() erfolgreich ist, werden alle Änderungen und das enqueue wirksam
  • Auch innerhalb des listenQueue-Handlers kann der Zustand in Deno KV aktualisiert werden
  • Um Updates pro Nachricht nur einmal auszuführen, lässt sich ein Ablauf zur Vermeidung doppelter Verarbeitung aufbauen, bei dem ein Nonce gespeichert und gelöscht wird
    • Beim enqueue wird ein Nonce mitgespeichert
    • Wenn bei der Verarbeitung kein Nonce vorhanden ist, wird sofort zurückgegeben
    • Bei erfolgreicher Verarbeitung wird der Nonce gelöscht und Zähler- sowie Kontostandszustand werden gemeinsam aktualisiert

Geeignete Einsatzfälle für Queues

  • Queues sind nützlich für die Skalierung von Anwendungen, weil sie Servern ermöglichen, asynchrone Verarbeitung auszulagern und zukünftige Aufgaben zu planen
  • Geplante E-Mail-Benachrichtigungen

    • Wenn eine vom Nutzer gestartete Aufgabe lange dauert oder nicht auf eine Abschlussantwort gewartet werden muss, kann sie in eine Queue ausgelagert werden, um die Reaktionsfähigkeit des Servers oder der App zu erhalten
    • Im Beispiel wird eine welcome_email-Nachricht sofort eingereiht, während eine survey_email-Nachricht mit { delay: 259200000 } verzögert ausgeführt wird
  • Zuverlässige Webhook-Verarbeitung

    • Wenn Webhook-Requests nicht direkt verarbeitet, sondern in eine Queue gelegt werden, lassen sich Antwort und eigentliche Verarbeitung trennen
    • Mit Oak und Queues wird bei einem POST-Request an /webhook der Request-Body in die Queue gelegt und HTTP 200 zurückgegeben
    • Die eigentliche Webhook-Verarbeitung erfolgt asynchron in listenQueue über processWebHook
  • Slack- und Discord-Bots

    • Queues kann auch zum Erstellen von Discord- oder Slack-Bots verwendet werden
    • Das Beispiel einer Slack reminder app zeigt, wie sich mit Deno Queues eine einfache Slack-Reminder-App bauen lässt
    • Das Beispiel eines Discord bot erstellt mit Deno Queues ein Giveaway und ermöglicht Nutzern die Teilnahme mit einem Klick
    • Weitere Beispiele für die Nutzung von Queues finden sich auf docs.deno.com

Abrechnungsmodell

  • Für Deno Queues gibt es keinen separaten dedizierten Abrechnungsposten
  • Die Kosten werden auf Basis von Deno-KV-Operationen und Deno-Deploy-Requests berechnet
    • Nachricht enqueue: Jeder enqueue-Vorgang entspricht einem KV-Schreibvorgang
    • Nachrichtenempfang: Für jede empfangene Nachricht fallen ein KV-Schreibvorgang und eine Request-Gebühr an
  • Abgerechnet werden nur die tatsächlich genutzten Operationen

Dokumentation und Roadmap

1 Kommentare

 
GN⁺ 2023-09-29
Meinungen auf Hacker News
  • Habe gerade das Innenleben der lokalen SQLite-Version untersucht und meine Notizen zusammengefasst: https://til.simonwillison.net/deno/deno-kv#user-content-deno...
    Der interessanteste Teil ist vermutlich das Schema, das für diese Funktion verwendet wird:
    CREATE TABLE queue ( ts integer not null, id text not null, data blob not null, backoff_schedule text not null, keys_if_undelivered blob not null, primary key (ts, id) );
    CREATE TABLE queue_running( deadline integer not null, id text not null, data blob not null, backoff_schedule text not null, keys_if_undelivered blob not null, primary key (deadline, id) );
    CREATE INDEX kv_expiration_ms_idx on kv (expiration_ms);

    • Zwei Tabellen mit nahezu denselben Feldern anzulegen und Zeilen zwischen ihnen zu verschieben, scheint mir normalerweise keine gute Praxis zu sein.
      Sind Indizes nicht genau für so etwas gedacht?
    • Aus Sicht des „Open-Source-Geschäftsmodells“ ist besonders interessant, dass Deno in Kernfunktionen des Frameworks Features einbaut, die eigentlich nur die eigene SaaS-Plattform als Upgrade auf globaler Skalierung gut bereitstellen kann.
      Das ist ziemlich neu, aber auch etwas beunruhigend. Natürlich muss kommerzialisiert werden, aber es hätte nicht vollständig geschlossen sein müssen; für die Erweiterungsschicht hätte man wohl auch andere Lizenzen wie BSL oder PolyForm verwenden können.
  • Ich finde, die Richtung, die Deno hier einschlägt, ist ziemlich zukunftsorientiert
    Ich benutze Go dieses Jahr zum ersten Mal, und Teile der Sprache ähneln C sehr. Neben den Unterschieden zu C, etwa dass man dank Garbage Collection Werte zurückgeben kann, die wie auf dem Stack allozierte Pointer aus Funktionen aussehen, sind auch eingebaute Features wie map[string]string ganz natürlich integriert. Wenn man nach 2023 eine neue Sprache entwickelt, fühlt es sich fast absurd an, keine eingebaute Syntax für Map-Typen zu haben
    Deshalb wirkt es logisch, dass ein Ökosystem rund um Webserver Bibliotheken für primitive Funktionen bekommt, die wir häufig nutzen – und vielleicht irgendwann sogar Sprachsyntax dafür. Ich kann mich an kein jüngeres Projekt mit verteilten Webservern erinnern, das nicht einmal einen Key-Value-Store für Caches, einen Locking-Mechanismus oder auch nur eine temporäre Queue hatte. Ein verteiltes System ohne Key-Value-Store ähnelt vage einer Programmiersprache ohne Map-Typ und ist wirklich eine Art Schweizer Taschenmesser unter den Technologien
    Das potenzielle Problem ist, dass Deno diesen Weg allein geht. Es ist schwer abzuschätzen, ob die neuen Funktionen, die Deno derzeit hinzufügt, auch auf konkurrierenden Plattformen verfügbar sein werden. Und selbst wenn die API als Open Source veröffentlicht wird: Sie liegt im Deno-Namespace, daher weiß ich nicht, ob es einfach funktioniert, wenn man auf AWS statt FoundationDB lieber Redis verwenden möchte
    Deshalb habe ich das Gefühl, Deno lieber meiden zu wollen. Die Syntax und die exponierten Funktionen sind wirklich attraktiv, aber ich mache mir Sorgen, dass ich nach einer Abhängigkeit davon den Code umfangreich umbauen müsste, um ihn auf mehreren Cloud-Infrastrukturen deploybar zu machen. Viele Kunden verlangen genau das. Wenn man zum Beispiel irgendwann an Oracle oder Salesforce verkaufen möchte und diese On-Premises-Ausführung verlangen, bleibt es an mir hängen, herauszufinden, wie ich den dort verfügbaren Key-Value-Store an die Funktionalität des Deno-Pakets anpasse
    Das ist ein zweischneidiges Schwert. Deno könnte damit Erfolg haben, diese Vision einem breiteren Publikum aufzudrängen, aber vorerst werde ich es wohl vorsichtig betrachten

    • In diesem Fall ist auch KV Connect, das Protokoll für Remote-Verbindungen, dokumentiert: https://github.com/denoland/deno/tree/main/ext/kv#kv-connect
      Ich habe kv-connect-kit, eine Implementierung dieses Protokolls in reinem TypeScript, kurz ausprobiert; es stellt eine KV-Client-API in jeder JavaScript-Runtime bereit. Das schließt auch Cloudflare Workers ein, wo es keinerlei Deno-Namespace gibt
      GitHub: https://github.com/skymethod/kv-connect-kit / npm: https://www.npmjs.com/package/kv-connect-kit / deno/x: https://deno.land/x/kv_connect_kit / Demo: https://keyspace.deno.dev/
      Das Protokoll scheint wie beschrieben zu funktionieren, und auch andere Backends zu schreiben wirkt ziemlich unkompliziert
    • Ich stimme nicht zu. Eine Sprach-Runtime mit einem bestimmten KV-Interface zu koppeln, das an einen bestimmten Hosting-Dienst gebunden ist, ist das Gegenteil von zukunftsorientiert
      Die Tech-Branche hat erhebliche Fortschritte dabei gemacht, sich von Vendor-Lock-in-Stacks zu lösen; das hier erinnert eher wieder an diese Zeiten
      Ich sehe keinen praktischen Unterschied zwischen Deno.KV als Teil der Runtime und einem import KV am Anfang der Datei. Letzteres kann man von Deno oder von wo auch immer beziehen
      Und wie wahrscheinlich ist es, dass das Deno-Team Bindings für andere Sprachen als die eigene bereitstellt? Würde man es zukunftsorientiert nennen, wenn Google anfangen würde, Google-Cloud-spezifische Primitive nativ in Go einzubauen?
    • Ich mag Deno, bin aber aus demselben Grund bei der Einführung vorsichtig. Damit Deno gewinnt, müssten meiner Meinung nach auch Hosting-Konkurrenten vollständig unterstützt werden
      Gleichzeitig müsste Self-Hosting genauso gut oder besser sein als bei den Branchenführern. Ein anderer Weg wäre, Self-Hosting aufzugeben und eine Multicloud-Lösung anzubieten, die auf Adaptern zu Hosting-Partnern basiert und eine kleine Marge aufschlägt. Dann müsste Deno nicht direkt als Hosting-Anbieter konkurrieren
    • Deno versucht damit lediglich, mit anderen JS/TS-SaaS zu konkurrieren. Vercel/NextJS hat ebenfalls einen KV-Dienst, Cloudflare auch, und in anderen Fällen gibt es Firebase
      Das wirkt für mich weniger zukunftsorientiert als vielmehr wie die Monetarisierung von Deno als SaaS. Es ist das, was heutzutage in der JavaScript-Welt alle mit „Cloud“-Produkten machen: ein Framework drankleben und AWS weiterverkaufen. Deshalb hängt auch ein Preisschild daran
      Wenn Denos KV von FoundationDB abhängt, scheint es auch unwahrscheinlich, dass aktiv Adapter für andere Datenbanken gebaut werden. Datenbanktechnologie zu wechseln ist immer eine große Sache, weil jede ihren eigenen Einsatzzweck und ihre eigenen Performance-Eigenschaften hat
    • GC allowing one to return what look like stack allocated pointers from functions liegt meiner Meinung nach weniger an der Garbage Collection als vielmehr an Escape Analysis und SSA
  • Die Argumentation, dass „man sich durch Boilerplate-Code und komplizierte Konfiguration wühlen muss, um Public-Cloud-Infrastruktur zu nutzen, was viel Zeit und Energie von Entwicklern frisst“, überzeugt mich nicht wirklich.
    Am Ende bauen wir Infrastruktur, die stabil sein muss. 30 Minuten dafür aufzuwenden, eine bewährte Technik wie eine SQS-Queue einzurichten, wirkt nicht schlechter, als einer Spielzeug-Queue, die Deno „auf SQLite / FoundationDB“ gebaut hat, vollständig zu vertrauen. Ist der Einrichtungsaufwand von 30 Minuten und die verbesserte „Developer Experience“ dieses Risiko wirklich wert? Wie oft richtet man überhaupt eine Queue ein?

    • Ich denke, das hängt davon ab, was für eine Person man ist.
      Als Hobbyprogrammierer nutze ich große Anbieter wie AWS oder Google nicht besonders, weil sie ziemlich komplex wirken. Wenn man damit vertraut ist, ist es vielleicht gar nicht so schlimm.
      Außerdem möchte ich jedes Projekt als eigenes Repository auf GitHub haben. Idealerweise sollte eine Web-App für jede andere Person einfach unter einer eigenen Domain als unabhängige Web-App startbar sein. Denn ich möchte nicht auch noch für die Daten anderer Leute verantwortlich sein.
      Das ähnelt in gewisser Weise dem Sandstorm-Use-Case. Eine Installationsanleitung wie „probier es lokal aus, und wenn du es wirklich nutzen willst, erstelle ein Deno-Deploy-Konto“ klingt ziemlich vernünftig.
    • Viele Leute haben keine Erfahrung mit AWS oder SQS, und selbst wer Erfahrung hat, muss sich weiterhin darum kümmern. Deno verfolgt einen Batteries-included-Ansatz, damit Entwickler etwas nehmen und sofort verwenden können.
      Wenn jemand eine gleichwertige Bibliothek für die Cloud anbietet, wird es sicher Leute geben, die sie übernehmen. Im Fall von Deno scheint man gemeinsame Probleme der Entwickler im eigenen Ökosystem zu identifizieren und mit gebündelten, brauchbaren Lösungen anzugehen.
    • Zugespitzt gesagt: Ich glaube, man sollte Queues inzwischen lieber nicht mehr direkt verwenden. Das ist ähnlich wie rohes HTTP/2 zu nutzen oder Threads selbst zu verwalten.
      Wenn man eine Workflow-Engine nutzt, die Queues abstrahiert, kann man „einfach Code schreiben“, ohne Jobs, Zeitpläne, Status und Retries selbst zu verwalten.
      https://www.inngest.com/blog/how-durable-workflow-engines-wo...
      Allerdings bin ich als Autor einer Workflow-Engine voreingenommen.
    • Nach dieser Logik ist im Grunde alles Neue ein „Spielzeugprojekt“, und weil nur AWS bewährt ist, dürfte niemand neue Produkte veröffentlichen.
    • Woher weißt du, dass das eine Spielzeug-Queue ist?
  • Aus früherer Erfahrung mit „At-least-once-Delivery-Semantik“ und „User-Code“: Nutzer verstehen At-least-once Delivery nicht richtig.
    In dem Moment, in dem eine Nachricht erneut zugestellt wird, hagelt es Nutzerbeschwerden und Ausfälle.
    Entweder man vollbringt das beinahe Unmögliche und erfindet Exactly-once-Delivery-Semantik, oder man sollte meiner Meinung nach immer 0,1 % aller Nachrichten erneut zustellen, damit Nutzer sich nicht darauf verlassen können, dass sie nur einmal zugestellt werden.

    • Das frühe SQS war praktisch so. Ich weiß nicht, ob das Absicht war, aber dadurch habe ich schnell gelernt, dass man erneute Zustellungen ernst nehmen muss.
    • Wenn Nachrichten idempotent sein müssen, empfehle ich dringend, auf Senderseite Integrationstests zu erstellen, die das sicherstellen.
  • Ich nutze derzeit Deno Deploy, und für ein Solo-Projekt ist die Performance hervorragend und es ist wirklich einfach.
    Aus der Perspektive von jemandem, der in großen Teams AWS-Entwicklung gemacht hat, begrüße ich den Trend, Komplexität loszuwerden und Dinge einfach starten zu können, ohne über Konfiguration nachzudenken. DNS war ebenfalls sehr einfach, und die automatischen SSL-Zertifikate waren wirklich gut. Ich habe überhaupt nichts daran auszusetzen, worauf dieses Produkt abzielt, und freue mich auf die Roadmap.

  • Wenn ich nichts übersehen habe, sieht es so aus, als gäbe es pro Deno.openKv()-Instanz nur eine Queue.
    In der lokalen Version könnte man vermutlich mehrere Queues erstellen, indem man jedes Mal Deno.openKv("db-2.db") mit einem anderen SQLite-Dateipfad aufruft, aber für eine ziemlich übliche Anforderung wirkt das nach viel Overhead.
    Vielleicht ist das auch Denos Architekturstil. Erwartet man bei komplexen Apps mit Deno eine Microservice-artige Struktur, bei der mehrere Skripte zusammenarbeiten und jedes seinen eigenen KV-Store und seine eigene Queue hat?

    • Stimmt. Derzeit gibt es nur eine einzelne Queue.
      Einer der Kernentwickler hat das auf Discord bestätigt: https://discord.com/channels/684898665143206084/115671428253...
      Zitat: „Ja, richtig. Derzeit unterstützen wir nur eine einzelne Queue. Man kann aber mehrere Nachrichtentypen über diese eine Queue multiplexen.“
  • Wie sieht es mit den Preisen aus?
    Ich dachte, Deno sei eine Art Node.js-Alternative; übersehe ich da etwas? Kann man das lokal oder self-hosted kostenlos nutzen?

    • Das funktioniert genauso wie der Rest von Deno KV. Die Open-Source-Version nutzt SQLite, aber wenn man in Denos Cloud-Produkt deployed, wird stattdessen das proprietäre FoundationDB verwendet.
      Da „Queues are built on Deno KV“ gilt: lokal wird SQLite genutzt, und auf Deno Deploy wird FoundationDB für maximale Verfügbarkeit und Durchsatz verwendet.
      Zu diesem Muster habe ich hier etwas mehr aufgeschrieben: https://til.simonwillison.net/deno/deno-kv
    • Dem Artikel zufolge nutzt man lokal während der Entwicklung SQLite, und wenn man auf Deno deployed, wird es Teil der vom Cloud-Anbieter bereitgestellten Funktionen.
  • Falls die Entwickler mitlesen: Bei der derzeit hervorragenden Nutzbarkeit von KV sieht es für mich so aus, als könnte Zugriffskontrolle später schmerzhaft werden.
    Ich frage mich, ob die Lösung einfach lautet: „Implementiere es selbst im Userspace und mach keine Bugs“, oder ob dafür etwas geplant ist.

    • Meinst du Zugriffskontrolle auf Nutzerebene, zum Beispiel ACLs für authentifizierte Nutzer, oder etwas anderes? Wenn du ausführlichere Gedanken dazu teilst, behalten wir diese Sorge weiter im Hinterkopf.
  • Die jüngste Jupyter-Unterstützung und diese Queue-Funktion machen mich neugierig. Das sind wirklich tolle Dinge.
    Aber ich glaube nicht, dass ich einen Server mit Deno schreiben und auf meinem eigenen Server in Produktion betreiben würde. Serverless mag ich nicht.
    Die Geschäftsmodelle von Deno und Bun sind um Serverless herum gebaut, also um Deno Deploy. Und Deno Deploy ist wiederum eine andere Runtime.
    Bei KV und Queues wird das deutlich. Lokal oder self-hosted bekommt man eine gerade so funktionierende Version, die auf anderer Technologie aufsetzt.
    Das bedeutet: Man self-hostet eine andere Version von Deno als die, die die meisten Deno-Kunden verwenden.
    Wie viele Leute nutzen Deno, um Server auf eigener Hardware zu betreiben? Wenn diese Zahl klein ist, würde ich lieber Node verwenden.

  • Deno ist wirklich spannend. Ein tragfähiges Geschäftsmodell, gute Usability und kein unnötiger Konfigurationsaufwand sind großartig

    • Ich habe versucht, mir Hoffnungen für Deno zu machen, bin mir aber nicht sicher. Ich sehe nicht, was daran anders ist als das, was wir in der Vergangenheit schon unzählige Male gemacht haben.
      Deno selbst ist im Kern Node.js plus automatische TypeScript-Kompilierung. Früher hätte man dafür nur eine zusätzliche Konfigurationsdatei gebraucht. Als das Projekt herauskam, wurde stark damit geworben, sich vom chaotischen NPM-Ökosystem zu lösen, aber am Ende musste man doch NPM-Unterstützung hinzufügen, und auch dieses Unterscheidungsmerkmal verschwand.
      Deno Deploy ist wie die Dutzenden bereits existierenden Lambda-artigen Dienste. Der KV-Store und nun die Queue sind Redis, LevelDB, RocksDB, Dynamo, SQS oder unzähligen ähnlichen Optionen gleichwertig. Ich habe noch keine Funktion gesehen, die Deno wirklich vom Rest abhebt.
      Wenn man es bevorzugt, dass ein Unternehmen Sprachruntime, Compute-Hosting und Datenspeicher komplett übernimmt, kann das attraktiv sein. Ich persönlich würde das aber lieber vermeiden, besonders wenn dieses Unternehmen in dem Bereich keine Erfolgsbilanz vorzuweisen hat.
    • Warum glaubst du, dass Deno ein tragfähiges Geschäftsmodell hat?