Latencia GraphQL: Métricas resolver-level para federation

How Contagious is the Coronavirus?
Aprende a medir la latencia por resolver en APIs GraphQL con federation y a usar esos datos para reducir MTTR y cumplir SLA.

Introducción

En los entornos modernos donde Apollo Server se combina con federation, la latencia no es solo una cuestión de tiempo de respuesta global; cada resolver puede convertirse en un cuello de botella oculto. Este artículo muestra, paso a paso, cómo diseñar un proyecto que mida la latencia a nivel de resolver, analice los datos y los convierta en acciones concretas para reducir MTTR y cumplir SLA.

1. Definir el proyecto y la arquitectura

1.1. Alcance y requisitos

  • Identificar los servicios que forman parte de la federación.
  • Establecer umbrales de latencia aceptables por dominio de negocio.
  • Seleccionar una herramienta de observabilidad compatible con APM (por ejemplo, Lescopr).

1.2. Stack tecnológico

  • Apollo Server 4 como gateway.
  • Node.js 20 para los micro‑servicios.
  • Prometheus + Grafana o Lescopr para recolección y visualización.
  • OpenTelemetry para instrumentación automática.

1.3. Entregables iniciales

  1. Repositorio Git con configuración base.
  2. Docker‑compose que levante el gateway y dos servicios federados.
  3. Archivo README.md con instrucciones de despliegue.

2. Instrumentar resolvers con métricas de latencia

2.1. ¿Qué es la latencia a nivel de resolver?

La latencia a nivel de resolver es el tiempo que tarda cada función de resolución en ejecutar su lógica, desde la recepción del request hasta la devolución del dato, incluyendo llamadas a bases de datos o servicios externos.

2.2. Implementación paso a paso

  1. Instalar OpenTelemetry
    npm install @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/instrumentation-graphql
    
  2. Crear un wrapper para resolvers
    const { trace } = require('@opentelemetry/api');
    const tracer = trace.getTracer('lescopr-resolver');
    
    function metricResolver(resolver) {
      return async (parent, args, context, info) => {
        const span = tracer.startSpan(info.fieldName);
        try {
          return await resolver(parent, args, context, info);
        } finally {
          span.end();
        }
      };
    }
    
  3. Aplicar el wrapper
    const resolvers = {
      Query: {
        user: metricResolver(async (_, { id }, ctx) => {
          return await ctx.services.user.getUser(id);
        }),
        product: metricResolver(async (_, { sku }, ctx) => {
          return await ctx.services.inventory.getProduct(sku);
        }),
      },
    };
    
  4. Exportar métricas a Lescopr
    const { CollectorTraceExporter } = require('@opentelemetry/exporter-collector');
    const exporter = new CollectorTraceExporter({ url: 'https://api.lescopr.com/v1/traces' });
    provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
    

2.3. Verificación local

  • Ejecuta npm run start y abre el endpoint /metrics.
  • Usa curl http://localhost:4000/graphql -d '{ user(id: "1") { name } }' y verifica que aparecen métricas resolver_duration_seconds en Prometheus.

3. Analizar métricas y optimizar consultas

3.1. Creación de dashboards

En Lescopr, configura un panel con:

  • Latencia media por resolver (histograma).
  • Percentil 95 para detectar outliers.
  • Series temporales que correlacionen latencia con número de peticiones.

3.2. Identificación de cuellos de botella

  • Busca resolvers con p99 > 150 ms.
  • Revisa llamadas a bases de datos que no usan índices.
  • Detecta dependencias externas que superan el umbral de 200 ms.

3.3. Acciones de optimización

  • Caching: agrega DataLoader para batch y cache de peticiones.
  • Parallelism: usa Promise.all cuando varios resolvers pueden ejecutarse simultáneamente.
  • Refactorizar lógica: divide resolvers monolíticos en sub‑resolvers más ligeros.

3.4. Validación de mejoras

  • Vuelve a generar carga con k6 o Artillery.
  • Compara métricas pre‑ y post‑optimización en el mismo panel de Lescopr.
  • Documenta la reducción de MTTR (por ejemplo, de 12 min a 4 min).

4. Validar, documentar y lanzar

4.1. Pruebas de integración

  • Ejecuta pruebas con jest que verifiquen tiempos máximos por resolver.
  • Integra pruebas de contrato GraphQL (graphql-tester).

4.2. Documentación interna

  • Añade una sección en el README.md que explique cómo leer los dashboards de Lescopr.
  • Incluye ejemplos de alertas basadas en umbrales de latencia.

4.3. Despliegue continuo

  • Configura GitHub Actions para publicar la imagen Docker en GitHub Container Registry.
  • Usa Helm para desplegar en Kubernetes con variables de entorno que apunten al endpoint de Lescopr.

4.4. Monitoreo post‑lanzamiento

  • Establece alertas en Lescopr para p95 > 120 ms en resolvers críticos.
  • Programa revisiones mensuales de los dashboards.

Conclusión

Medir la latencia a nivel de resolver en una arquitectura federada permite identificar con precisión los cuellos de botella que afectan la experiencia del usuario y los acuerdos de nivel de servicio. Con la instrumentación adecuada y los dashboards de Lescopr, los equipos pueden reducir el MTTR, mantener SLA y escalar sin sorpresas.

Para profundizar, la documentación de Lescopr detalla la implementación paso a paso.