pgBouncer funciona muy bien pero ocasionalmente no está disponible

9

Estoy ejecutando pgBouncer frente a una base de datos de Postgres 9 ocupada. La mayor parte del tiempo funciona bien. Pero cada pocas horas recibiré un correo electrónico de error de mi aplicación con una excepción de psycopg2:

OperationalError ('no se pudo conectar al servidor: no se puede asignar la dirección solicitada ¿El servidor se ejecuta en el host "neo-hulk" y acepta conexiones TCP / IP en el puerto 6432?')

Esta es una aplicación de Python con un montón de trabajadores de apio ejecutando tareas. Cuando llegan esos errores, verifico el pgbouncer db y el tamaño del grupo está dentro de los límites. Después de experimentar un poco, configuré el tamaño máximo del grupo en 400 y el tamaño del grupo en 200. El modo de grupo es "sesión" (las solicitudes son en su mayoría de confirmación automática, casi no hay transacciones).

¿Qué hace que pgBouncer 'desaparezca' así? es solo por cortos períodos de tiempo (y en total estamos hablando de una pequeña cantidad de solicitudes en comparación con el gran volumen de solicitudes que se están entregando) pero esas solicitudes que fallan son importantes.

¡Gracias!

Harel
fuente
Sistema operativo y versión? Versión del kernel si Linux? Versiones exactas de PostgreSQL y PgBouncer? ¿Has ejecutado PgBouncer en el nivel de registro de depuración y has visto si informa algo útil?
Craig Ringer
Debian 6. Linux versión 2.6.32-5-amd64 (Debian 2.6.32-48squeeze1) pgbouncer versión 1.5.4 Postgres 9.1. El registro no registra la conexión / desconexión, ya que pensé que era un poco demasiado, pero no hay errores presentes cuando se lanzan esos errores de la aplicación. El error proviene de psycopg2 pensando que no hay un servidor de base de datos con quien hablar, aunque este problema no existía antes del pgbouncer
Harel
1
Hm, tan actual PgBouncer, y el núcleo es antiguo pero bastante estable. Creo que debe habilitar un registro más detallado en PgBouncer con -vvvy ver si puede hacer coincidir la salida de registro anómala con sus errores a tiempo.
Craig Ringer
Hice un "set verbose = 1; reload;" en el shell pgbouncer y no pudo encontrar nada fuera de lo común en el registro. este es un sistema de producción, por lo que no se pudo detener el servicio para que no se ejecute como demonio con -vvv. Ojalá tenga el mismo resultado. tenga en cuenta que el error sugiere que no pudo conectarse a pgbouncer en absoluto, es decir, no pudo encontrarlo escuchando en ese puerto. Hay miles de conexiones hechas todo el tiempo y es extraño que un pequeño número de ellas falle así.
Harel
Difícil; suena como una posible condición de carrera, pero en qué / dónde ...
Craig Ringer

Respuestas:

15

La parte " No se puede asignar la dirección solicitada " en el mensaje de error proviene de la pila TCP del núcleo. Cuando se encuentra de forma intermitente, esto generalmente significa que el espacio de los sockets disponibles se agota debido a demasiados sockets en estado de espera ( TIME_WAITo menos probable FIN_WAIT_1o FIN_WAIT_2)

El rango de puertos de socket se puede generar mediante cat /proc/sys/net/ipv4/ip_local_port_range. El valor predeterminado en un núcleo Linux de stock es generalmente 32768 61000.

Puede verificar el resultado netstat -ton|grep WAITen el cliente (s) y en el host de pgBouncer cuando el sistema está ocupado. La -obandera mostrará los contadores de tiempo de espera relacionados con los estados de espera.

Si el número total de sockets TCP está cerca, 61000-32768=28232entonces el agotamiento de este rango es probablemente su problema. Dado que un socket cerrado pasa 60 segundos en TIME_WAITestado normal, si un host cliente se conecta más de 28232 veces en un minuto, las nuevas conexiones fallarán con el error mencionado hasta que se liberen los puertos.

Como primera solución, el rango de puertos TCP puede extenderse:

 # echo "1025 65535" >/proc/sys/net/ipv4/ip_local_port_range

Si no es satisfactorio, verifique las banderas tcp_tw_recycley tcp_tw_reuse, también ajustables a través de /proc/sys/net/ipv4y sysctl.

Se definen como (desde man tcp):

       tcp_tw_recycle (booleano; predeterminado: deshabilitado; desde Linux 2.4)
              Habilite el reciclaje rápido de sockets TIME_WAIT. Habilitando esto
              No se recomienda esta opción ya que esto causa problemas cuando se trabaja
              ing con NAT (traducción de direcciones de red).

       tcp_tw_reuse (booleano; predeterminado: deshabilitado; desde Linux 2.4.19 / 2.6)
              Permita reutilizar los zócalos TIME_WAIT para nuevas conexiones cuando sea
              seguro desde el punto de vista del protocolo. No debe cambiarse sin
              Asesoramiento / solicitud de expertos técnicos.

Personalmente tuve éxito tcp_tw_recyclecuando me enfrenté a este problema con una aplicación cliente MySQL, pero no tome esto como una recomendación, ya que mi comprensión de TCP es superficial en el mejor de los casos.

Daniel Vérité
fuente
1
Esa respuesta muestra cualquier error de comprensión superficial de TCP. Gracias por eso. Aumenté el rango de puertos y lo dejé correr por un tiempo para ver si tiene algún efecto. (¿Necesito reiniciar después de configurarlo?)
Harel
Creo que el aumento del puerto lo ha logrado. Hasta ahora no he recibido ningún error. Un recuento aproximado de las líneas de netstat muestra cerca de 20K en el cliente, por lo que desde allí hasta el límite predeterminado de 28K no es largo. ¡Gracias por eso!
Harel
1
¡Bueno! ¿Quieres poner el escenario en el /etc/sysctl.confque net.ipv4.ip_local_port_range = 1025 65535tener que persista tras los reinicios.
Daniel Vérité
Gracias. He recibido errores desde entonces, pero no ese, así que todavía es bueno. Dejarlo funcionar durante unos días y hará que la permanente cambie. Me alegro de que esto hasta ahora parezca funcionar porque los otros cambios me dan miedo :)
Harel