Ir al contenido
Español

Aggregate seed provider

Esta página aún no está disponible en tu idioma.

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,
ClusterOptions,
AggregateSeedProvider,
KubernetesApiSeedProvider,
KubernetesApiSeedProviderOptions,
DnsSeedProvider,
DnsSeedProviderOptions,
ConfigSeedProvider,
ConfigSeedProviderOptions,
} from 'actor-ts';
const kubernetesApiSeedProviderOptions = KubernetesApiSeedProviderOptions.create()
.withNamespace(namespace)
.withServiceName(labelSelector)
.withSystemName('my-app')
.withPort(2552);
const dnsSeedProviderOptions = DnsSeedProviderOptions.create()
.withHostname('_actor-ts._tcp.example.com')
.withSystemName('my-app')
.withUseSrv();
const configSeedProviderOptions = ConfigSeedProviderOptions.create()
.withSeeds(['fallback-1:2552', 'fallback-2:2552'])
.withSystemName('my-app');
const provider = new AggregateSeedProvider([
new KubernetesApiSeedProvider(
kubernetesApiSeedProviderOptions,
),
new DnsSeedProvider(
dnsSeedProviderOptions,
),
new ConfigSeedProvider(
configSeedProviderOptions,
),
]);
const seeds = await provider.lookup();
const clusterOptions = ClusterOptions.create()
.withHost(host)
.withPort(port)
.withSeeds(seeds);
await Cluster.join(system, clusterOptions);

non-empty + no error

empty / error

non-empty

empty / error

lookup

try provider[0].lookup

result?

try provider[1].lookup

result?

try provider[2]...

...eventually return []

if all fail

return result

return result

The provider stops at the first non-empty result. An empty list from one provider triggers fallback to the next; an error also triggers fallback (the error is passed to an optional log callback — a no-op by default).

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).

const k8sSeedProviderOptions = KubernetesApiSeedProviderOptions.create()
.withNamespace(process.env.K8S_NAMESPACE ?? '')
.withServiceName('actor-ts')
.withSystemName('my-app')
.withPort(2552);
new AggregateSeedProvider([
seedsFromEnv('ACTOR_TS_SEEDS', 'my-app'), // 1st: env var
new KubernetesApiSeedProvider(k8sSeedProviderOptions), // 2nd: K8s API
]);

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.

const dnsSeedProviderOptions = DnsSeedProviderOptions.create()
.withHostname('_actor-ts._tcp.example.com')
.withSystemName('my-app')
.withUseSrv();
const configSeedProviderOptions = ConfigSeedProviderOptions.create()
.withSeeds(['known-stable-node-1:2552', 'known-stable-node-2:2552'])
.withSystemName('my-app');
new AggregateSeedProvider([
new DnsSeedProvider(
dnsSeedProviderOptions,
),
new ConfigSeedProvider(
configSeedProviderOptions,
),
]);

Try DNS first; if it returns empty (DNS server hiccup, no records yet during bootstrapping), use known-stable static IPs.

const kubernetesApiSeedProviderOptions = KubernetesApiSeedProviderOptions.create()
.withNamespace('primary-region')
.withServiceName('actor-ts')
.withSystemName('my-app')
.withPort(2552);
const kubernetesApiSeedProvider2Options = KubernetesApiSeedProviderOptions.create()
.withNamespace('failover-region')
.withServiceName('actor-ts')
.withSystemName('my-app')
.withPort(2552);
new AggregateSeedProvider([
new KubernetesApiSeedProvider(
kubernetesApiSeedProviderOptions,
),
new KubernetesApiSeedProvider(
kubernetesApiSeedProvider2Options,
),
]);

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.

const kubernetesApiSeedProviderOptions = KubernetesApiSeedProviderOptions.create()
.withNamespace('my-app')
.withServiceName('actor-ts')
.withSystemName('my-app')
.withPort(2552);
const configSeedProviderOptions = ConfigSeedProviderOptions.create()
.withSeeds(['10.0.0.1:2552'])
.withSystemName('my-app');
new AggregateSeedProvider([
new KubernetesApiSeedProvider(
kubernetesApiSeedProviderOptions,
), // raises 403 in non-K8s
new ConfigSeedProvider(
configSeedProviderOptions,
),
]);

A 403 from K8s API → 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.

AggregateSeedProvider takes an optional second constructor argument — a log callback (message: string, err?: unknown) => void. It defaults to a no-op, so provider errors are silently swallowed unless you pass one. Supply a log function to get visibility into which providers tried + failed:

new AggregateSeedProvider(
[
new KubernetesApiSeedProvider(kubernetesApiSeedProviderOptions),
new ConfigSeedProvider(configSeedProviderOptions),
],
(message, error) => console.warn(`[seed] ${message}`, error),
);

The first provider that returns a non-empty result is used. Test the order explicitly:

OrderEffect
K8s, then DNSK8s wins in K8s deployments; DNS is the fallback.
DNS, then K8sDNS wins everywhere; K8s only fires when DNS is unreachable.

Put the preferred provider first; the fallback last.