Retry Plugin

Retry Plugin

El Retry Plugin proporciona un mecanismo de reintento robusto para Module Federation. Cuando los módulos remotos o los recursos no se cargan, reintenta automáticamente para mantener estable tu aplicación.

Características

  • Reintentos automáticos: mejora la estabilidad reintentando las cargas de recursos fallidas
  • Rotación de dominios: alterna automáticamente entre varios dominios de respaldo
  • Cache-busting: añade parámetros de query para evitar interferencias de la caché en los reintentos
  • Configuración flexible: personaliza el número de reintentos, el retardo y los callbacks

Instalación

npm install @module-federation/retry-plugin

Guía de migración

De v0.18.x a v0.19.x

La configuración del plugin se ha simplificado. Los antiguos objetos de configuración fetch y script están obsoletos:

// ❌ Old way (deprecated)
RetryPlugin({
  fetch: {
    url: 'http://localhost:2008/not-exist-mf-manifest.json',
    fallback: () => 'http://localhost:2001/mf-manifest.json',
  },
  script: {
    url: 'http://localhost:2001/static/js/async/src_App_tsx.js',
    customCreateScript: (url, attrs) => { /* ... */ },
  }
})

// ✅ New way
RetryPlugin({
  retryTimes: 3,
  retryDelay: 1000,
  domains: ['http://localhost:2001'],
  manifestDomains: ['http://localhost:2001'],
  addQuery: ({ times, originalQuery }) => `${originalQuery}&retry=${times}`,
})

Uso

Método 1: usarlo en el plugin de build

rspack.config.ts
import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
import { defineConfig } from '@rsbuild/core';

export default defineConfig({
  plugins: [
    pluginReact(),
    pluginModuleFederation({
      runtimePlugins: [
        path.join(__dirname, './src/runtime-plugin/retry.ts'),
      ],
    }),
  ],
});
// ./src/runtime-plugin/retry.ts
import { RetryPlugin } from '@module-federation/retry-plugin';
const retryPlugin = () => RetryPlugin({
  retryTimes: 3,
  retryDelay: 1000,
  manifestDomains: ['https://domain1.example.com', 'https://domain2.example.com'],
  domains: ['https://cdn1.example.com', 'https://cdn2.example.com'],
  addQuery: ({ times, originalQuery }) => `${originalQuery}&retry=${times}`,
  onRetry: ({ times, url }) => console.log('retry', times, url),
  onSuccess: ({ url }) => console.log('success', url),
  onError: ({ url }) => console.log('error', url),
})
export default retryPlugin;

Método 2: usarlo solo en runtime

import { createInstance, loadRemote } from '@module-federation/enhanced/runtime';
import { RetryPlugin } from '@module-federation/retry-plugin';

const mf = createInstance({
  name: 'federation_consumer',
  remotes: [],
  plugins: [
    RetryPlugin({
      retryTimes: 3,
      retryDelay: 1000,
      manifestDomains: ['https://domain1.example.com', 'https://domain2.example.com'],
      domains: ['https://cdn1.example.com', 'https://cdn2.example.com'],
      addQuery: ({ times, originalQuery }) => `${originalQuery}&retry=${times}`,
      onRetry: ({ times, url }) => console.log('retry', times, url),
      onSuccess: ({ url }) => console.log('success', url),
      onError: ({ url }) => console.log('error', url),
    }),
  ],
});

Configuración

Básica

retryTimes

  • Tipo: number
  • Opcional
  • Número de reintentos; por defecto es 3

retryDelay

  • Tipo: number
  • Opcional
  • Retardo entre reintentos en milisegundos; por defecto es 1000

Avanzada

domains

  • Tipo: string[]
  • Opcional
  • Dominios de respaldo para la rotación. Por defecto es un array vacío

addQuery

  • Tipo: boolean | ((context: { times: number; originalQuery: string }) => string)
  • Opcional
  • Indica si se debe añadir un parámetro de query al reintentar
  • Por defecto se aplica cache-busting a los reintentos de remote entries de tipo esm y module, porque los navegadores almacenan en caché por URL los imports dinámicos fallidos. Las demás rutas de reintento no añaden ningún parámetro de query por defecto
  • Establece addQuery: false para desactivar el cache-busting por defecto de las remote entries ESM/module
  • Si se proporciona una función, recibe el número de reintento y la query string original, y debe devolver la nueva query string

fetchOptions

  • Tipo: RequestInit
  • Opcional
  • Opciones de fetch personalizadas; por defecto es un objeto vacío

manifestDomains

  • Tipo: string[]
  • Opcional
  • Lista de rotación de dominios que se usa al obtener el manifest (p. ej., mf-manifest.json). Tiene prioridad sobre domains en los reintentos de obtención del manifest. Los demás recursos siguen usando domains.

Callbacks

onRetry

  • Tipo: ({ times, domains, url, tagName }: { times?: number; domains?: string[]; url?: string; tagName?: string }) => void
  • Opcional
  • Se dispara en cada reintento
  • Parámetros: times número de reintento actual, domains lista de dominios, url URL de la solicitud, tagName tipo de recurso

onSuccess

  • Tipo: ({ domains, url, tagName }: { domains?: string[]; url?: string; tagName?: string; }) => void
  • Opcional
  • Se dispara cuando un reintento acaba teniendo éxito
  • Parámetros: domains lista de dominios, url URL de la solicitud, tagName tipo de recurso

onError

  • Tipo: ({ domains, url, tagName }: { domains?: string[]; url?: string; tagName?: string; }) => void
  • Opcional
  • Se dispara cuando fallan todos los reintentos
  • Parámetros: domains lista de dominios, url URL de la solicitud, tagName tipo de recurso

Detalles

Lógica de reintento

El plugin reintenta automáticamente cuando falla la carga de un recurso. El número de reintentos se controla con retryTimes. Por ejemplo:

  • retryTimes: 3 significa hasta 3 reintentos (después del primer intento)
  • Se aplica un retardo de retryDelay ms antes de cada reintento

Rotación de dominios

Cuando se configura domains, el plugin rota el host en cada reintento:

const retryPlugin = RetryPlugin({
  domains: [
    'https://cdn1.example.com',
    'https://cdn2.example.com',
    'https://cdn3.example.com'
  ],
});

Orden de los intentos:

  1. Intento inicial: URL original
  2. 1.er reintento: cambia a cdn2.example.com
  3. 2.º reintento: cambia a cdn3.example.com
  4. 3.er reintento: cambia a cdn1.example.com

Cache-busting

Usa addQuery para añadir parámetros de query durante los reintentos y evitar interferencias de la caché:

Para las remote entries de tipo esm o module, el plugin añade retryCount por defecto para que cada reintento de import dinámico use una URL nueva. Este comportamiento por defecto no afecta a los reintentos del manifest, de fetch ni de scripts clásicos. Desactívalo explícitamente si lo necesitas:

const retryPlugin = RetryPlugin({
  addQuery: false,
});

Para habilitar el cache-busting en todas las rutas de reintento:

const retryPlugin = RetryPlugin({
  addQuery: true, // adds ?retryCount=1, ?retryCount=2, etc.
});

También puedes proporcionar una función:

const retryPlugin = RetryPlugin({
  addQuery: ({ times, originalQuery }) => {
    return `${originalQuery}&retry=${times}&timestamp=${Date.now()}`;
  },
});

Callbacks

Puedes monitorizar el ciclo de vida de los reintentos con callbacks:

const retryPlugin = RetryPlugin({
  onRetry: ({ times, domains, url, tagName }) => {
    console.log(`Retry #${times}, domains: ${domains}, url: ${url}`);
  },
  onSuccess: ({ domains, url, tagName }) => {
    console.log(`Retry success, domains: ${domains}, url: ${url}`);
  },
  onError: ({ domains, url, tagName }) => {
    console.log(`Retry failed, domains: ${domains}, url: ${url}`);
  },
});

Parámetros de los callbacks:

  • times: número de reintento actual (empieza en 1)
  • domains: la lista de dominios actual
  • url: URL de la solicitud actual
  • tagName: tipo de recurso ('fetch' o 'script')

Casos de uso

1. Conmutación por error de CDN

const retryPlugin = RetryPlugin({
  retryTimes: 2,
  domains: [
    'https://cdn1.example.com',
    'https://cdn2.example.com',
    'https://cdn3.example.com'
  ],
  addQuery: true,
});

2. Redes inestables

const retryPlugin = RetryPlugin({
  retryTimes: 5,
  retryDelay: 2000,
  onRetry: ({ times }) => {
    console.log(`Unstable network, retry #${times}`);
  },
});

3. Monitorización y registro

const retryPlugin = RetryPlugin({
  onRetry: ({ times, url }) => {
    analytics.track('resource_retry', { times, url });
  },
  onError: ({ url }) => {
    logger.error('Resource load failed after all retries', { url });
  },
});

Notas

  1. Rendimiento: un número elevado de reintentos aumenta el tiempo de carga; ajústalo a tu entorno
  2. Dominios: asegúrate de que todos los dominios de domains sirvan el mismo recurso
  3. Caché: si addQuery está habilitado, ten en cuenta la estrategia de caché de la CDN
  4. Gestión de errores: cuando fallan todos los reintentos, se lanza el error original; gestiónalo en las capas superiores

Códigos de error

  • RUNTIME_008: fallo de carga de un recurso que activa el mecanismo de reintento