Este post es para mostrar resultados medibles. No “se siente más rápido”, sino:
latencia (p95/p99),
conexiones (PostgreSQL / PgBouncer),
RAM (Odoo + DB),
comparando antes vs después de meter PgBouncer y ordenar la concurrencia.
Nota: los números del ejemplo son ilustrativos. La parte valiosa es el método para que lo repitas en tu entorno y publiques tus propios “antes/después”.
1) Qué medimos (y por qué esas 3 cosas)
Latencia (p95/p99)
Porque el usuario no siente el promedio: siente el peor 1–5%.
p50: “promedio” percibido en calma
p95/p99: donde se ve la saturación (cola, locks, pool)
Conexiones
Porque Odoo escala… hasta que PostgreSQL se ahoga o PgBouncer hace cola.
Sin PgBouncer: cada worker puede aumentar presión sobre max_connections
Con PgBouncer: reduces conexiones reales y estabilizas
RAM
Porque la RAM te define cuántos workers puedes sostener y si terminas en swap (y swap = latencia horrible).
2) Antes: arquitectura típica “sin disciplina”
Antes (simplificado):
Odoo (workers) → PostgreSQL directo
max_connections alto “por las dudas”
crons pesados compitiendo con usuarios
picos de latencia por contención
Señales comunes “antes”:
Postgres con muchas conexiones activas/idle
p99 que se dispara en horarios de crons o cierres
RAM subiendo “a serrucho” con picos
3) Después: qué cambiamos (lo mínimo que mueve la aguja)
Después:
Odoo → PgBouncer (pool_mode=transaction) → PostgreSQL
default_pool_size controlando conexiones reales
reserve_pool_size para picos cortos
crons domados (concurrencia y transacciones más cortas)
observabilidad con SHOW POOLS; + p95/p99 + pg_stat_activity
4) Cómo tomamos la foto “antes” (baseline)
Ventana de medición
1 hora de carga normal
15–30 min de pico (ideal: cuando corren crons pesados)
misma hora del día, mismo tráfico (si puedes)
Métricas mínimas a capturar
Latencia
p95/p99 de endpoints clave:
login
abrir listado grande
confirmar/validar documento típico
generar PDF (si aplica)
Conexiones
Postgres:
pg_stat_activity (cuántas conexiones activas/idle)
(si ya hay PgBouncer) SHOW POOLS;
RAM
RSS del proceso Odoo (por worker)
RAM total y swap
(opcional) cache/buffers si estás afinando DB
5) Cómo tomamos la foto “después” (misma metodología)
La clave del “antes vs después” es que solo cambia la arquitectura/config, no el método de medición.
Repite:
mismas operaciones (o mismo test)
misma ventana de tiempo
mismos percentiles
6) Tabla comparativa (ejemplo listo para blog)
Ejemplo ilustrativo (cambia por tus números reales)
Latencia (ms)
| Operación | Antes p95 | Antes p99 | Después p95 | Después p99 |
|---|---|---|---|---|
| Login | 420 | 1200 | 180 | 450 |
| Abrir listado (2000 regs) | 900 | 2600 | 420 | 900 |
| Validar documento | 1100 | 3500 | 520 | 1200 |
| Reporte PDF | 4000 | 12000 | 3200 | 7000 |
Lectura: el p50 suele mejorar “algo”, pero la victoria real está en el p99: menos colas y menos picos.
Conexiones
| Métrica | Antes | Después |
|---|---|---|
| Conexiones totales a Postgres (pico) | 280 | 80 |
| Conexiones activas simultáneas (pico) | 120 | 60 |
| cl_waiting en PgBouncer (pico) | N/A | 0–5 (picos cortos) |
| maxwait (pico) | N/A | 0–1s (picos) |
Lectura: con PgBouncer, la idea es bajar conexiones reales y que cualquier espera sea pequeña y controlada, no explosiva.
RAM
| Métrica | Antes | Después |
|---|---|---|
| RAM total Odoo (pico) | 9.5 GB | 8.2 GB |
| RAM por worker (promedio) | 450 MB | 420 MB |
| Swap | 1.2 GB (picos) | 0 GB |
Lectura: no siempre baja muchísimo la RAM, pero el objetivo es estabilidad (sin swap) y menos picos.
7) Qué explica el “después” (la parte que da credibilidad)
1) PgBouncer reduce “churn” de conexiones reales
Menos overhead en Postgres, menos carga de autenticación, menos presión de max_connections.
2) Transaction pooling convierte concurrencia en “turnos”
En lugar de “200 conexiones reales peleando”, tienes un número controlado de conexiones server que se reutilizan.
3) Crons + long transactions: el verdadero asesino del p99
Al reducir duración de transacciones (batching) o evitar solapes de crons, reduces:
lock waits
conexiones “secuestradas”
colas en pool
8) Cómo presentarlo con impacto (y sin humo)
Incluye 3 capturas / snippets en el post:
Antes: pg_stat_activity con muchas conexiones (o gráfico)
Después: SHOW POOLS; mostrando cl_waiting ~ 0 y sv_idle saludable
Latencia: p95/p99 antes vs después (gráfico simple o tabla)
Eso hace que el post sea “irrefutable” y útil.
9) Plantilla para tu cierre (orientada a resultados)
“Antes teníamos p99 de 3–12s en operaciones críticas y picos de conexiones que estresaban PostgreSQL.
Después de introducir PgBouncer (transaction pooling) y ordenar crons/transacciones, el p99 bajó X%, las conexiones reales bajaron Y% y eliminamos swap.
Resultado: estabilidad predecible en horas pico.”