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 ```bash 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: ```ts // ❌ 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 ```ts title="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'), ], }), ], }); ``` ```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 ```ts 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: ```javascript 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: ```javascript const retryPlugin = RetryPlugin({ addQuery: true, // adds ?retryCount=1, ?retryCount=2, etc. }); ``` You can also provide a function: ```javascript const retryPlugin = RetryPlugin({ addQuery: ({ times, originalQuery }) => { return `${originalQuery}&retry=${times}&timestamp=${Date.now()}`; }, }); ``` ### Callbacks You can monitor the retry lifecycle with callbacks: ```javascript 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 ```javascript const retryPlugin = RetryPlugin({ retryTimes: 2, domains: [ 'https://cdn1.example.com', 'https://cdn2.example.com', 'https://cdn3.example.com' ], addQuery: true, }); ``` ### 2. Unstable networks ```javascript const retryPlugin = RetryPlugin({ retryTimes: 5, retryDelay: 2000, onRetry: ({ times }) => { console.log(`Unstable network, retry #${times}`); }, }); ``` ### 3. Monitoring and logging ```javascript 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