Aggregate seed provider
AggregateSeedProvider wraps multiple seed providers and tries
them in order, returning the first non-empty result. Useful
when:
- You want DNS first, fall back to static seeds if DNS is unreachable.
- The same code runs in multiple environments: K8s for prod, static config for local dev.
- You’re doing disaster recovery — primary discovery mechanism + backup.
import { Cluster, AggregateSeedProvider, KubernetesApiSeedProvider, DnsSeedProvider, ConfigSeedProvider,} from 'actor-ts';
const provider = new AggregateSeedProvider([ new KubernetesApiSeedProvider({ namespace, labelSelector, containerPort: 2552 }), new DnsSeedProvider({ service: '_actor-ts._tcp.example.com' }), new ConfigSeedProvider({ seeds: ['fallback-1:2552', 'fallback-2:2552'] }),]);
const seeds = await provider.lookup();await Cluster.join(system, { host, port, seeds });How it chains
Section titled “How it chains”lookup() → try provider[0].lookup() ├── non-empty result + no error → return that └── empty / error → try provider[1].lookup() ├── non-empty → return that └── empty / error → try provider[2]... └── ... eventually return [] if all failThe provider stops at the first non-empty result. An empty list from one provider triggers fallback to the next; an error also triggers fallback (with the error logged at debug level).
If every provider fails or returns empty, lookup() resolves
to [] — the cluster’s Cluster.join then either self-bootstraps
or retries (depending on its own settings).
Common patterns
Section titled “Common patterns”Local dev → prod K8s
Section titled “Local dev → prod K8s”new AggregateSeedProvider([ new ConfigSeedProvider({ envVar: 'ACTOR_TS_SEEDS' }), // 1st: env var new KubernetesApiSeedProvider({ // 2nd: K8s API namespace: process.env.K8S_NAMESPACE ?? '', labelSelector: 'app=actor-ts', containerPort: 2552, }),]);Local dev: export ACTOR_TS_SEEDS=localhost:2552 → uses the env
var. In K8s, the env var isn’t set; falls through to the K8s
API.
DNS-first with static fallback
Section titled “DNS-first with static fallback”new AggregateSeedProvider([ new DnsSeedProvider({ service: '_actor-ts._tcp.example.com' }), new ConfigSeedProvider({ seeds: ['known-stable-node-1:2552', 'known-stable-node-2:2552'], }),]);Try DNS first; if it returns empty (DNS server hiccup, no records yet during bootstrapping), use known-stable static IPs.
Multi-region disaster recovery
Section titled “Multi-region disaster recovery”new AggregateSeedProvider([ new KubernetesApiSeedProvider({ namespace: 'primary-region', ... }), new KubernetesApiSeedProvider({ namespace: 'failover-region', ... }),]);In a multi-region deployment, try the local region first; if no pods are running locally, attempt to join the failover region’s cluster. Aggressive — only makes sense if the regions are actually meant to share state.
Error handling
Section titled “Error handling”new AggregateSeedProvider([ new KubernetesApiSeedProvider({ ... }), // raises 403 in non-K8s new ConfigSeedProvider({ seeds: ['10.0.0.1:2552'] }),]);A 403 from K8s API → logged + skipped → falls through to the config provider. This makes aggregate the right tool for “this code path runs everywhere” — the K8s lookup just becomes a noop where K8s isn’t available.
Errors logged at debug level — quiet by default. Configure
the system at debug level if you want visibility into which
providers tried + failed.
Ordering matters
Section titled “Ordering matters”The first provider that returns a non-empty result is used. Test the order explicitly:
| Order | Effect |
|---|---|
| K8s, then DNS | K8s wins in K8s deployments; DNS is the fallback. |
| DNS, then K8s | DNS wins everywhere; K8s only fires when DNS is unreachable. |
Put the preferred provider first; the fallback last.
Where to next
Section titled “Where to next”- Discovery overview — the bigger picture.
- Config seed provider — static-list provider commonly used as fallback.
- DNS seed provider — the DNS-based provider.
- Kubernetes API seed provider — the K8s-based provider.
- Joining and seeds — how the result is used.