Cloudflare
Durable Objects
Custos
Produção
Limites

Langlebige Objekte in der Produktion: Wie die Rechnung aussehen wird und welche Grenzen überraschen

Detaillierte Analyse der tatsächlichen Kosten langlebiger Objekte in der Produktion – wie man GB-Sekunden berechnet, wo der serielle Durchsatz zum Engpass wird und welche Grenzen bei der Skalierung überraschend sind.

Langlebige Objekte in der Produktion: Wie die Rechnung aussehen wird und welche Grenzen überraschen

Die Preisgestaltung für langlebige Objekte scheint einfach zu sein, bis Sie den tatsächlichen ersten Monat berechnen. Drei interagierende Komponenten, ein kostenloses Kontingent, das großzügig erscheint, bis Sie den korrekten Speicherbedarf messen, und eine Durchsatzobergrenze, die deutlich über dem liegt, was die meisten Ingenieure erwarten, wenn die Produktionszahlen eintreffen. Wenn man die Struktur versteht, bevor man sie in Produktion nimmt, erspart man sich eine überraschende Rechnung und eine architektonische Neugestaltung unter Druck.

Die abgestufte Kostenstruktur

Voraussetzung ist der „Workers Paid“-Plan – 5 USD/Monat, ohne ihn sind DOs nicht verfügbar. Von dort:

Ansprüche: 0,15 $/Million nach der ersten kostenlosen Million pro Monat. Jeder Aufruf eines DO-Stubs zählt als Anfrage. Der Worker, der das Routing durchführt, verwendet unabhängig davon das normale Worker-Anfragekontingent.

Berechnen Sie in GB-Sekunden: 12,50 $/Million GB-Sekunden nach 400.000 kostenlosen Daten pro Monat. GB-Sekunde ist die Arbeitseinheit: genutzter Speicher in GB multipliziert mit der Ausführungszeit in Sekunden. Der minimale Speicherbedarf pro DO-Instanz beträgt 128 MB (0,125 GB). Eine Anfrage, die 10 ms bei 128 MB dauert, verbraucht 0,00125 GB-Sekunden.

Speicher: 0,20 $/GB-Monat, danach 1 GB kostenlos. Kumulativ – wenn Sie 1.000 DOs mit jeweils 10 KB haben, sind das 10 MB Speicherplatz, also deutlich im freien Bereich. Wenn Sie größere Datenmengen pro Instanz beibehalten, fallen die Speicherkosten an.

Alarme: 0,15 $/Million Aufrufe. Die gleiche Tabelle wie bei normalen Anfragen.

Die tatsächliche Berechnung der kostenlosen Rechenstufe

400.000 GB-Sekunden/Monat ist die Zahl. Bei einem Mindestbedarf von 128 MB pro Instanz entspricht dies einer Ausführungszeit von 3,2 Millionen Sekunden – etwa 889 Stunden aktiver DO pro Monat, verteilt auf alle Ihre DOs.

Das Konto, auf das es ankommt, richtet sich nach der Arbeitslast, nicht nach DO. Wenn Sie DOs haben, die Anfragen von 10 ms bis 128 MB verarbeiten, verbraucht jede Anfrage 0,00125 GB-Sekunden. Das kostenlose Kontingent deckt 320 Millionen dieser Anfragen ab – viel mehr als das kostenlose Kontingent an Anfragen (1 Million). Der Engpass des kostenlosen Plans für Workloads mit leichten und schnellen DOs ist das Anforderungskontingent, nicht das Rechenkontingent.

Das Szenario, das dieses Konto umkehrt, sind DOs, die einen großen Zustand im Speicher beibehalten. Wenn ein DO ein 2 MB großes Dokument in den Speicher lädt, um gemeinsame Bearbeitungen zu verarbeiten, beträgt sein Platzbedarf nicht 0,125 GB, sondern eher 0,127 GB, zuzüglich des isolierten Overheads. Bei DOs, die große JSON-Dateien, Bildpuffer zur Verarbeitung oder große lokale Caches laden, wächst die tatsächliche GB-Sekunde mit der Größe des In-Memory-Status und nicht mit der Ausführungszeit der Anfrage.

Wie viel kostet ein konkreter Arbeitsaufwand? 1 Million 100-ms-Anfragen bei 128 MB: 100.000 GB-Sekunden = 1,25 $ an Rechenleistung (innerhalb der kostenlosen Rechenstufe, aber über der kostenlosen Anforderungsstufe: 0,15 $ × (1 − 1) = kostenlos, wenn es die erste Million ist, 0,15 $, wenn es die zweite ist). Insgesamt: 1,25 $ für Rechenleistung + 0,15 $ für zusätzliche Anfragen = 1,40 $ zusätzlich zu den 5 $ im Plan.

Die Durchsatzobergrenze, die früher als erwartet angezeigt wird

Die serielle Ausführung garantiert die Konsistenz von DOs und deren Durchsatzobergrenze. Ein DO verarbeitet jeweils eine Anfrage. Der maximale Durchsatz pro Instanz ist umgekehrt proportional zur durchschnittlichen Zeit pro Anfrage.

Die Formel: Maximaler Durchsatz (req/s) = 1000 ms / durchschnittliche Zeit pro Anfrage (ms).

5 ms Verarbeitung pro Anfrage: 200 Anforderungen/s pro DO-Instanz. 10 ms Verarbeitung: 100 Anforderungen/s. 50-ms-Verarbeitung (einschließlich E/A als Aufruf an einen externen Dienst): 20 Anforderungen/s.

Diese Obergrenze scheint hoch zu sein, bis Sie über eine beliebte Funktion verfügen, die alle Anfragen an dieselbe DO-ID weiterleitet. Ein Chatroom mit 500 Nachrichten pro Sekunde, die an denselben DO gesendet werden, wird mehr als 300 Nachrichten pro Sekunde in die Warteschlange stellen, wenn die durchschnittliche Verarbeitungszeit 4 ms beträgt. Die von den Kunden wahrgenommene Latenz steigt proportional zur Größe der Warteschlange.

Die Lösung ist Sharding. Anstatt die DO-ID direkt vom Raum oder der Ressource abzuleiten, fügen Sie ein numerisches Suffix hinzu, das auf einem Hash der Kennung basiert:

const shardCount = 10; const shard = Math.abs(hashCode(roomId)) % shardCount; const id = env.ROOMS.idFromName(`room-${roomId}-shard-${shard}`);

Jeder Shard ist eine unabhängige DO-Instanz mit einer eigenen Durchsatzobergrenze. Sharding eignet sich gut für Vorgänge, bei denen die Konsistenz nur innerhalb eines Shards gewährleistet werden muss. Wenn Sie globale Konsistenz über alle Shards einer Ressource hinweg benötigen, wird die Lösung komplexer.

Überraschende Speichergrenzen

list() liefert maximal 128 Einträge pro Aufruf. Diese Grenze ist nicht genau dokumentiert, erscheint aber jedes Mal, wenn Sie einen DO mit mehr als 128 Schlüsseln im Speicher haben und list() erwarten, dass der vollständige Satz vorhanden ist.

Paginierung verwendet Cursor:

async listAll(): Promise<Map<string, unknown>> { const result = new Map<string, unknown>(); let cursor: string | undefined; while (true) { const batch = await this.ctx.storage.list({ cursor, limit: 128 }); for (const [key, value] of batch) { result.set(key, value); } if (batch.size < 128) break; cursor = [...batch.keys()].at(-1); } return result; }

Dies in der Entwicklung zu ignorieren (wo DOs nur wenige Daten haben) und in der Produktion mit 500 Schlüsseln herauszufinden, ist ein Fehler, der stillschweigend zu falschen Ergebnissen führt – die Anwendung empfängt die ersten 128 Elemente und verhält sich so, als ob sie alle wären.

Eine weitere Einschränkung: Transaktionen sind auf eine einzelne DO-Instanz beschränkt. Es gibt keine atomare Transaktion, die zwei verschiedene DO-Instanzen ändert. Wenn Ihr Design Atomizität zwischen zwei DOs erfordert – zum Beispiel die Übertragung von Guthaben von einem DO auf ein anderes – müssen Sie ein zweiphasiges Commit-Protokoll auf der Anwendungsebene implementieren, mit einer Entschädigung im Fehlerfall. In den meisten Fällen signalisiert dies, dass das Design überarbeitet werden muss: Entweder sollte der Zustand im selben DO leben, oder die Operation erfordert keine strikte Atomizität zwischen Instanzen.

Was nach der Produktionsaufnahme zu überwachen ist

Die Antwortlatenz nach DO-ID ist der direkteste Indikator für einen Durchsatzengpass. Wenn bestimmte DOs eine zunehmende Latenz aufweisen, während andere schnell werden, stellen diese spezifischen DOs Anforderungen in die Warteschlange – Kandidaten für das Sharding.

Der Verbrauch von GB-Sekunden im Vergleich zur Anzahl der Anfragen zeigt DOs mit einem größeren Speicherbedarf als erwartet. Wenn das Verhältnis GB-Sekunden/Anfrage wächst, ohne die Verarbeitungszeit zu ändern, lädt ein DO mehr Status in den Speicher.

Die Speicherung nach Namespace zeigt das Wachstum der Daten an, die Sie möglicherweise ohne eine Bereinigungsroutine ansammeln. DOs, ​​die in den Speicher schreiben, ohne sie jemals zu löschen, sammeln Daten auf unbestimmte Zeit an – 0,20 $/GB/Monat scheinen billig zu sein, bis Sie ein paar GB an historischen Daten haben, auf die niemand mehr zugreift.

Wo 5 $/Monat mehr bringen

Der Basispreis von 5 USD/Monat deckt den kostenpflichtigen Plan ab. Für kleine Anwendungen mit Nutzung innerhalb der kostenlosen Kontingente sind dies die Gesamtkosten. Bei Anwendungen, die wachsen, steigen die Grenzkosten der drei Dimensionen – Anfragen, Rechenleistung, Speicher – je nach Art der Last auf unterschiedliche Weise.

Häufige Anforderungsladevorgänge, aber leichte Verarbeitung: Die Anforderungsstufe ist vor der Berechnung erschöpft. Lösung: Überprüfen Sie, ob eine der Anfragen im Worker-Layer zwischengespeichert werden kann, bevor sie den DO erreichen.

Hohe Verarbeitungslasten bei wenigen Anfragen: Die Rechenleistung dominiert. Lösung: Messen Sie den tatsächlichen Speicherbedarf und die durchschnittliche Ausführungszeit und prüfen Sie, ob ein Teil der Berechnung auf den Worker verschoben werden kann, der das Routing durchführt.

Viele DOs mit persistenten Daten: Der Speicher dominiert. Lösung: Legen Sie TTL für Daten fest, die nicht unbegrenzt haltbar sein müssen, und implementieren Sie Bereinigungsroutinen über die Alarm-API.

Die in der Produktion vorherrschenden Grenzen sind der Seriendurchsatz und list(). Den Rest finden Sie in der Dokumentation, bevor es zu einer Überraschung kommt. Diese beiden finden Sie in den ersten Tagen bei echtem Verkehr.

Lesen Sie auch