Repensar Postgres en un mundo post-servidor

Las arquitecturas sin servidor han aportado a los equipos de ingeniería una gran cantidad de beneficios. Obtenemos implementaciones más simples, escala automática e infinita, mejor simultaneidad y una superficie API sin estado. Es difícil imaginar volver al mundo de los servicios administrados, los entornos locales rotos y el SSH en servidores. Cuando comencé a hacer desarrollo web, pasar de los servidores en un armario al espacio en rack fue una revolución.

No es sólo el alojamiento y la forma en que implementamos las aplicaciones lo que ha cambiado bajo este nuevo paradigma. Estas ventajas de la tecnología sin servidor han presentado desafíos a la arquitectura MVC tradicional que ha sido tan ubicua. Recordando esos primeros días, marcos como Zend, Laravel, Django y Rails fueron increíbles impulsores de la productividad. No sólo influyeron en la forma en que creamos aplicaciones, sino también en la forma en que pensamos acerca de la resolución de problemas con la web. Estos fueron sus “monolitos majestuosos” y solidificaron el patrón MVC como el estándar de facto para la mayoría de las aplicaciones web que utilizamos hoy.

En muchos sentidos, el auge de los microservicios y con él la idea de arquitecturas hexagonales (también conocidas como puertos y adaptadores) condujeron naturalmente a este nuevo mundo sin servidores. Comenzó creando y alojando API independientes organizadas por un contexto compartido que todavía estaban respaldadas por los marcos clásicos que ya conocíamos y amábamos.

La popularidad de NodeJS llevó al marco rápido donde ahora teníamos un micromarco menos rígido que nos permitía organizar nuestro código de manera más flexible. La metamorfosis final de este patrón son piezas individuales de lógica empresarial que se pueden ejecutar bajo demanda en la nube. Ya no administramos máquinas ni siquiera múltiples servidores API. Cada pieza de lógica empresarial sólo existe cuando se necesita, durante el tiempo que se necesita y ya no. Son livianos y solo paga por las partes individuales de su aplicación que se utilizan.

Hoy en día, apenas nos damos cuenta de que existe un servidor; incluso la terminología, sin servidor, es un nombre inapropiado diseñado para resaltar lo lejos que hemos llegado desde los días en que se configuraban XAMP o VirtualBox y Vagrant. Los beneficios son claros, las horas ahorradas, los dolores de cabeza evitados y la libertad de resolver problemas comerciales simplemente con código acercan más que nunca la creación de software al simple acto de escribir prosa.

El problema

Los marcos MVC clásicos codificaban no sólo el patrón de trabajo en tres niveles distintos (datos, aplicación y presentación), sino también la tecnología para cada uno. Pudo elegir algunas opciones en cada capa, por ejemplo Postgres o MySQL como capa de datos, pero la idea general es que estas decisiones se toman por usted. Adoptas implícitamente la idea de convención sobre configuración.

Postgres como solución de capa de datos tiene mucho sentido. Es robusto, rápido, admite transacciones ACID y cuenta con más de treinta años de desarrollo a sus espaldas. También es de código abierto, puede alojarse en casi cualquier lugar y es probable que siga existiendo durante otros treinta años. Podría hacer cosas mucho peores que apostar el futuro de su empresa en Postgres. Agregue a eso todo el trabajo realizado para integrarlo en estos marcos igualmente probados en batalla y la historia para elegir Postgres se vuelve muy sólida.

Sin embargo, cuando entramos en un contexto sin servidor, este tipo de arquitectura presenta una serie de desafíos, especialmente cuando se trata del manejo de nuestros datos.

Los problemas comunes incluyen:

  1. Mantener conexiones con estado: cuando cada usuario es una nueva conexión a Postgres, esto puede maximizar la cantidad de conexiones que Postgres puede manejar rápidamente.
  2. Escala aprovisionada: con Postgres debemos asegurarnos de aprovisionar la base de datos del tamaño correcto para nuestra aplicación con anticipación, lo cual no es ideal cuando nuestra capa de aplicación puede escalar automáticamente a cualquier carga de trabajo.
  3. Modelo de seguridad tradicional: este modelo no permite ningún uso del lado del cliente y es vulnerable a ataques de inyección SQL.
  4. Centralización de datos: si bien nuestra aplicación puede implementarse globalmente, esto es de poca utilidad cuando nuestra base de datos está atrapada en una única ubicación, potencialmente a miles de kilómetros de donde deben estar los datos.
  5. Alta sobrecarga operativa: la tecnología sin servidor promete liberarnos de la complejidad y eliminar barreras para resolver problemas comerciales. Con Postgres volvemos a tener que gestionar un servicio nosotros mismos, ocupándonos de la fragmentación, el escalado, la distribución y las copias de seguridad.

Los sistemas tradicionales como Postgres nunca fueron diseñados para este propósito. Para empezar, Postgres opera bajo el supuesto de una conexión con estado. Lo que esto significa es que Postgres mantendrá abierta una conexión con un servidor para optimizar el tiempo de respuesta. En una aplicación monolítica tradicional, si su servidor tuviera que abrir una nueva conexión cada vez que solicitara datos, esto sería bastante ineficiente. La solicitud de red real sería en muchas ocasiones el principal cuello de botella. Al mantener esta conexión en caché, Postgres elimina este cuello de botella. A medida que escala su aplicación, es probable que tenga varias máquinas ejecutándose y una sola base de datos de Postgres puede manejar muchas de esas conexiones, pero este número no es infinito. De hecho, en muchos casos, debe establecer este número al momento de aprovisionar la base de datos.

En un contexto sin servidor, cada solicitud es efectivamente una nueva máquina y una nueva conexión a la base de datos. Cuando Postgres intenta mantener abiertas estas conexiones, rápidamente podemos toparnos con nuestro límite de conexión y los límites de memoria de la máquina. Esto también introduce otro problema con el caso de uso tradicional de Postgres, que son los recursos aprovisionados.

Con Postgres tenemos que decidir el tamaño de la base de datos, la capacidad de la máquina en la que se ejecuta, dónde se encuentra esa máquina y el límite de conexiones en el momento de la creación. Esto nos coloca en una situación en la que nuestra aplicación puede escalar automáticamente pero debemos vigilar de cerca nuestra base de datos y escalarla nosotros mismos. Esto puede ser aún más complicado cuando nos enfrentamos a picos de tráfico que no son consistentes ni en el tiempo ni en el lugar. En última instancia, al pasar a la tecnología sin servidor, hemos reducido la sobrecarga operativa de nuestra capa de aplicación, pero hemos creado una mayor sobrecarga operativa en nuestra base de datos. ¿No sería mejor si tanto nuestra aplicación como nuestra capa de datos pudieran escalar juntas sin que tuviéramos que administrarlas?

La complejidad requerida para que los sistemas tradicionales como Postgres funcionen en un entorno sin servidor a menudo puede ser suficiente para abandonar la arquitectura por completo. La tecnología sin servidor requiere la ejecución de la lógica empresarial sin estado y bajo demanda. Esto nos permite crear programas más ligeros y escalables, pero no nos permite conservar cosas como las conexiones de red y puede verse ralentizado por dependencias adicionales como ORM y middleware.

La solución ideal

Es hora de que comencemos a pensar en un nuevo tipo de base de datos, una que esté más en línea con el espíritu de la tecnología sin servidor y que abrace el desarrollo iterativo y herramientas más unificadas. Queremos que esta base de datos tenga la misma escala automática bajo demanda que el resto de nuestra aplicación, así como que maneje la distribución global, que son el sello distintivo de la promesa sin servidor. Esta solución ideal debería ser:

  1. Soporte para conexiones sin estado y sin límites.
  2. Autoescalado tanto para el tamaño de la máquina como en el tamaño de la propia base de datos.
  3. Ser accesible de forma segura tanto desde el cliente como desde el servidor para admitir API sin servidor y casos de uso de Jamstack.
  4. Distribuidos globalmente para que los datos estén siempre más cerca de donde se necesitan.
  5. Libre de gastos operativos, por lo que no agregamos complejidad al administrar aspectos como fragmentación, distribución y copias de seguridad.

Si realmente queremos adoptar la arquitectura sin servidor, debemos asegurarnos de que nuestra base de datos escale junto con el resto de la aplicación. En este caso, tenemos una variedad de soluciones, algunas de las cuales implican seguir con Postgres. Amazon Aurora es un ejemplo de una solución en la nube de Postgres que nos brinda escalabilidad y copias de seguridad automáticas, y nos brinda cierta distribución global de datos. Sin embargo, Amazon Aurora no es fácil de configurar y no nos libera de todos los gastos operativos. Tampoco podemos acceder de forma segura a nuestros datos desde el cliente sin crear una API adjunta, ya que todavía sigue el modelo de seguridad tradicional de Postgres.

Otra opción aquí son servicios como Hasura, que nos permiten aprovechar Postgres pero acceder a nuestros datos a través de una API GraphQL. Esto resuelve nuestros problemas de seguridad al acceder a los datos del cliente y nos acerca mucho más a la facilidad de uso que tenemos con muchos de nuestros otros servicios sin servidor. Sin embargo, debemos administrar nuestra base de datos nosotros mismos y esto simplemente agrega otra capa encima de nuestra base de datos para administrar la seguridad. Si bien la capa de aplicación de Hasura está distribuida, nuestra base de datos no lo está, por lo que no obtenemos una verdadera distribución global con este sistema.

Creo que en este punto deberíamos buscar algunas soluciones adicionales que realmente atiendan todos los puntos anteriores. Cuando buscamos soluciones fuera de Postgres, tenemos que agregar dos requisitos adicionales que ponen las soluciones a la par con el poder de Postgres:

  1. Soporte para transacciones ACID robustas y distribuidas.
  2. Soporte para modelado relacional de modo que podamos realizar fácilmente operaciones de unión en datos normalizados.

Cuando normalmente salimos de los sistemas de bases de datos relacionales y entramos en el mundo de las soluciones sin esquemas, las transacciones ACID y los datos relacionales y normalizados son a menudo cosas que sacrificamos. Por eso queremos asegurarnos de que cuando optimizamos para sistemas sin servidor no perdamos las características que han convertido a Postgres en un competidor tan fuerte durante tanto tiempo.

CosmosDB de Azure admite una variedad de bases de datos (tanto SQL como NoSQL) internamente. CosmosDB también nos proporciona bibliotecas que pueden funcionar tanto en el cliente como en el servidor liberándonos de una dependencia adicional como Hasura. Obtenemos cierta distribución global y escala automática. Sin embargo, todavía tenemos muchas opciones que tomar y no estamos completamente libres de la gestión de bases de datos. Todavía tenemos que administrar el tamaño de nuestra base de datos de manera efectiva y elegir entre muchas opciones de bases de datos que tienen sus pros y sus contras.

Lo que realmente queremos es una solución totalmente administrada donde se pueda abstraer la sobrecarga operativa de elegir el tamaño y el tipo de base de datos. En un sentido general, tener que investigar muchos tipos de bases de datos y estimar la escala serían cosas que importarían mucho menos si tuviéramos todas las funciones que necesitamos. Fauna es una solución en la que no tenemos que preocuparnos por el tamaño de la base de datos ni tenemos que seleccionar el tipo de base de datos bajo el capó. Obtenemos el soporte de transacciones ACID, distribución global y sin pérdida de datos sin tener que encontrar la mejor tecnología subyacente para lograrlo. También podemos acceder libremente a nuestra base de datos en el cliente o en el servidor con soporte completo para funciones sin servidor. Esto nos permite crear de manera flexible diferentes tipos de aplicaciones en una variedad de arquitecturas, como clientes JAMstack, API sin servidor, backends tradicionales de larga duración o combinaciones de estos estilos.

Cuando se trata de bases de datos sin esquema, ganamos flexibilidad pero nos vemos obligados a pensar de manera diferente sobre nuestro modelo de datos para consultar nuestros datos de manera más eficiente. Al alejarse de Postgres, este suele ser un punto de fricción sutil pero importante. Con Fauna , tenemos que pasar a un diseño sin esquema, ya que no se puede optar por otro tipo de base de datos. Sin embargo, Fauna utiliza un modelo de base de datos relacional de documentos único. Esto nos permite utilizar conocimientos y principios de bases de datos relacionales al modelar nuestros datos en colecciones e índices. Es por eso que creo que vale la pena considerarlo para las personas acostumbradas a Postgres, ya que la sobrecarga mental no es la misma que con otras opciones de NoSql.

Conclusión

Sistemas como Postgres han sido poderosos aliados para crear aplicaciones durante más de treinta años. El auge de los ciclos de desarrollo ágiles e iterativos nos llevó a la revolución sin servidor que tenemos ante nosotros hoy. Somos capaces de crear aplicaciones cada vez más complejas con menos gastos operativos que nunca. Ese poder requiere que pensemos en las bases de datos de manera diferente y exijamos un nivel similar de flexibilidad y facilidad de gestión. Queremos preservar las mejores cualidades de Postgres, como las transacciones ACID, pero deshacernos de los aspectos más desagradables de trabajar con la base de datos, como la agrupación de conexiones, el aprovisionamiento de recursos, la seguridad, la distribución y la gestión de escala, disponibilidad y confiabilidad.

Soluciones como Aurora Serverless v2 de Amazon crean una solución sin servidor que funciona en este nuevo mundo. También hay soluciones como Hasura que se suman a esto para cumplir aún más la promesa de la tecnología sin servidor. También tenemos soluciones como Cosmos DB y Fauna que no están basadas en Postgres pero que están diseñadas para funcionar sin servidor y al mismo tiempo admiten funciones importantes de Postgres.

Si bien Cosmos DB nos brinda mucha flexibilidad en términos de qué base de datos utilizamos, todavía nos deja con muchas decisiones y no está completamente libre de gastos operativos. Fauna lo ha hecho para que usted no tenga que comprometerse con las transacciones ACID, el modelado relacional o los datos normalizados, y al mismo tiempo alivia toda la sobrecarga operativa de la administración de bases de datos. Fauna es un replanteamiento completo de una base de datos que está realmente libre de gastos operativos. Al combinar lo mejor del pasado con las necesidades del futuro, Fauna ha creado una solución que se comporta más como una API de datos y se siente natural en un futuro sin servidor.


Siga a Michael Rispoli en Twitter

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir