Serverless y Edge Runtime Parte 2

Compartir esta publicación

Este es el segundo post de una serie de dos partes que explora el mundo de Serverless y Edge Runtime. En el post anterior, nos familiarizamos con serverless; el enfoque principal de este post será el Edge Runtime, dónde puede ser útil y cuáles son sus advertencias.

Ubicación Edge o Edge Runtime

Mientras que definir lo que es serverless era bastante sencillo, con Edge tenemos un poco más de problemas. Cuando se menciona ‘edge’, puede referirse a unos cuantos conceptos bastante diferentes: puede referirse a «ubicación edge», o «tiempo de ejecución edge», o incluso «funciones edge». Repasemos cada uno de ellos y veamos qué significa exactamente.

1. Edge, la ubicación: el concepto de ejecutar servidores más cerca de nuestros usuarios. Si tomamos AWS Lambda, podríamos tener una instancia desplegada en la región us-east-2, lo que significa que ahí es donde nuestro servidor gira y sirve a los usuarios. Sin embargo, si tenemos un usuario de Asia, podemos prever que para él o ella, solo llegar a nuestra función sin servidor tendrá latencia debido a la distancia geográfica. Podemos desplegar otra función serverless en ap-east-1 (ejecutando exactamente el mismo código que en us-east-2) para servir a los usuarios del este, reduciendo la latencia originada por la distancia. Lambda@Edge de AWS encaja en esta categoría; esencialmente estamos desplegando «réplicas» de nuestra función sin servidor, y cada usuario será atendido por la instancia que esté geográficamente más cerca de él.

2. Edge, Runtime: a diferencia de las funciones sin servidor, en las que un servidor real tiene que girar y luego servir la solicitud (por lo tanto, asumimos el coste del arranque en frío), el tiempo de ejecución Edge garantiza un entorno en el que nuestro código puede ejecutarse inmediatamente en una plataforma V8 sin necesidad de poner en marcha ningún servidor nuevo. Se trata de una tecnología diseñada para ser integrada en frameworks, no directamente en aplicaciones; probablemente la más notable sea NextJS, que la utiliza, pero proveedores como Supabase también han creado productos sobre ella. Una gran advertencia a tener en cuenta es que el Edge Runtime utiliza el motor V8 de Javascript bajo el capó, por lo tanto, nuestra aplicación debe estar en Javascript o Typescript y no depender de ningún componente que requiera algún tipo de runtime nativo (C, C++, Rust, etc.).

3. Funciones Edge: son bloques de código, funciones sencillas que se ejecutan en Edge Runtime. Mientras que el Edge Runtime proporciona la plataforma para que la utilicen los frameworks, las Edge Functions son nuestras pequeñas aplicaciones.

  Explaining BEMIT: ITCSS + BEM

Global vs. Regional Edge

Sobre el papel, podría parecer mejor que nuestros servidores/funciones estuvieran lo más cerca posible de los usuarios, ya que seguramente así se reduciría el tiempo de respuesta de las peticiones.

¿Cierto?

La mayoría de las veces, un punto a considerar aquí es dónde reside nuestra base de datos, geográficamente. ¿Tenemos una única instancia de Postgres en el este de Estados Unidos?

Dado que la mayoría de las veces nuestros clientes (con suerte) no consultan directamente la base de datos, sino que lo hacen a través de un servidor backend, esto puede llegar a ser bastante importante. Si este backend está cerca de la base de datos (en la misma región), la primera petición entre el usuario y nuestro backend puede tardar unos milisegundos; sin embargo, si el punto final al que accede el usuario en el servidor realiza varias idas y vueltas a la base de datos (por ejemplo, primero consulta si el usuario está autorizado, luego vuelve a consultar para obtener algunos datos, luego vuelve a consultar para obtener algo más, etc.), dado que el servidor backend está regionalmente cerca de la base de datos, este número `n` de peticiones no introducirá mucha latencia.

Sin embargo, si alejamos nuestro backend de la región de la base de datos y lo situamos lo más cerca posible del usuario, la primera petición inicial entre el usuario y el backend será más rápida, pero luego el servidor tendrá que hacer viajes de ida y vuelta más largos a la base de datos. Esto puede descontrolarse rápidamente si nuestro endpoint realiza un número considerable de consultas a la base de datos. Vercel recomienda en este caso Edge Functions regionales, que se despliegan cerca de las dependencias de la base de datos. Esto significa que nuestra Edge Function podría no estar geográficamente más cerca del usuario, sino de la base de datos. Alternativamente, podríamos incluso utilizar bases de datos distribuidas globalmente.

Vercel empezó a ofrecer también soluciones para estos casos, como Postgres KV, Blob y Edge Config.

Por defecto, las funciones Edge se ejecutarán en todas las regiones del mundo, por lo que este modo de trabajo regional es algo que debemos aceptar y configurar.

Turso

La empresa Turso ofrece una solución interesante para ayudar también con este posible problema: bases de datos libSQL (SQLite) alojadas en el borde, que se despliegan en Fly.io en 26 ubicaciones de todo el mundo. Podemos elegir estratégicamente en qué ubicaciones desplegarlas, procurando que sean las más cercanas a nuestras funciones Edge para reducir la latencia. Funciona mediante la creación de una instancia primaria de nuestra base de datos, que, una vez creada, no se puede mover a una ubicación diferente. A continuación, podemos crear réplicas de la misma, que se sincronizarán con la base de datos primaria. Las operaciones de lectura serán rápidas desde estas réplicas, sin embargo, las operaciones de escritura todavía tendrán que ir a la base de datos primaria.

  Implementación de Google OAuth para utilizar la API de Google en Cloudflare Workers
CTA Software

Ventajas y desventajas

Una vez comprendido lo que son el Tiempo de ejecución Edge y las Funciones Edge, podemos evaluar algunos de sus pros y sus contras.

Empezando por las ventajas:

  • No arranca en frío en absoluto.
  • Cercanía: si tenemos en cuenta el punto anterior sobre no situar nuestro servicio backend lejos de nuestros almacenes de datos, todavía se puede conseguir una gran proximidad.
  • Coste: con serverless, estamos pagando por el equipo que hemos utilizado, pero con las funciones Edge, tendemos a pagar por solicitud. Esto hace que sea difícil comparar el coste, pero anecdóticamente, para la mayoría de los casos de uso, las Edge Functions tienden a ser más baratas, de media.

Consideremos ahora algunos aspectos negativos:

  • Dependencia de V8: Sólo se pueden ejecutar aplicaciones Javascript/Typescript: Vercel menciona Node, y Supabase menciona Deno, como sus entornos de ejecución.
  • Madurez: el tiempo de ejecución y las funciones Edge todavía se consideran tecnologías de vanguardia.

Migración de nuestro conversor JSON-CSV a una función Edge

Al igual que en el post anterior, vamos a tratar de tener una idea de cómo implementar realmente una función Edge. El proceso será muy similar a cómo se aprovisionó nuestro convertidor CSV sin servidor.

Dado que nuestra función serverless de conversión de JSON a CSV fue escrita en Go, no podemos portarla uno a uno al Edge Runtime, en Vercel. Tendremos que convertir nuestro código a TypeScript o Javascript.

Podemos mantener la misma estructura del proyecto con una carpeta «api»; sin embargo, dentro de ella, crearemos un archivo index.ts con el siguiente contenido de ejemplo de Vercel:

export const config = {
  runtime: "edge",
};

export default (request: Request) => {
  return new Response(`Hello, from ${request.url} I'm now an Edge Function!`);
};

El objeto «config» es necesario para indicar que en realidad no se trata de una función sin servidor, sino de una función Edge; si estuviéramos escribiendo una aplicación NextJS, también tendríamos que utilizar una notación similar.

  Volviendo a las raíces: Integración continua del software (primera parte)

Al igual que antes, el comando CLI de vercel deploy se puede utilizar para crear un entorno de ensayo, y vercel --prod para empujar nuestra API a un entorno de producción.

Resumen del despliegue de Vercel

Inspeccionando la compilación y el despliegue en la interfaz de Vercel, podemos confirmar que, efectivamente, nuestro código se despliega como una Edge Function, ejecutándose globalmente (¿recuerdas la diferencia entre regional y global? para nuestro conversor CSV, dado que no es necesaria una conexión a la base de datos, tiene sentido optar por el modelo de despliegue global).

Otro punto a tener en cuenta aquí son los objetos Request y Response; son interfaces Web API estándar de la API Fetch.

Ahora vamos a reescribir el código Go a TypeScript:

export const config = {
  runtime: "edge",
};

interface EmployeeData {
  name: string;
  age: number;
  jobTitle: string;
  badgeNumber: number;
}

export default async (request: Request) => {
  if (request.method !== "POST") {
    return new Response("Only POST requests are allowed.", { status: 405 });
  }

  const res = new Response();

  res.headers.append("Content-Type", "text/csv");
  res.headers.append("Content-Disposition", "attachment; filename=data.csv");

  const rows: string[][] = [];
  rows.push(["Name", "Age", "Job Title", "Badge Number"]);

  let employees: EmployeeData[] = [];

  try {
    employees = await request.json();
  } catch (error) {
    return new Response("Could not parse the JSON payload.", { status: 400 });
  }

  try {
    for (const employee of employees) {
      const row: string[] = [
        employee.name,
        employee.age.toString(),
        employee.jobTitle,
        employee.badgeNumber.toString(),
      ];
      rows.push(row);
    }

    const csvData = rows.map((row) => row.join(",")).join("\n");

    return new Response(csvData);
  } catch (error) {
    return new Response("Could not write CSV data.", { status: 500 });
  }
};

Después de desplegarlo en Vercel, mi URL esta vez es: https://vercel-ts-edge-example.vercel.app/ Al igual que en el post anterior, si decides ejecutar el siguiente curl, podrás convertir los datos de los empleados a un CSV, y esta vez, no deberías experimentar ningún arranque en frío.

curl --location 'https://vercel-ts-edge-example.vercel.app/api' \
--header 'Content-Type: application/json' \
--data '[
    {
        "name": "John Doe",
        "age": 45,
        "jobTitle": "Software Developer",
        "badgeNumber": 58195
    },
    {
        "name": "Jane Doe",
        "age": 32,
        "jobTitle": "Software Developer",
        "badgeNumber": 58191
    }
]'

Referencias

Author

  • DanielAgg 1

    Full stack developer, with 6+ years of experience, building back-, and frontends with various technologies, designing and developing scalable infrastructures on Microsoft Azure.

    Ver todas las entradas

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Suscríbete a nuestro boletín de noticias

Recibe actualizaciones de los últimos descubrimientos tecnológicos

¿Tienes un proyecto desafiante?

Podemos trabajar juntos

apiumhub software development projects barcelona
Secured By miniOrange