EvmFallbackDataSourceBuilder builds a data source that drives an ordered list of downstream
sources — Portal datasets, JSON-RPC endpoints, or custom sources — and fails over between them
automatically. While the most-preferred source is healthy it is the only one consuming data; when
it degrades, the next healthy source takes over at a batch boundary, and when a higher-preference
source recovers, the fallback switches back up. The built source is a drop-in replacement for a
single EVM Portal stream.
For a deployment-oriented walkthrough, see High availability.
Install
rpc downstream source is used.
Complete example
Downstream sources
setDownstreamSources([...]) takes plain tagged config objects, most-preferred first:
{type: 'portal', url, ...}— a SQD Network Portal dataset; same options assetPortal.{type: 'rpc', url, network?, ...}— a JSON-RPC endpoint; same options assetRpc, including the per-network preset.{type: 'custom', buildSource}— the escape hatch: a function that receives the shared field selection and requests and returns any EVM data source.
name used in metrics and logs, defaulting to `${type}-${index}`
(portal-0, rpc-1, …).
How failover works
Each source has a trinary health state:healthy, unhealthy, or unknown. unknown lets the
first batch ship before any probe completes — a fresh fallback starts streaming immediately.
The active source is failed over when:
- it errors on a batch request or liveness check (
livenessFailThresholdconsecutive failures flip itunhealthy); - it lags: its height falls more than
maxLagBlocksbehind an independent chain-head reference — the maximum head of the other eligible sources. The lag trigger arms only after the tip is first reached, so a long historical backfill never false-fires; - it stalls: its next-batch request stays outstanding longer than
maxStalenessMs(time parked waiting for your handler to consume a batch does not count).
unhealthy source waits out cooldownMs, returns to unknown, and must pass
livenessRecoverThreshold consecutive probes (with capability confirmed) to become healthy
again. With preferPrimary: 'eager' (the default), a recovered higher-preference source is
promoted at the next batch boundary; 'onFailureOnly' switches only on failure.
When every source is down, the fallback polls indefinitely by default (the no-downtime goal).
Set allDownTimeoutMs to a finite value to throw AllSourcesDownError instead.
Policy reference
setPolicy({...}) accepts a FallbackPolicy; all fields are optional:
Capability probe
setCapabilityProbe(probe) — default true. A source counts as healthy only once it confirms
it can actually serve the configured data at the indexing frontier. This catches
reachable-but-incapable nodes — traces or debug_ methods disabled, pruned state — before a
switch-up promotes them. Pass false to govern health by liveness alone, or {timeoutMs}
(default 30 000 ms) to tune the probe. The probe targets capabilityLookahead blocks ahead of the
last committed height, clamped to capabilityTipMargin blocks below the tip so a caught-up source
probes a settled block.
Metrics
When the built source runs underrun() or Processor with Prometheus
enabled, six gauges are registered automatically — no wiring needed:
For a custom registry or metric prefix, use
fallbackMetricsSink(source, prefix?) from
@subsquid/squid-sdk/fallback. Registration is idempotent per registry and prefix, so calling it
alongside the automatic registration is safe.
Errors
AllSourcesDownError (importable from @subsquid/squid-sdk/evm/fallback or
@subsquid/squid-sdk/fallback) is thrown only when allDownTimeoutMs is set to a finite value
and expires. With the default policy the fallback never throws for availability reasons — it
polls until a source recovers.