En abril de 2021 hice mi primer commit en el monorepo de la plataforma cloud-native de CARTO. Era el PR número 9, un docker-compose. Cinco años después he mirado hacia atrás con calma y me he encontrado con unos 450 commits, unos 447 pull requests y presencia en prácticamente todos los servicios del repositorio.
Lo interesante no son las cifras. Lo interesante es que, revisando ese historial, aparece un hilo conductor que yo mismo no había identificado del todo: he pasado media década conectando la plataforma a data warehouses ajenos. Seis proveedores distintos —BigQuery, Snowflake, Redshift, Databricks, Oracle y PostgreSQL—, cada uno con su propio modelo de credenciales, su pooling, sus timeouts y sus mensajes de error.
Este artículo es lo que he sacado en claro de ese trabajo. No es una guía, es más bien el poso de repetir el mismo problema seis veces y descubrir que solo cambia la superficie.
Conectar a una base de datos ajena no es “conectar a una base de datos”
Cuando escribes una conexión a PostgreSQL en tu propio proyecto, hay un montón de decisiones que ni te planteas: sabes la versión, controlas la red, tienes las credenciales, y si algo falla lo ves en tus logs.
Nada de eso aplica cuando la conexión la configura un cliente contra su propia infraestructura. El warehouse no es tuyo. La credencial es del cliente y puede caducar, rotarse o revocarse sin avisar. La latencia depende de una red que no controlas. El esquema puede tener diez tablas o diez mil. Y cuando algo falla, el error que recibes viene de un sistema que no puedes inspeccionar.
Esto convierte lo que parece una tarea de fontanería en un problema de diseño bastante más profundo. La superficie es siempre la misma —“guarda estas credenciales y ejecuta esta query”—, pero debajo cada proveedor tiene su propia personalidad.
El modelo de credenciales es donde está la verdadera diferencia
Si tuviera que quedarme con un solo aprendizaje, sería este: lo que distingue a un data warehouse de otro no es el dialecto SQL, es cómo demuestra quién eres.
El SQL se parece bastante entre proveedores. Las diferencias existen, pero son manejables. La autenticación, en cambio, es donde cada uno ha tomado decisiones radicalmente distintas, y donde te encuentras los problemas que de verdad cuestan.
Un recorrido rápido por lo que me he encontrado:
Service accounts con clave. El modelo clásico: el cliente te da un JSON con una clave privada y tú la guardas. Funciona, es sencillo de implementar, y es exactamente lo que a cualquier responsable de seguridad le quita el sueño. Estás almacenando una credencial de larga duración que da acceso a los datos de otra empresa.
Workload Identity Federation. La respuesta a lo anterior, y un cambio de mentalidad importante. En lugar de guardar una clave, tu servicio presenta su propia identidad y el proveedor decide si confía en ella. No hay secreto que almacenar, ni que rotar, ni que filtrar. Implementarlo cuesta más —hay que configurar la relación de confianza en ambos lados— pero elimina una categoría entera de riesgo.
OAuth en dos sabores. Aquí hay una distinción que tardé en interiorizar y que conviene tener clara desde el principio: no es lo mismo user-to-machine que machine-to-machine. En el primero, la conexión actúa en nombre de una persona concreta, hereda sus permisos y muere cuando esa persona pierde el acceso. En el segundo, la conexión tiene identidad propia. Elegir mal significa que un mapa publicado deja de funcionar el día que alguien cambia de departamento.
Key-pair authentication. Un punto intermedio: criptografía asimétrica en vez de contraseña, sin la complejidad de federar identidades. Buen equilibrio cuando el proveedor no ofrece federación.
La conclusión práctica es que el modelo de autenticación hay que elegirlo antes de escribir la primera línea, porque condiciona todo lo demás: dónde vive el secreto, qué pasa cuando caduca, quién puede usar la conexión y qué ocurre cuando ese alguien deja la empresa.
Los tokens caducan en el peor momento posible
De todos los bugs que he tocado en estos años, los relacionados con tokens en caché son los que más me han enseñado.
El patrón es siempre el mismo. Cacheas un token para no pedir uno nuevo en cada petición —razonable, es una llamada de red cara—. El token caduca. Tu caché no se entera. A partir de ahí sirves un token muerto a cada petición que llega, y el error que devuelve el proveedor rara vez dice “tu token ha caducado”: dice algo genérico sobre permisos o sobre la conexión.
Lo que aprendí de esto no es “invalida la caché”, que es evidente. Es algo más sutil: el evento que invalida un token no siempre ocurre en tu sistema. Cuando un usuario vuelve a autenticarse en el proveedor, tu caché no recibe ninguna notificación. Sigue tan contenta con su copia obsoleta. Cualquier diseño que asuma que verá pasar todos los cambios de estado relevantes está construido sobre una premisa falsa.
La lección general, que va mucho más allá de los tokens: cuando cacheas algo cuyo ciclo de vida controla un tercero, la invalidación por tiempo no basta. Necesitas un camino para detectar que lo cacheado ya no sirve, y recuperarte sin que el usuario tenga que hacer nada.
Los errores no controlados son el enemigo real
Hay un patrón que he repetido tantas veces en estos cinco años que se ha convertido casi en un reflejo: convertir un 500 no controlado en una respuesta correcta.
Parece un detalle menor. No lo es. Un 500 significa que tu servicio no supo qué hacer con una situación, y en una plataforma multi-tenant eso tiene consecuencias que van más allá de la petición que falló.
Cuando una excepción del driver de un proveedor sube sin capturar, se lleva por delante cosas que no tienen nada que ver: puede tumbar el proceso que atiende a otros clientes, o dejar conexiones colgadas en el pool. El fallo de un cliente concreto, con su warehouse concreto mal configurado, acaba degradando el servicio de todos los demás.
Por eso el manejo de errores específico por proveedor no es cosmética. Cada driver tiene su propia taxonomía de fallos —timeouts, permisos, esquemas que no existen, cuotas superadas— y traducirlos a respuestas HTTP correctas es lo que mantiene el aislamiento entre inquilinos.
Además tiene un efecto secundario que no esperaba: cuando los errores están bien clasificados, el soporte deja de escalarte tickets. Un mensaje que dice “el usuario no tiene permisos sobre este esquema” lo resuelve el propio cliente. Un 500 genérico acaba siempre en tu bandeja de entrada.
Listar esquemas es más difícil de lo que parece
Un ejemplo pequeño que ilustra bien lo que decía sobre no controlar el otro lado.
Para que alguien elija una tabla, primero hay que enseñarle qué tablas hay. La forma obvia de hacerlo es recorrer la jerarquía y preguntar por cada objeto. Funciona perfectamente cuando el cliente tiene veinte tablas.
Con un cliente que tiene decenas de miles, ese mismo código convierte una pantalla de selección en una operación de varios minutos, o directamente en un timeout. Y no puedes hacer nada desde tu lado: el coste está en el warehouse ajeno.
La solución pasa por dejar de preguntar objeto a objeto y usar las vistas de metadatos del proveedor, que devuelven el catálogo completo en una sola query. Es más trabajo, porque cada proveedor expone esos metadatos de forma distinta.
Lo que me llevo de aquí es una regla que aplico ya casi sin pensar: cualquier operación cuyo coste crezca con datos que no controlas es un incidente esperando su momento. Funcionará en desarrollo, funcionará en la demo, y fallará con el cliente más grande, que suele ser el que menos ganas tienes de que le falle algo.
Cinco años, una conclusión
Si tuviera que resumir todo esto en una idea, sería que la integración con sistemas ajenos es fundamentalmente un ejercicio de humildad. Tu código es la parte pequeña. La parte grande es todo lo que ocurre al otro lado del cable: credenciales que caducan, esquemas que crecen, redes que van lentas, proveedores que cambian su API.
El trabajo bien hecho aquí no se nota. Nadie escribe para agradecerte que su conexión a Snowflake lleve dos años funcionando sin intervención. Se nota justo lo contrario, y por eso la mayor parte del esfuerzo se va en los caminos que nunca deberían ejecutarse.
Cuando empecé pensaba que conectar a seis data warehouses distintos sería seis veces el mismo trabajo. Resultó ser una vez el mismo trabajo y seis veces un problema distinto de autenticación.










