Skip to main content
2 jul 2026pruebas estres subida archivos multipart

Por Performate

Pruebas de stress de subida de archivos: límites multipart, timeouts y cuellos de botella en storage

Estresa pipelines multipart con tamaños de payload realistas, subidas concurrentes y simulación de latencia de antivirus.

Tu API devuelve 200 para un avatar de 2 MB en Postman y en producción se derrumba cuando marketing lanza una campaña con recibos PDF de 15 MB. Las rutas de subida multipart rara vez son «otro POST más»: cruzan proxies inversos, WAF, antivirus, SDKs de object storage y workers asíncronos. Una prueba de carga de subida multipart debe estresar cada frontera con tamaños y concurrencia realistas—no un único archivo feliz desde tu laptop.

En esta guía verás por qué el rendimiento de uploads es un problema de pipeline, cómo modelar payloads variados en k6, cuándo simular latencia de escaneo y qué señales deberían frenar un release antes de que los usuarios vean 413 o ACK de un minuto.

Por qué las rutas de subida fallan bajo carga—no en pruebas manuales

Las pruebas funcionales demuestran que el servidor acepta un archivo. Las de carga demuestran que el sistema acepta muchos a la vez:

  • Proxies inversos aplican client_max_body_size o equivalente—los fallos son 413 sin línea en logs de aplicación.
  • Servidores de aplicación bufferizan partes multipart en memoria; subidas grandes concurrentes disparan heap y pausas de GC.
  • Antivirus o inspección de contenido encolan trabajo de forma asíncrona—el HTTP 200 llega tarde aunque el storage haya terminado.
  • Object storage limita PUTs o conexiones; la latencia de cola crece mientras la tasa de error se mantiene baja.
  • CDN o edge pueden terminar uploads distinto que el origen—probar solo desde un generador regional oculta límites por geografía.

Piensa en las subidas como una cinta transportadora con tres básculas. Probar solo la primera no dice nada de atascos en escaneo o S3.

Combina escenarios de upload con parametrización k6 desde CSV o JSON para que la mediana y el P95 de adjuntos en analytics manden en la mezcla—no un PNG de muestra del desarrollador.

Implementación práctica en k6: cuerpos multipart, tags y métricas custom

k6 envía multipart con http.file() y http.multipartBody() (binary uploads). Etiqueta cada iteración por bucket de tamaño para separar http_req_duration y fallos por clase de payload en el resumen.

Script de ejemplo (ilustrativo—no listo para producción). URLs, tokens y umbrales ficticios. Adapta rutas, auth, fixtures y SLO a tu entorno.

Qué demuestra este ejemplo:

  • Mezcla de tamaños: SharedArray elige fixtures pequeño, mediano y grande según percentiles de analytics—no un blob estático.
  • Subida multipart: http.file() adjunta binario con content-type correcto para el boundary.
  • Métrica específica de upload: Un Trend mide tiempo hasta headers de respuesta—útil cuando el ACK llega después del storage.
  • Checks más allá del status: Valida JSON con clave de objeto o id de upload para que «200 con cuerpo vacío» no pase en silencio.
  • Concurrencia por env: UPLOAD_RPS permite repetir el mismo script en CI cuando cambian límites del gateway.
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Trend } from 'k6/metrics';
import { SharedArray } from 'k6/data';

const uploadAck = new Trend('upload_ack_ms', true);

const files = new SharedArray('fixtures', function () {
  return [
    { tag: 'size:small', path: './fixtures/recibo-200kb.pdf', mime: 'application/pdf' },
    { tag: 'size:medium', path: './fixtures/factura-2mb.pdf', mime: 'application/pdf' },
    { tag: 'size:large', path: './fixtures/paquete-12mb.zip', mime: 'application/zip' },
  ];
});

const BASE = __ENV.API_BASE || 'https://staging.ejemplo.com';
const TOKEN = __ENV.API_TOKEN || 'reemplazar';

export const options = {
  scenarios: {
    uploads: {
      executor: 'constant-arrival-rate',
      rate: Number(__ENV.UPLOAD_RPS || 8),
      timeUnit: '1s',
      duration: '5m',
      preAllocatedVUs: 40,
      maxVUs: 120,
    },
  },
  thresholds: {
    http_req_failed: ['rate<0.01'],
    'http_req_duration{size:large}': ['p(95)<45000'],
    upload_ack_ms: ['p(99)<60000'],
  },
};

export default function () {
  const pick = files[Math.floor(Math.random() * files.length)];
  const bin = open(pick.path, 'b');

  const body = {
    file: http.file(bin, pick.path.split('/').pop(), pick.mime),
    purpose: 'adjunto_reclamo',
  };

  const res = http.post(`${BASE}/v1/uploads`, body, {
    headers: { Authorization: `Bearer ${TOKEN}` },
    tags: { name: 'multipart_upload', size: pick.tag.split(':')[1] },
    timeout: '120s',
  });

  uploadAck.add(res.timings.waiting);

  check(res, {
    'status 201 o 202': (r) => r.status === 201 || r.status === 202,
    'upload id presente': (r) => r.json('uploadId') !== undefined,
  });

  sleep(1);
}

Para colas de antivirus, añade un segundo escenario que haga poll al endpoint de estado si tu API devuelve 202 + processing. Separas latencia de «aceptado» de «listo»—crítico cuando producto promete «subido en 30 segundos».

Marco de decisión: qué simular en staging

SituaciónAcción recomendada
Tráfico estable, adjuntos mixtosconstant-arrival-rate con SharedArray ponderado a percentiles de analytics
Lanzamiento de tamaño máximo mayorEscenario spike con 90 % payloads grandes 10 min; vigilar 413 y memoria
Antivirus asíncrono en el ACKMedir upload_ack_ms y poll opcional; alertar p99 de ACK, no solo errores HTTP
URLs presignadas directo a S3Probar API de mint y PUT del cliente si el navegador sube fuera de tu API
Cambio de límite en gatewayBúsqueda binaria de tamaños cerca del límite en smoke; CI con tasa 413 = 0

Usa mezcla ponderada por tamaño cuando los logs muestran colas largas—solo la mediana no captura coste cuadrático en storage.

Usa spike de payloads grandes cuando marketing o compliance sube bytes permitidos—los proxies suelen fallar antes que la aplicación.

Usa flujo en dos pasos con presigned cuando el cliente sube a object storage; martillar solo tu API omite throttles de CDN y storage.

Observabilidad y checklist pre-release

Las regresiones multipart son difíciles sin tags y fixtures bajo control de versiones. Antes de subir RPS:

  • Documentar percentiles de tamaño (p50, p95, máx) y qué fixture representa cada bucket.
  • Confirmar que límites de body en staging coinciden con producción—or documentar deltas intencionales.
  • Etiquetar iteraciones k6 con size:small|medium|large (o rangos en bytes) para umbrales por bucket.
  • Archivar checksum de fixtures y git SHA del script por ejecución.
  • Añadir smoke en CI tras cambios de gateway: baja tasa, un archivo por bucket, umbrales estrictos en 413/5xx (pruebas de carga en CI/CD).

Cómo Performate simplifica pruebas multipart

Armar blobs binarios a mano cada sprint no escala. Flujo concreto para el mismo journey /v1/uploads de este artículo—adapta nombres y tasas a tu analytics.

Ejemplo: estresar recibos sin tres forks de script

  1. Importa una colección Postman con requests de subida pequeño, mediano y grande (o duplica uno y cambia adjuntos en la UI). Problema resuelto: un workspace en lugar de tres repos que divergen.
  2. Crea un escenario con llegada constante a 8 req/s (o tu techo seguro en staging) y los tres requests con pesos 60/30/10 según distribución del mes pasado. Problema resuelto: mezcla honesta sin editar SharedArray en cada ajuste.
  3. Tags por request: size:small, size:medium, size:large, más route:uploads. Problema resuelto: informes alineados con el modelo de tags de k6.
  4. Ejecuta y abre la vista de comparación—filtra por tag de tamaño y comprueba si p95 en archivos grandes cumple el SLO. Problema resuelto: producto e infra debaten un export, no tres hojas de cálculo.
  5. Cuando el gateway sube límites, actualiza adjuntos en la colección y re-ejecuta—mismos escenarios, nuevos fixtures. Problema resuelto: cambios de límite son actualización de colección, no repo nuevo.
  6. Exporta k6 generado para smoke en CI y mantener alineados tuning local y pipeline.

Ese flujo coincide con el cta de este post: orquestar recorridos multipart desde colecciones sin armar blobs a mano cada sprint.

Cierre

El rendimiento de subidas es un problema de pipeline: proxies, memoria de app, escáneres y storage introducen modos de fallo distintos. Prueba en carga la mezcla de tamaños y concurrencia reales, etiqueta cada clase de payload y trata ACK lentos con HTTP limpio como riesgo de release.

Ejecuta esta semana la mezcla ponderada de analytics en staging antes del próximo cambio de gateway o tamaño máximo—y anota qué bucket posee la cola de latencia que tu SLO exige.

Prueba Performate gratis | Reserva una demo | Peticiones HTTP k6

¿Listo para optimizar el rendimiento de tu API?

Usa Performate para orquestar recorridos multipart desde colecciones sin armar blobs binarios a mano cada sprint.

← Volver a todas las entradas