Ir al contenido
Ayotl

← Todas las notas

22 de septiembre de 2026 · En Ayotl

«Timeout expired», o por qué la base no se dejaba alcanzar

La cadena de conexión que copia todo el mundo del panel funciona desde tu portátil y no desde tu servidor. La diferencia no está en la contraseña.

Para contar los días de prueba de cada tester, el sitio de la marca tenía que leer la base de otro de los productos, que vive en un proyecto de Supabase distinto. Se copió la cadena que da el panel, se puso en el servidor y la respuesta fue siempre la misma: `conexión: timeout expired`. Ni «contraseña incorrecta» ni «no existe el host»; silencio hasta agotar el tiempo.

$ dig +short A  db.xxxxxxxx.supabase.co     # nada
$ dig +short AAAA db.xxxxxxxx.supabase.co   # 2600:1f14:131e:...
$ dig +short A  aws-1-us-west-2.pooler.supabase.com
44.252.246.120  44.225.139.66  34.215.156.231
La conexión directa sólo existe en IPv6; el pooler tiene IPv4.

Desde un portátil con IPv6 funciona y uno concluye que la cadena es buena. Desde una plataforma que sale por IPv4, no resuelve y se queda esperando. La solución es usar el session pooler, que cambia el usuario y el host pero no la contraseña. Media tarde por un registro DNS que nadie enseña.

Por el camino apareció algo peor y silencioso: el cliente de Postgres emite un evento `error` cuando el handshake falla, y sin nadie escuchándolo Node lo convierte en excepción no capturada y **se lleva el proceso por delante**. El contenedor moría y el proxy devolvía un 502 genérico, que no dice nada del error real. Dos líneas lo arreglan, y la lección es más general: en una integración nueva, lo primero que se escribe no es la consulta, es el manejo del fallo.

Y un hallazgo de regalo: al probar la otra vía, la API REST de ese proyecto llevaba meses caída («could not query the database for the schema cache») y nadie se había enterado, porque esa app se conecta por Postgres directo y nunca la usa. Una integración nueva es también una auditoría gratis de lo que creías sano.

Todas las notas