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}×tamp=${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