Retry Plugin

Retry Plugin

The Retry Plugin provides a robust retry mechanism for Module Federation. When remote modules or resources fail to load, it retries automatically to keep your app stable.

Features

  • Automatic retries: Improve stability by retrying failed resource loads
  • Domain rotation: Switch across multiple backup domains automatically
  • Cache-busting: Add query parameters to avoid cache interference on retries
  • Flexible config: Customize retry times, delay, and callbacks

Install

npm install @module-federation/retry-plugin

Migration Guide

From v0.18.x to v0.19.x

The plugin configuration has been simplified. The old fetch and script configuration objects are deprecated:

// ❌ 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}`,
})

Usage

Method1: Used in the build plugin

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;

Method2: Used in the pure 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),
    }),
  ],
});

Configuration

Basic

retryTimes

  • Type: number
  • Optional
  • Number of retries, default is 3

retryDelay

  • Type: number
  • Optional
  • Delay between retries in milliseconds, default is 1000

Advanced

domains

  • Type: string[]
  • Optional
  • Backup domains for rotation. Default is an empty array

addQuery

  • Type: boolean | ((context: { times: number; originalQuery: string }) => string)
  • Optional
  • Whether to append a query parameter when retrying, default is false
  • If a function is provided, it receives retry count and the original query string, and should return the new query string

fetchOptions

  • Type: RequestInit
  • Optional
  • Custom fetch options, default is an empty object

manifestDomains

  • Type: string[]
  • Optional
  • Domain rotation list used when fetching the manifest (e.g. mf-manifest.json). Takes precedence over domains for manifest fetch retries. Other resources still use domains.

Callbacks

onRetry

  • Type: ({ times, domains, url, tagName }: { times?: number; domains?: string[]; url?: string; tagName?: string }) => void
  • Optional
  • Triggered on each retry
  • Params: times current retry number, domains domain list, url request URL, tagName resource type

onSuccess

  • Type: ({ domains, url, tagName }: { domains?: string[]; url?: string; tagName?: string; }) => void
  • Optional
  • Triggered when a retry finally succeeds
  • Params: domains domain list, url request URL, tagName resource type

onError

  • Type: ({ domains, url, tagName }: { domains?: string[]; url?: string; tagName?: string; }) => void
  • Optional
  • Triggered when all retries fail
  • Params: domains domain list, url request URL, tagName resource type

Details

Retry logic

The plugin retries automatically when a resource fails to load. The number of retries is controlled by retryTimes. For example:

  • retryTimes: 3 means up to 3 retries (after the first attempt)
  • A delay of retryDelay ms is applied before each retry

Domain rotation

When domains is configured, the plugin rotates the host on each retry:

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

Order of attempts:

  1. Initial attempt: original URL
  2. 1st retry: switch to cdn2.example.com
  3. 2nd retry: switch to cdn3.example.com
  4. 3rd retry: switch to cdn1.example.com

Cache-busting

Use addQuery to add query parameters during retries to avoid cache interference:

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

You can also provide a function:

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

Callbacks

You can monitor the retry lifecycle with 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}`);
  },
});

Callback params:

  • times: current retry count (starts from 1)
  • domains: the current domain list
  • url: current request URL
  • tagName: resource type ('fetch' or 'script')

Use cases

1. CDN failover

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

2. Unstable networks

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

3. Monitoring and logging

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

Notes

  1. Performance: High retry counts increase loading time; tune for your environment
  2. Domains: Ensure all domains in domains serve the same resource
  3. Caching: If addQuery is enabled, consider CDN caching strategy
  4. Error handling: After all retries fail, the original error is thrown; handle it upstream

Error codes

  • RUNTIME_008: Resource load failure that triggers the retry mechanism