Ir al contenido

Errores reales de producción (Odoo + PgBouncer + PostgreSQL) y cómo se resolvieron

5 de diciembre de 2025 por
Errores reales de producción (Odoo + PgBouncer + PostgreSQL) y cómo se resolvieron
John Wolf
| Todavía no hay comentarios

En producción, los problemas “no avisan” con mensajes bonitos. Suelen aparecer como: lentitud intermitente, picos de 500, timeouts, o “solo a ciertas horas”. En este post recopilo errores muy comunes (y reales) en stacks Odoo + PgBouncer, con el patrón:

Síntoma → Causa raíz → Cómo se resolvió → Cómo evitar que vuelva.


1) PoolError: The Connection Pool Is Full (Odoo) en longpolling / picos

Síntoma

  • Errores 500 en endpoints tipo /longpolling/poll o en momentos de carga.

  • Traceback con: psycopg2.pool.PoolError: The Connection Pool Is Full. Odoo

Causa raíz (lo típico)

  • db_maxconn demasiado bajo para la concurrencia real (workers + crons + longpolling).

  • O bien picos donde muchas requests intentan abrir cursor al mismo tiempo.

  • A veces se ve “aunque el cálculo teórico daba” (porque el tráfico real y el longpolling hacen que el peor caso ocurra más seguido). Odoo+1

Cómo se resolvió

  • Subir db_maxconn moderadamente (sin hacerlo gigante).

  • Revisar workers y especialmente max_cron_threads (crons paralelos + longpolling es combo explosivo).

  • Si había PgBouncer, asegurarse de que max_client_conn no estuviera limitando “por arriba”.

Cómo evitar que vuelva

  • Instrumentar picos: si suben los 500, mirar qué hora y qué cron coincide.

  • En PgBouncer, vigilar cl_waiting (si ya hay cola, Odoo tenderá a reintentar y el pool interno sufre).


2) “Todo anda, pero tiempo real / chat / notificaciones no funcionan” (WebSocket mal ruteado)

Síntoma

  • Odoo carga, pero Discuss/Livechat/notificaciones en tiempo real se sienten “muertas”.

  • A veces ves errores en /websocket/ o simplemente no hay updates.

Causa raíz

  • Reverse proxy (Nginx/Apache) no está redirigiendo /websocket/ al puerto gevent (gevent_port, típico 8072).

  • En multi-worker esto es un clásico: el HTTP va a 8069, pero WebSocket debe ir separado. Odoo+2Full Stack Web Dev & Designer Expert+2

Cómo se resolvió

  • Arreglar proxy:

    • / → upstream Odoo HTTP (8069)

    • /websocket/ → upstream gevent (8072)

  • Confirmar que Odoo tiene gevent_port configurado.

Cómo evitar que vuelva

  • Checklist de deploy: “¿/websocket está ruteando al upstream correcto?”

  • Healthcheck específico a /websocket/ (si tu stack lo permite) además del /web.


3) “Con PgBouncer en transaction pooling, el bus/NOTIFY/LISTEN se rompe”

Síntoma

  • “Tiempo real” inconsistente: a veces llega, a veces no.

  • Notificaciones que dependen de LISTEN/NOTIFY se comportan raro.

Causa raíz

  • LISTEN/NOTIFY no funciona bien con pool_mode=transaction, porque transaction pooling rompe expectativas de sesión: el cliente no conserva la misma conexión server. Esto se discute abiertamente en el ecosistema PgBouncer. GitHub+2PgBouncer+2

Cómo se resolvió (patrones que sí funcionan)

  • Mantener PgBouncer en transaction para el 95–99% del tráfico (ORM normal).

  • Para el componente que necesita sesión persistente:

    • bypass directo a PostgreSQL, o

    • PgBouncer aparte en pool_mode=session solo para ese caso.

Cómo evitar que vuelva

  • Documentar internamente: “transaction pooling ≠ features de sesión”.

  • Si tu Odoo depende de mecanismos real-time, planear esa excepción desde el inicio.


4) “La app se pone lenta en oleadas”: cl_waiting sube en PgBouncer

Síntoma

  • Usuarios se quejan de lentitud “por rachas”.

  • No siempre hay error; solo tarda.

Señal clave

En PgBouncer, SHOW POOLS; y ves:

  • cl_waiting creciendo (clientes esperando conexión server).

GitLab lo resume perfecto: cl_waiting indica que clientes intentan ejecutar una transacción pero PgBouncer no pudo asignarles una conexión server de inmediato; si crece, suele ser porque las conexiones están siendo “hogged” (transacciones largas) o el pool quedó chico. Runbooks

Causa raíz típica

  • Un cron pesado o import masivo abrió transacciones largas y “secuestró” conexiones server.

  • O simplemente default_pool_size demasiado bajo para el pico real.

Cómo se resolvió

  • Primero identificar si hay transacciones largas/locks (Postgres).

  • Si era pico legítimo y Postgres estaba cómodo: subir default_pool_size y/o usar reserve_pool_size.

  • Si era transacción larga: reescribir el job a batching (lotes más pequeños) para liberar conexiones más rápido.

Cómo evitar que vuelva

  • Alerta temprana: cl_waiting > 0 sostenido.

  • Alertar también por transacciones > X segundos (Postgres), porque eso predice cl_waiting.


5) “PgBouncer no reutiliza conexiones / comportamiento raro con statements”: prepared statements + transaction pooling

Síntoma

  • Conexiones que parecen “raras”: más churn, errores o comportamiento no esperado con ciertos drivers.

  • A veces se ve como “no reusa, crea nuevas”, o errores difíciles de reproducir.

Causa raíz

  • PgBouncer en pool_mode=transaction no soporta bien ciertas expectativas de sesión, y eso incluye casos con prepared statements, especialmente con algunos clientes/drivers. Esto se menciona frecuentemente en troubleshooting de producción. Stack Overflow+1

Cómo se resolvió

  • Si el driver dependía fuertemente de prepared statements “a nivel sesión”, se evaluó:

    • desactivarlos del lado del cliente (si aplica), o

    • usar pool_mode=session para ese servicio específico (no para todo Odoo), o

    • separar workloads (servicio A por transaction, servicio B por session).

Cómo evitar que vuelva

  • No mezclar “todo por session” por miedo: session pooling es más caro.

  • Pero tampoco forzar transaction pooling donde el cliente no coopera (la propia doc de PgBouncer lo advierte). PgBouncer+1


6) El patrón que más se repite: “arreglamos con métricas, no con fe”

Cuando un incidente aparece, hay un orden que casi siempre ahorra tiempo:

  1. PgBouncer → SHOW POOLS;

    • ¿hay cl_waiting?

  2. Si hay cola: ¿es por pool chico o por “conexiones secuestradas”?

    • GitLab recomienda investigar long-running queries/transactions si cl_waiting sube. Runbooks

  3. Postgres → pg_stat_activity

    • ¿hay transacciones largas o locks?

  4. Recién después ajustar:

    • default_pool_size / reserve_pool_size (si es demanda legítima)

    • crons/batching (si es transacción larga)

    • proxy/websocket (si es real-time)


Cierre

La mayoría de “errores raros” en Odoo + PgBouncer no son raros: son dos o tres gotchas repetidos:

  • pool interno de Odoo insuficiente para el pico real,

  • websocket mal ruteado,

  • y transaction pooling usado en features que necesitan sesión,

  • más crons con transacciones largas secuestrando conexiones.


Siguiente capítulo ->

Errores reales de producción (Odoo + PgBouncer + PostgreSQL) y cómo se resolvieron
John Wolf 5 de diciembre de 2025
Compartir
Etiquetas
Archivo
Iniciar sesión dejar un comentario