Ingeniería Artículo técnico 8 min de lectura

Optimización de latencia en la firma de documentos XML masivos

Cuando emites miles de comprobantes en poco tiempo, «firmar más rápido» suele ser la primera idea. Pero antes de optimizar hay que saber dónde se va el tiempo. Aquí cómo medir, qué optimizar y qué dejar en paz.

DÓNDE SE VA EL TIEMPO p50 · p95 · p99

  • XML Generar el XML CPU LIGERO
  • RSA Firmar CPU PESADO
  • HTTP Enviar a recepción RED
  • SRI Esperar la autorización ESPERA EXTERNA
PRIMERO Mide cada etapa por separado antes de optimizar.
01 — MEDICIÓN

Primero, mide: ¿dónde se va el tiempo?

Cada comprobante pasa por etapas muy distintas:

ETAPATIPO DE TRABAJOSE ACELERA CON
Generar el XMLCPU, ligeroRara vez es el problema
FirmarCPU, pesadoMás núcleos, reutilizar el certificado cargado
Enviar a recepciónRed y esperaConcurrencia controlada
Esperar la autorizaciónEspera externaConsultar sin bloquear, webhooks

Si no mides cada etapa por separado, vas a optimizar la equivocada. Registra el tiempo de cada una y mira los percentiles p50, p95 y p99, no solo el promedio: los comprobantes lentos son los que alargan tu proceso.

02 — BENCHMARK

Mide el costo de firmar en tu infraestructura

Este script mide solo la operación RSA, sin XAdES ni red. Te da una referencia de cuánto cuesta firmar en tu servidor:

jsbenchmark-firma.js
import crypto from "node:crypto";

const { privateKey } = crypto.generateKeyPairSync("rsa", { modulusLength: 2048 });
const xml = Buffer.from("<factura>...</factura>".repeat(50));
const N = 500;

const inicio = process.hrtime.bigint();
for (let i = 0; i < N; i++) {
  crypto.sign("sha256", xml, privateKey);
}
const ms = Number(process.hrtime.bigint() - inicio) / 1e6;

console.log(`${(ms / N).toFixed(2)} ms por firma (solo la operación RSA, sin XAdES ni red)`);

El número depende de tu hardware, así que mídelo tú. Si firmar cuesta una fracción pequeña del tiempo total, el cuello de botella está en otro lado.

03 — OPTIMIZACIONES

Optimizaciones que sí valen la pena

  1. Carga el certificado una sola vez Abrir y descifrar el .p12 en cada firma es trabajo repetido. Cárgalo en memoria y reutilízalo, cuidando su almacenamiento seguro.
  2. Firma en paralelo, con tantos trabajadores como núcleos Firmar usa CPU, así que más trabajadores que núcleos no ayuda. En Node usa worker threads o procesos separados; en otros lenguajes, un pool de hilos.
  3. Limita la concurrencia hacia el SRI Enviar sin límite no es más rápido: satura el servicio y provoca más errores y reintentos. Usa una concurrencia controlada.
  4. Separa las etapas con colas Una cola para firmar, otra para enviar y otra para consultar la autorización. Cada etapa se escala por separado y una lentitud en una no frena a las demás.
  5. Guarda el XML firmado Si un envío falla, reintenta con el mismo XML firmado, no vuelvas a generarlo ni a firmarlo. Ahorras CPU y evitas generar un comprobante distinto.
  6. No esperes la autorización dentro del lote Envía, guarda la clave de acceso y consulta después, o mejor aún, recibe webhooks.
  7. Aplica contrapresión (backpressure) Si la cola de envío crece sin control, frena la etapa anterior en vez de acumular memoria.

Concurrencia controlada hacia el SRI

jsconConcurrencia.js
// Ejecuta `tarea` sobre cada elemento con, como máximo, `limite` ejecuciones a la vez
export async function conConcurrencia(items, limite, tarea) {
  const resultados = new Array(items.length);
  let siguiente = 0;

  async function trabajador() {
    while (true) {
      const i = siguiente++;
      if (i >= items.length) return;
      resultados[i] = await tarea(items[i], i);
    }
  }

  const n = Math.min(limite, items.length);
  await Promise.all(Array.from({ length: n }, trabajador));
  return resultados;
}
04 — CUIDADO

Qué no optimizar sin medir

  • Cambiar de algoritmo o de librería de firma por intuición El formato lo define el SRI.
  • Subir la concurrencia al máximo Más solicitudes simultáneas pueden empeorar la latencia total.
  • Reducir el tamaño del XML a costa de datos obligatorios El esquema es el que es.
05 — ARQUITECTURA

Una arquitectura orientada a eventos

  1. Ventas Cada venta deja un comprobante por firmar.
  2. Cola: por firmar Acumula los comprobantes pendientes de firma.
  3. Trabajadores de firma Tantos como núcleos, con el certificado ya cargado.
  4. Cola: por enviar Guarda el XML firmado listo para enviar.
  5. Envío con concurrencia limitada Protege al SRI y evita reintentos innecesarios.
  6. SRI → estado en tu sistema Por webhook o por consulta de la clave de acceso.

Cada paso es un punto donde puedes medir, escalar y reintentar.

06 — POR API

Qué pasa cuando usas una API

Si firmas y envías a través de una API como Flashpi, parte de este trabajo ya está resuelto del lado del servicio. Tu parte es no bloquear: encola, envía con concurrencia controlada y recibe el estado por webhooks.

Conclusión

Optimizar la latencia es, sobre todo, medir y separar etapas. Con el certificado cargado una vez, firma en paralelo, concurrencia limitada hacia el SRI y colas entre etapas, un lote grande deja de ser un problema.

SIGUE LEYENDO

Del comprobante a la autorización

Ver todos los artículos
Empieza hoy

Solicita tu API key de pruebas

Prueba la API en el ambiente de pruebas del SRI y pasa a producción cuando estés listo.