Cloudflare
Durable Objects
Custos
Produção
Limites

Oggetti durevoli in produzione: come sarà la bolletta e i limiti che sorprendono

Analisi dettagliata dei costi reali degli oggetti durevoli nella produzione: come calcolare i GB-secondi, dove il throughput seriale diventa un collo di bottiglia e i limiti che sorprendono su larga scala.

Oggetti durevoli in produzione: come sarà la bolletta e i limiti che sorprendono

Il prezzo di Sustainable Objects sembra semplice finché non si calcola il primo mese effettivo. Tre componenti interagenti, un livello gratuito che sembra generoso finché non si misura l'impronta di memoria corretta e un limite di throughput che appare ben prima di quello che la maggior parte degli ingegneri si aspetta quando arrivano i numeri di produzione. Comprendere la struttura prima di metterla in produzione evita una fattura a sorpresa e una riprogettazione architettonica sotto pressione.

La struttura dei costi per livelli

Il piano retribuito dai lavoratori è il prerequisito: $ 5 al mese, senza di esso i DO non sono disponibili. Da lì:

Reclami: $ 0,15/milione dopo il primo milione gratuito al mese. Ogni chiamata a uno stub DO conta come una richiesta. Il lavoratore che esegue l'instradamento utilizza la normale quota di richiesta dei lavoratori, separata da questa.

Elaborazione in GB al secondo: $ 12,50/milione di GB al secondo dopo 400.000 gratuiti al mese. GB al secondo è l'unità di lavoro: memoria utilizzata in GB moltiplicata per il tempo di esecuzione in secondi. L'impronta di memoria minima per istanza DO è 128 MB (0,125 GB). Una richiesta che impiega 10 ms a 128 MB consuma 0,00125 GB al secondo.

Spazio di archiviazione: $ 0,20/GB al mese dopo 1 GB gratuito. Cumulativo: se disponi di 1.000 DO con 10 KB ciascuno, si tratta di 10 MB di spazio di archiviazione, ben all'interno dell'intervallo libero. Se mantieni dati più grandi per istanza, inizia a comparire il costo di archiviazione.

Allarmi: 0,15 $/milione di invocazioni. La stessa tabella delle richieste normali.

Il calcolo reale del livello di calcolo gratuito

Il numero è 400mila GB-secondo/mese. Con un ingombro minimo di 128 MB per istanza, ciò equivale a 3,2 milioni di secondi di esecuzione: circa 889 ore di DO attivo al mese, distribuite tra tutti i DO.

L'account che conta è per carico di lavoro, non per DO. Se disponi di DO che elaborano richieste da 10 ms a 128 MB, ciascuna richiesta consuma 0,00125 GB al secondo. Il livello gratuito copre 320 milioni di queste richieste, molto più delle richieste del livello gratuito (1 milione). Il collo di bottiglia del piano gratuito, per carichi di lavoro con DO leggeri e veloci, è la quota di richieste, non la quota di calcolo.

Lo scenario che inverte questo account è quello dei DO che mantengono uno stato di grandi dimensioni in memoria. Se un DO carica un documento da 2 MB in memoria per elaborare modifiche collaborative, il suo ingombro non è di 0,125 GB: è più vicino a 0,127 GB, più il sovraccarico di isolamento. Per i DO che caricano JSON di grandi dimensioni, buffer di immagini per l'elaborazione o cache locali di grandi dimensioni, i GB al secondo effettivi crescono con la dimensione dello stato in memoria, non con il tempo di esecuzione della richiesta.

Quanto costa un carico di lavoro concreto? 1 milione di richieste da 100 ms a 128 MB: 100.000 GB al secondo = 1,25 USD di elaborazione (all'interno del livello di elaborazione gratuito, ma al di sopra del livello gratuito di richieste: 0,15 USD × (1 − 1) = gratuito se è il primo milione, 0,15 USD se è il secondo). In totale: $ 1,25 di calcolo + $ 0,15 di richieste extra = $ 1,40 in aggiunta ai $ 5 del piano.

Il limite di throughput visualizzato prima del previsto

L'esecuzione seriale garantisce la coerenza dei DO e il relativo limite di throughput. Un DO elabora una richiesta alla volta. Il throughput massimo per istanza è inversamente proporzionale al tempo medio per richiesta.

La formula: throughput massimo (req/s) = 1000 ms / tempo medio per richiesta (ms).

Elaborazione di 5 ms per richiesta: 200 req/s per istanza DO. Elaborazione 10ms: 100 richieste/s. Elaborazione 50ms (incluso I/O come chiamata a un servizio esterno): 20 req/s.

Questo limite sembra elevato finché non si dispone di una funzionalità popolare che instrada tutte le richieste allo stesso DO ID. Una chat room con 500 messaggi al secondo inviati allo stesso DO metterà in coda oltre 300 messaggi al secondo se il tempo di elaborazione medio è di 4 ms. La latenza percepita dai clienti aumenta proporzionalmente alla dimensione della coda.

La soluzione è lo sharding. Invece di derivare il DO ID direttamente dalla stanza o dalla risorsa, aggiungi un suffisso numerico basato su un hash dell'identificatore:

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

Ogni frammento è un'istanza DO indipendente, con il proprio limite di throughput. Lo sharding funziona bene per le operazioni in cui la coerenza deve essere garantita solo all'interno di uno shard: se è necessaria la coerenza globale tra tutti gli shard di una risorsa, la soluzione diventa più complessa.

Limiti di archiviazione sorprendenti

list() restituisce un massimo di 128 voci per chiamata. Questo limite non è documentato in modo visibile, ma appare ogni volta che hai un DO con più di 128 chiavi in ​​memoria e chiami list() aspettandoti il ​​set completo.

L'impaginazione utilizza il cursore:

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; }

Ignorare questo in fase di sviluppo (dove i DO hanno pochi dati) e scoprirlo in produzione con 500 chiavi è un bug che produce silenziosamente risultati errati: l'applicazione riceve i primi 128 elementi e si comporta come se fossero tutti.

Un altro limite: le transazioni sono limitate a una singola istanza DO. Non esiste alcuna transazione atomica che modifichi due diverse istanze DO. Se il tuo progetto richiede atomicità tra due DO, ad esempio il trasferimento di credito da un DO a un altro, devi implementare un protocollo di commit in due fasi a livello di applicazione, con compensazione in caso di fallimento. Nella maggior parte dei casi, ciò segnala che la progettazione deve essere rivista: o lo stato dovrebbe vivere nello stesso DO, oppure l'operazione non necessita di una rigorosa atomicità tra le istanze.

Cosa monitorare dopo essere entrati in produzione

La latenza della risposta in base al DO ID è l'indicatore più diretto del collo di bottiglia della velocità effettiva. Se DO specifici hanno una latenza crescente mentre altri diventano veloci, quei DO specifici accodano le richieste, candidati allo sharding.

Il consumo di GB al secondo rispetto al numero di richieste rivela DO con un ingombro di memoria maggiore del previsto. Se il rapporto GB-secondi/richiesta aumenta senza modificare il tempo di elaborazione, alcuni DO caricano più stato nella memoria.

L'archiviazione per spazio dei nomi mostra la crescita dei dati che potresti accumulare senza una routine di pulizia. I DO che scrivono nello spazio di archiviazione senza mai eliminare accumulano dati a tempo indeterminato: $ 0,20/GB al mese sembra economico finché non si hanno pochi GB di dati storici a cui nessuno accede più.

Dove $ 5 al mese rendono di più

La base di base di $ 5 al mese copre il piano a pagamento. Per le piccole applicazioni con utilizzo compreso nei livelli gratuiti, questo è il costo totale. Per le applicazioni in crescita, il costo marginale delle tre dimensioni (richieste, elaborazione e storage) cresce in modi diversi a seconda del tipo di carico.

Carichi di richieste frequenti ma elaborazione leggera: il livello di richiesta si esaurisce prima del calcolo. Soluzione: controlla se una qualsiasi delle richieste può essere memorizzata nella cache nel livello Worker prima di raggiungere il DO.

Carichi di elaborazione pesanti con poche richieste: il calcolo domina. Soluzione: misurare l'impronta di memoria effettiva e il tempo medio di esecuzione e verificare se parte del calcolo può essere spostata sul lavoratore che esegue il routing.

Molte cose da fare con dati persistenti: lo storage domina. Soluzione: impostare TTL per i dati che non devono durare indefinitamente e implementare routine di pulizia tramite Alarm API.

I limiti più diffusi nella produzione sono il throughput seriale e list(). Il resto lo troverete nella documentazione prima di essere una sorpresa. Questi due li trovi nei primi giorni con traffico reale.

Leggi anche