Ottimizzare significa bilanciare latenza, freschezza e costo
In un’applicazione cloud-native l’ottimizzazione ruota attorno a due leve: mettere in cache i dati letti spesso per ridurre latenza e chiamate al backend, e reagire con eleganza quando un servizio applica throttling. Ogni cache introduce però il rischio di dati stantii: la scelta del TTL è il compromesso centrale fra freschezza e hit rate. All’esame DVA-C02 (passing score 700/1000) gli scenari chiedono quasi sempre “qual è il modo più efficiente o meno costoso”, non come si configura passo passo.
Le tre cache da conoscere
Amazon API Gateway caching si abilita per stage e memorizza le response in base a una cache key derivata dai parametri di richiesta. Riduce sia la latenza sia il numero di invocazioni verso l’integrazione (AWS Lambda, backend HTTP), abbattendo i costi di compute. Il TTL è configurabile: un valore alto massimizza gli hit ma serve response potenzialmente vecchie. Ideale per endpoint di lettura idempotenti con dati che cambiano lentamente.
Amazon DynamoDB Accelerator (DAX) è una cache in-memory specifica per DynamoDB, con latenza in microsecondi e API compatibili con quelle di DynamoDB (basta usare il client DAX). È write-through e gestisce un item cache e un query cache. Va scelto quando il carico è read-heavy e latency-sensitive. Attenzione: le strongly consistent read non passano dalla cache ma vanno direttamente alla tabella, quindi DAX conviene con eventually consistent read. Riducendo le read capacity unit consumate, ha un effetto diretto sui costi.
Amazon ElastiCache (Redis o Memcached) è una cache generica davanti a database come Amazon RDS. I due pattern d’uso sono lazy loading (cache-aside: si legge dalla cache, in caso di miss si legge dal DB e si popola la cache) e write-through (si scrive contemporaneamente su cache e DB). Lazy loading carica solo i dati richiesti ma paga la penalità del cache miss e rischia dati stantii; write-through mantiene la cache fresca ma riempie la memoria anche di dati mai letti. Un TTL sulle entry di lazy loading è il rimedio classico contro lo staleness. Redis aggiunge replication, persistence e strutture dati avanzate; Memcached è multi-thread e più semplice per key-value puro con scaling orizzontale.
Throttling, exponential backoff e jitter
Quando superi la capacità di un servizio ricevi errori di throttling: DynamoDB restituisce ProvisionedThroughputExceededException, API Gateway e Lambda rispondono con 429 (Too Many Requests). La strategia corretta è retry con exponential backoff: a ogni tentativo fallito l’attesa cresce esponenzialmente (per esempio 1s, 2s, 4s) invece di ritentare subito. Da solo però il backoff sincronizza i client, che riprovano tutti nello stesso istante creando picchi (“thundering herd”). Il jitter aggiunge una componente casuale al tempo d’attesa, distribuendo i retry e riducendo la contention.
I retry con backoff sono già implementati automaticamente dagli AWS SDK per gli errori transitori e di throttling, con numero massimo di tentativi configurabile. All’esame, di fronte a throttling intermittente la risposta giusta è quasi sempre “affidarsi al retry con exponential backoff e jitter”, non aumentare subito la capacity.
Cost optimization: quando la cache paga
Il caching riduce i costi spostando il lavoro dal backend costoso (compute, RCU, query sul DB) a un layer di memoria più economico. Ma la cache stessa ha un costo: conviene solo con un buon hit rate e dati riletti spesso. Un TTL troppo basso vanifica il risparmio (troppi miss), troppo alto serve dati vecchi. Per picchi gestibili conviene il backoff prima di scalare la capacity.
Trappole tipiche d’esame
- Cache per DynamoDB con latenza in microsecondi → DAX: non ElastiCache; DAX è nativo per DynamoDB e non richiede logica cache-aside custom.
- DAX ma le letture non risultano cachate → strongly consistent read: le consistent read bypassano DAX; per sfruttarlo servono eventually consistent read.
- Errori 429 o throttling intermittente → exponential backoff con jitter: aggiungere il jitter, non solo il backoff, per evitare retry sincronizzati; si aumenta la capacity solo se il problema persiste.
- Dati stantii nella cache applicativa → impostare un TTL: con lazy loading il TTL è il rimedio contro lo staleness; il write-through mantiene freschezza ma spreca memoria.
- Ridurre latenza su endpoint di lettura di API Gateway → cache dello stage: abbatte anche i costi di invocazione del backend, ma solo per response poco variabili.
- Serve replication, persistence o strutture dati → Redis, non Memcached: per failover, persistenza o sorted set e pub-sub la risposta è Redis; Memcached per key-value semplice multi-thread.