Scegliere il data store giusto

Firestore è la scelta di default per app web e mobile: database documentale (NoSQL) fully managed, scala automaticamente, offre real-time listeners e sincronizzazione offline. Non ha join relazionali né SQL: se il modello richiede transazioni complesse su tabelle correlate, non è lui. Cloud SQL è il managed relazionale (PostgreSQL, MySQL, SQL Server) per carichi OLTP con schema, join e transazioni ACID a scala regionale, con read replica e HA failover. Quando serve un relazionale con scala orizzontale globale e strong consistency (multi-region, availability 99,999%), la risposta è Spanner — costoso, quindi giustificato solo dalla scala e dallo SLA, non per una singola istanza. Memorystore (Redis/Memcached) è la cache in-memory a bassa latenza davanti al database, non un data store primario. Per file e blob c’è Cloud Storage: per far caricare o scaricare oggetti direttamente dal client senza esporre credenziali né far transitare i byte dall’app, si usano i signed URL, che concedono accesso temporaneo e a tempo a un singolo oggetto.

Messaggistica ed eventi: Pub/Sub ed Eventarc

Pub/Sub è il backbone asincrono: disaccoppia producer e consumer e fa fan-out (un messaggio a molti subscriber). La consegna è at-least-once, quindi i duplicati sono normali e il consumer deve essere idempotente; per ordinare i messaggi servono le ordering key, e i messaggi non processabili finiscono in un dead-letter topic. Eventarc serve invece a instradare eventi tra servizi in formato CloudEvents: cattura eventi da sorgenti Google Cloud (es. Cloud Audit Logs, Cloud Storage) o da Pub/Sub e li recapita a target come Cloud Run. In pratica Eventarc è lo strato standardizzato di eventing (costruito su Pub/Sub) quando vuoi reagire a “è successo qualcosa in un servizio GCP” senza cablare a mano le integrazioni.

Task asincroni, scheduling e orchestrazione

Qui l’esame ama confondere. Cloud Tasks serve per accodare lavoro asincrono verso un endpoint HTTP con controllo fine: rate limiting, concurrency, retry per singolo task e schedulazione del singolo task. È la scelta quando devi proteggere un backend a valle dal sovraccarico o smussare i picchi — cosa che Pub/Sub, orientato all’event fan-out, non fa. Cloud Scheduler è il cron gestito: fa partire un job a orari fissi verso HTTP, Pub/Sub o App Engine (es. report notturni, pulizie periodiche). Workflows orchestra più servizi in una sequenza o state machine definita in modo dichiarativo, con passaggio di stato, gestione errori e retry tra step: usalo quando devi coordinare chiamate in ordine con logica condizionale, non per un semplice fanout di eventi.

Idempotenza dei consumer

Poiché Pub/Sub, Cloud Tasks ed Eventarc garantiscono at-least-once, ogni consumer deve poter ricevere lo stesso evento più volte senza effetti collaterali duplicati. Il pattern è deduplicare su una idempotency key stabile (message ID, task name o un identificatore di business) persistita in un data store, così il retry riconosce ciò che è già stato elaborato. La semantica “exactly-once” percepita si ottiene così, non assumendo che il broker non ripeta mai la consegna.

Trappole tipiche d’esame

  • Relazionale a scala globale con strong consistency → Spanner (non Cloud SQL): Cloud SQL è regionale e scala verticalmente; Spanner scala orizzontalmente su più region. Se non serve la scala globale, però, Spanner è overkill costoso.
  • App mobile con sync offline e real-time → Firestore (non Cloud SQL): real-time listeners e persistenza offline sono nativi in Firestore; forzare join e SQL è il distrattore.
  • Smussare i picchi verso un backend fragile con rate limiting → Cloud Tasks (non Pub/Sub): Pub/Sub fa fan-out di eventi ma non limita il rate di consegna a un endpoint; Cloud Tasks sì.
  • Job ricorrente a orario fisso → Cloud Scheduler (non un servizio sempre attivo in polling): il cron gestito è più semplice ed economico di un consumer perennemente acceso.
  • Consumer che riceve due volte lo stesso evento → idempotency key + dedup (non “attivo exactly-once e ignoro il problema”): at-least-once implica duplicati; la correttezza sta nel consumer.
  • Download diretto di un file privato dal client → signed URL (non aprire il bucket né proxare i byte dall’app): accesso temporaneo a un singolo oggetto senza credenziali permanenti.