Runbook: Latenza Elevata delle Action
Severita: Warning
Alert Prometheus: HighActionLatency
Descrizione
Questo alert si attiva quando la latenza al percentile 99 (p99) delle action Moleculer supera i 5 secondi per un periodo continuativo di 5 minuti. Indica che almeno l'1% delle richieste impiega piu di 5 secondi per completarsi, segnalando un problema di performance che impatta l'esperienza utente.
Regola Prometheus:
histogram_quantile(0.99,
rate(moleculer_request_duration_seconds_bucket{namespace="pos-enterprise"}[5m])
) > 5
Impatto
- Esperienza utente degradata: gli operatori di cassa percepiscono rallentamenti, le operazioni si accumulano.
- Timeout a cascata: le action lente bloccano il thread Node.js, rallentando tutte le richieste sul pod.
- Timeout client: le app Flutter hanno timeout di 15-30 secondi; superato il limite, l'utente riceve un errore.
- Accumulo di code: il bulkhead Moleculer accumula richieste in coda, eventualmente rigettandole.
- Impatto business: durante il servizio di punta (pranzo/cena), la lentezza del sistema rallenta il turnover dei tavoli.
Diagnosi
1. Identificare le action lente
# Top 10 azioni piu lente (p99) - Grafana
topk(10,
histogram_quantile(0.99,
rate(moleculer_request_duration_seconds_bucket{namespace="pos-enterprise"}[5m])
)
)
# Latenza media per azione
rate(moleculer_request_duration_seconds_sum{namespace="pos-enterprise"}[5m])
/ rate(moleculer_request_duration_seconds_count{namespace="pos-enterprise"}[5m])
2. Verificare le metriche interne Moleculer
# Statistiche delle action via API interna
curl -s https://platform.sellogic.cloud/api/\$node/actions | jq '
[.[] | select(.count > 0)] |
sort_by(-.time.mean) |
.[:10] |
.[] | {name, count, "avgMs": (.time.mean | round), "p99Ms": (.time."99p" // "N/A")}
'
3. Verificare la latenza MongoDB
# Latenza operazioni MongoDB (se mongodb-exporter e attivo)
rate(mongodb_op_latencies_latency_total{namespace="pos-enterprise"}[5m])
/ rate(mongodb_op_latencies_ops_total{namespace="pos-enterprise"}[5m])
# Query lente dal profiler MongoDB (soglia 100ms)
kubectl exec deployment/orders-node -n pos-enterprise -- \
node -e "
const m = require('mongoose');
m.connect(process.env.MONGO_URI).then(async () => {
const db = m.connection.db.db('t_demo');
// Abilitare profiler (livello 1 = solo query lente > 100ms)
await db.command({profile: 1, slowms: 100});
console.log('Profiler abilitato. Attendere qualche minuto, poi eseguire:');
console.log('db.system.profile.find().sort({ts:-1}).limit(10)');
setTimeout(() => process.exit(0), 1000);
});
"
# Dopo qualche minuto, leggere le query lente
kubectl exec deployment/orders-node -n pos-enterprise -- \
node -e "
const m = require('mongoose');
m.connect(process.env.MONGO_URI).then(async () => {
const db = m.connection.db.db('t_demo');
const slow = await db.collection('system.profile')
.find({}).sort({ts:-1}).limit(10).toArray();
slow.forEach(q => {
console.log('---');
console.log('Duration:', q.millis, 'ms');
console.log('Collection:', q.ns);
console.log('Operation:', q.op);
console.log('Query:', JSON.stringify(q.command || q.query).substring(0, 200));
console.log('Plan:', q.planSummary);
});
process.exit(0);
});
"
4. Verificare la cache Redis
# Hit rate della cache
kubectl run redis-check --rm -it --image=redis:7-alpine -n pos-enterprise -- \
redis-cli -u "$REDIS_URL" info stats | grep -E "keyspace_hits|keyspace_misses"
# Calcolare l'hit ratio:
# hit_ratio = hits / (hits + misses)
# Se < 80%, la cache non e efficace
# Cache hit ratio (Grafana)
rate(moleculer_cacher_hit_total{namespace="pos-enterprise"}[5m])
/ (rate(moleculer_cacher_hit_total{namespace="pos-enterprise"}[5m])
+ rate(moleculer_cacher_miss_total{namespace="pos-enterprise"}[5m]))
5. Verificare la congestione NATS
# Latenza del transporter NATS
histogram_quantile(0.99,
rate(moleculer_transit_publish_duration_seconds_bucket{namespace="pos-enterprise"}[5m])
)
# Messaggi in coda
nats_server_messages_pending{server="nats.sellogic.cloud"}
6. Tracing distribuito
Se Jaeger/Tempo e configurato:
# Cercare tracce lente nell'ultimo periodo
# Grafana → Explore → Tempo
# Query: {service.name="pos-enterprise"} | duration > 5s
# Via API Tempo
curl -s "https://tempo.sellogic.cloud/api/search?q={service.name%3D%22pos-enterprise%22}&minDuration=5s&limit=10" | jq .
7. Verificare il CPU throttling
Il throttling CPU causa direttamente latenza elevata:
# Tempo di throttling
rate(container_cpu_cfs_throttled_seconds_total{namespace="pos-enterprise", container="moleculer"}[5m])
Se il throttling e significativo, vedere il runbook High CPU.
Risoluzione
Causa: Query MongoDB lente (collection scan)
# Identificare query senza indice
kubectl exec deployment/orders-node -n pos-enterprise -- \
node -e "
const m = require('mongoose');
m.connect(process.env.MONGO_URI).then(async () => {
const db = m.connection.db.db('t_demo');
// Esempio: aggiungere indice sulla collection orders
await db.collection('orders').createIndex(
{status: 1, createdAt: -1},
{background: true, name: 'idx_status_created'}
);
console.log('Indice creato');
process.exit(0);
});
"
Indici raccomandati per le collection principali:
| Collection | Indice | Motivazione |
|---|---|---|
| orders | {status: 1, createdAt: -1} | Filtro per stato + ordinamento |
| orders | {tableId: 1, status: 1} | Ordini per tavolo |
| products | {categoryId: 1, isActive: 1} | Prodotti per categoria |
| bookings | {date: 1, slotId: 1} | Prenotazioni per data |
| cash_movements | {sessionId: 1, createdAt: -1} | Movimenti per sessione |
Causa: Cache miss elevati
# Verificare la configurazione TTL del cacher
kubectl exec deployment/api-gateway -n pos-enterprise -- \
node -e "const c = require('./moleculer.config'); console.log(JSON.stringify(c.cacher))"
# Se il TTL e troppo basso, aumentarlo
kubectl set env deployment --all -n pos-enterprise MOL_CACHER_TTL=600
Causa: Congestione NATS
# Scalare i servizi per distribuire il carico
kubectl scale deployment orders-node -n pos-enterprise --replicas=3
# Verificare che non ci siano messaggi "slow consumer"
kubectl logs deployment/api-gateway -n pos-enterprise | grep -i "slow consumer"
Causa: Serializzazione di payload grandi
# Verificare la dimensione media dei payload
kubectl logs deployment/api-gateway -n pos-enterprise --since=10m | \
grep "content-length" | awk '{print $NF}' | sort -n | tail -10
# Abilitare pagination per action che restituiscono liste grandi
# Esempio: products.list con pageSize=50 invece di restituire tutti
Causa: Event loop bloccato
# Verificare se ci sono operazioni sincrone che bloccano il loop
kubectl exec <POD_NAME> -n pos-enterprise -- \
node -e "
const blocked = require('blocked-at');
blocked((time, stack) => {
console.log('Event loop bloccato per', time, 'ms');
console.log(stack.join('\n'));
}, {threshold: 100});
setTimeout(() => process.exit(0), 30000);
"
Mitigazione temporanea: aumentare timeout
kubectl set env deployment/api-gateway -n pos-enterprise \
MOL_REQUEST_TIMEOUT=30000
Prevenzione
- Indici MongoDB: audit trimestrale degli indici con
db.collection.aggregate([{$indexStats: {}}]). - Cache warming: pre-popolare la cache Redis all'avvio del servizio per le query piu frequenti.
- Pagination obbligatoria: tutte le action
listdevono supportarepageepageSizecon default ragionevoli. - Tracing abilitato: configurare OpenTelemetry su tutti i nodi per identificare i colli di bottiglia.
- SLO definiti: impostare obiettivi di latenza per action (p99 < 1s per letture, p99 < 3s per scritture).
// moleculer.config.js - tracing
tracing: {
enabled: true,
exporter: {
type: "OTLP",
options: {
url: "http://tempo.sellogic.cloud:4318/v1/traces",
headers: {},
}
}
}
Escalation
| Livello | Condizione | Contatto |
|---|---|---|
| L1 - Ops | p99 > 5s, singola azione | Monitorare, verificare query |
| L2 - Dev | Latenza persistente, necessario profiling | Team Dev |
| L2 - Ops | Latenza causata da infrastruttura (DB, NATS) | DevOps Lead |
| L3 - All | p99 > 10s su azioni critiche (ordini, pagamenti) | Incident call |
Tempo massimo di risposta: 15 minuti. Durante orario di punta (12:00-14:00, 19:00-22:00) trattare come Critical.
Questa pagina ti è stata utile?