Ir al contenido

Cómo detectar saturación antes de que el usuario la note (Odoo + PgBouncer + PostgreSQL)

1 de diciembre de 2025 por
Cómo detectar saturación antes de que el usuario la note (Odoo + PgBouncer + PostgreSQL)
John Wolf
| Todavía no hay comentarios

La diferencia entre “se puso lento” y “fue un incidente” es cuándo te enteras.

Si esperas a que el usuario se queje, ya vas tarde. La buena noticia: en un stack Odoo → PgBouncer → PostgreSQL, la saturación deja huellas claras minutos antes.

Este post te da un sistema simple: 4 señales tempranas + umbrales prácticos + qué hacer según el patrón.


1) Señal temprana #1: aparece cola en PgBouncer

Qué mirar

En la consola admin de PgBouncer:

SHOW POOLS;

Los dos indicadores que te adelantan el problema son:

  • cl_waiting > 0 sostenido

  • maxwait subiendo (el cliente más antiguo lleva esperando)

Interpretación: tu app está intentando iniciar transacciones, pero PgBouncer no consigue asignarles una conexión “server” lo suficientemente rápido. Eso es “saturación” en tiempo real, incluso si todavía no hay errores visibles.

Umbral útil

  • Warning: cl_waiting > 0 durante 60–120s

  • Critical: maxwait > 1s sostenido (ya afecta UX), > 5s es incidente

Acción inmediata

  • Si hay cola: no adivines. Sigue al paso 4 (clasificar causa).


2) Señal temprana #2: crece el porcentaje de transacciones largas

Qué mirar (PostgreSQL)

Consulta rápida para ver sesiones y transacciones largas:

SELECT pid, usename, state, xact_start, query_start, wait_event_type, wait_event, query
FROM pg_stat_activity
WHERE datname = current_database()
ORDER BY xact_start NULLS LAST;

Señales de alerta

  • xact_start “viejo” (transacciones > 60–120s en horas pico)

  • muchas sesiones con wait_event_type = Lock

Interpretación: aunque el CPU esté “ok”, las transacciones largas secuestran concurrencia (y con PgBouncer, secuestran conexiones server).

Umbral útil

  • Warning: 1–3 transacciones > 2 min en horario de carga

  • Critical: transacciones > 5–10 min (casi siempre bloqueo/cron monstruo)


3) Señal temprana #3: el throughput cae pero la demanda no (la “muerte lenta”)

Qué mirar

  • Requests/segundo (o jobs/segundo) vs latencia

  • PgBouncer SHOW STATS; (si lo estás recolectando)

  • Odoo: latencia p95/p99 por endpoint (login, listado, write, confirmaciones, reportes)

Patrón clásico previo al incidente

  • p95 sube lentamente

  • p99 se dispara primero

  • throughput no sube (o cae) aunque haya tráfico normal

Interpretación: ya no estás “escalando” con carga. Estás en contención.

Umbral útil

  • Warning: p99 > 2–3x tu baseline

  • Critical: errores por timeout o retries masivos


4) Señal temprana #4: los crons empiezan a solaparse (y nadie mira)

Esto es brutal en Odoo.

Qué mirar

  • duración de crons pesados

  • horario real de ejecución vs esperado

  • si se están superponiendo (sobre todo si tienes max_cron_threads > 1)

Patrón previo al incidente

  • cron A tarda más → cron B arranca igual → ambos compiten por locks y DB

  • PgBouncer empieza a hacer cola

  • usuarios notan lentitud “en oleadas”

Umbral útil

  • Warning: cron que pasa de X min a 2X min de forma repetida

  • Critical: backlog (crons no terminan antes de su próximo run)


5) La clasificación clave: 3 tipos de saturación (y qué hacer)

Cuando detectas saturación temprana, clasifícala en una de estas 3.

Esto evita el error típico de “subir pool_size y listo”.

Tipo A — Saturación por pool (config/concurrencia)

Síntomas

  • cl_waiting sube

  • sv_idle ~ 0

  • Postgres NO está al 100%

  • no hay grandes locks, solo “mucho movimiento”

Acciones

  • sube default_pool_size con cuidado

  • agrega reserve_pool_size para picos

  • revisa si max_client_conn o max_db_connections te están limitando

Tipo B — Saturación por locks / transacciones largas

Síntomas

  • cl_waiting sube

  • maxwait sube sostenido

  • en Postgres ves wait_event_type = Lock o xact_start muy viejo

  • CPU no necesariamente alto (es contención)

Acciones

  • identifica la transacción larga (job/cron/acción de usuario)

  • corta duración: batching, commits por lote, evitar I/O externo en la transacción

  • añade lock_timeout, statement_timeout e idle_in_transaction_session_timeout (según política)

Tipo C — Saturación de recursos (CPU/RAM/I/O)

Síntomas

  • CPU al 100% o I/O wait alto

  • latencia sube en todos lados

  • PgBouncer puede mostrar cola, pero el problema raíz es el host/DB

Acciones

  • optimizar queries/índices

  • bajar concurrencia (workers/crons) para reducir contención

  • mejorar disco/IOPS

  • revisar bloat/autovacuum si el rendimiento cae con el tiempo


6) Un set mínimo de alertas “anti-incendio”

Si solo pudieras crear 6 alertas, serían estas:

PgBouncer

  1. cl_waiting > 0 por 2 min

  2. maxwait > 1s por 2 min

PostgreSQL

  1. transacciones > 2 min (conteo > N)

  2. sesiones esperando locks > N

Odoo / app

  1. p99 latencia > 2–3x baseline

  2. tasa de errores (timeouts/5xx) > baseline


7) El truco que más te da tiempo: “alerta por tendencia”, no por caída

Muchos monitorean “CPU > 90%”. Eso llega tarde.

Lo que te compra tiempo es alertar por cambio de comportamiento:

  • p99 sube 30–50% respecto al baseline

  • maxwait pasa de 0 a 0.5s y sigue subiendo

  • crons empiezan a durar 2x

Eso ocurre antes de que el usuario note el dolor.

Cierre

Si quieres detectar saturación antes que el usuario:

  • mide cola en PgBouncer,

  • mide transacciones largas y locks en Postgres,

  • mide p95/p99 en Odoo,

  • y vigila crons como si fueran usuarios (porque lo son, pero más peligrosos).


Siguiente capítulo ->

Cómo detectar saturación antes de que el usuario la note (Odoo + PgBouncer + PostgreSQL)
John Wolf 1 de diciembre de 2025
Compartir
Etiquetas
Archivo
Iniciar sesión dejar un comentario