Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Failing after 34s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
146 lines
6.0 KiB
Plaintext
146 lines
6.0 KiB
Plaintext
# Class: RoundRobinPool
|
|
|
|
Extends: `undici.Dispatcher`
|
|
|
|
A pool of [Client](/docs/docs/api/Client.md) instances connected to the same upstream target with round-robin client selection.
|
|
|
|
Unlike [`Pool`](/docs/docs/api/Pool.md), which always selects the first available client, `RoundRobinPool` cycles through clients in a round-robin fashion. This ensures even distribution of requests across all connections, which is particularly useful when the upstream target is behind a load balancer that round-robins TCP connections across multiple backend servers (e.g., Kubernetes Services).
|
|
|
|
Requests are not guaranteed to be dispatched in order of invocation.
|
|
|
|
## `new RoundRobinPool(url[, options])`
|
|
|
|
Arguments:
|
|
|
|
* **url** `URL | string` - It should only include the **protocol, hostname, and port**.
|
|
* **options** `RoundRobinPoolOptions` (optional)
|
|
|
|
### Parameter: `RoundRobinPoolOptions`
|
|
|
|
Extends: [`ClientOptions`](/docs/docs/api/Client.md#parameter-clientoptions)
|
|
|
|
* **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Client(origin, opts)`
|
|
* **connections** `number | null` (optional) - Default: `null` - The number of `Client` instances to create. When set to `null`, the `RoundRobinPool` instance will create an unlimited amount of `Client` instances.
|
|
* **clientTtl** `number | null` (optional) - Default: `null` - The amount of time before a `Client` instance is removed from the `RoundRobinPool` and closed. When set to `null`, `Client` instances will not be removed or closed based on age.
|
|
|
|
## Use Case
|
|
|
|
`RoundRobinPool` is designed for scenarios where:
|
|
|
|
1. You connect to a single origin (e.g., `http://my-service.namespace.svc`)
|
|
2. That origin is backed by a load balancer distributing TCP connections across multiple servers
|
|
3. You want requests evenly distributed across all backend servers
|
|
|
|
**Example**: In Kubernetes, when using a Service DNS name with multiple Pod replicas, kube-proxy load balances TCP connections. `RoundRobinPool` ensures each connection (and thus each Pod) receives an equal share of requests.
|
|
|
|
### Important: Backend Distribution Considerations
|
|
|
|
`RoundRobinPool` distributes **HTTP requests** evenly across **TCP connections**. Whether this translates to even backend server distribution depends on the load balancer's behavior:
|
|
|
|
**✓ Works when the load balancer**:
|
|
- Assigns different backends to different TCP connections from the same client
|
|
- Uses algorithms like: round-robin, random, least-connections (without client affinity)
|
|
- Example: Default Kubernetes Services without `sessionAffinity`
|
|
|
|
**✗ Does NOT work when**:
|
|
- Load balancer has client/source IP affinity (all connections from one IP → same backend)
|
|
- Load balancer uses source-IP-hash or sticky sessions
|
|
|
|
**How it works:**
|
|
1. `RoundRobinPool` creates N TCP connections to the load balancer endpoint
|
|
2. Load balancer assigns each TCP connection to a backend (per its algorithm)
|
|
3. `RoundRobinPool` cycles HTTP requests across those N connections
|
|
4. Result: Requests distributed proportionally to how the LB distributed the connections
|
|
|
|
If the load balancer assigns all connections to the same backend (e.g., due to session affinity), `RoundRobinPool` cannot overcome this. In such cases, consider using [`BalancedPool`](/docs/docs/api/BalancedPool.md) with direct backend addresses (e.g., individual pod IPs) instead of a load-balanced endpoint.
|
|
|
|
## Instance Properties
|
|
|
|
### `RoundRobinPool.closed`
|
|
|
|
Implements [Client.closed](/docs/docs/api/Client.md#clientclosed)
|
|
|
|
### `RoundRobinPool.destroyed`
|
|
|
|
Implements [Client.destroyed](/docs/docs/api/Client.md#clientdestroyed)
|
|
|
|
### `RoundRobinPool.stats`
|
|
|
|
Returns [`PoolStats`](PoolStats.md) instance for this pool.
|
|
|
|
## Instance Methods
|
|
|
|
### `RoundRobinPool.close([callback])`
|
|
|
|
Implements [`Dispatcher.close([callback])`](/docs/docs/api/Dispatcher.md#dispatcherclosecallback-promise).
|
|
|
|
### `RoundRobinPool.destroy([error, callback])`
|
|
|
|
Implements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise).
|
|
|
|
### `RoundRobinPool.connect(options[, callback])`
|
|
|
|
See [`Dispatcher.connect(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherconnectoptions-callback).
|
|
|
|
### `RoundRobinPool.dispatch(options, handler)`
|
|
|
|
Implements [`Dispatcher.dispatch(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).
|
|
|
|
### `RoundRobinPool.pipeline(options, handler)`
|
|
|
|
See [`Dispatcher.pipeline(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler).
|
|
|
|
### `RoundRobinPool.request(options[, callback])`
|
|
|
|
See [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).
|
|
|
|
### `RoundRobinPool.stream(options, factory[, callback])`
|
|
|
|
See [`Dispatcher.stream(options, factory[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback).
|
|
|
|
### `RoundRobinPool.upgrade(options[, callback])`
|
|
|
|
See [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback).
|
|
|
|
## Instance Events
|
|
|
|
### Event: `'connect'`
|
|
|
|
See [Dispatcher Event: `'connect'`](/docs/docs/api/Dispatcher.md#event-connect).
|
|
|
|
### Event: `'disconnect'`
|
|
|
|
See [Dispatcher Event: `'disconnect'`](/docs/docs/api/Dispatcher.md#event-disconnect).
|
|
|
|
### Event: `'drain'`
|
|
|
|
See [Dispatcher Event: `'drain'`](/docs/docs/api/Dispatcher.md#event-drain).
|
|
|
|
## Example
|
|
|
|
```javascript
|
|
import { RoundRobinPool } from 'undici'
|
|
|
|
const pool = new RoundRobinPool('http://my-service.default.svc.cluster.local', {
|
|
connections: 10
|
|
})
|
|
|
|
// Requests will be distributed evenly across all 10 connections
|
|
for (let i = 0; i < 100; i++) {
|
|
const { body } = await pool.request({
|
|
path: '/api/data',
|
|
method: 'GET'
|
|
})
|
|
console.log(await body.json())
|
|
}
|
|
|
|
await pool.close()
|
|
```
|
|
|
|
## See Also
|
|
|
|
- [Pool](/docs/docs/api/Pool.md) - Connection pool without round-robin
|
|
- [BalancedPool](/docs/docs/api/BalancedPool.md) - Load balancing across multiple origins
|
|
- [Issue #3648](https://github.com/nodejs/undici/issues/3648) - Original issue describing uneven distribution
|
|
|