Skip to main content

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:

CollectionIndiceMotivazione
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 list devono supportare page e pageSize con 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

LivelloCondizioneContatto
L1 - Opsp99 > 5s, singola azioneMonitorare, verificare query
L2 - DevLatenza persistente, necessario profilingTeam Dev
L2 - OpsLatenza causata da infrastruttura (DB, NATS)DevOps Lead
L3 - Allp99 > 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?