Overview
Lasso tracks blockchain state using HTTP polling as a reliable foundation with optional WebSocket subscriptions for sub-second updates. This dual-strategy design enables accurate lag detection for provider selection and subscription gap-filling.Architecture
Dual-Strategy Design
HTTP Polling (Always Running):- Bounded observation delay (
probe_interval_ms) - Enables optimistic lag calculation with known staleness
- Resilient to WebSocket failures
- Predictable observation delay for fair lag comparison
- Sub-second block notifications when healthy
- Degrades gracefully to HTTP on failure
- Can stale unpredictably (network issues, rate limits, provider cleanup)
BlockSync Components
BlockSync.Worker
Per-(chain, instance_id) GenServer tracking block heights. Location:Lasso.BlockSync.Worker
Operating Modes:
:http_only - HTTP polling only
:http_with_ws - HTTP + WebSocket subscription
BlockSync.Registry
Centralized ETS-based block height storage. Location:Lasso.BlockSync.Registry
Key Structure:
height: Block number (integer)timestamp: System timestamp when observed (milliseconds)source::httpor:ws(both write to same key, last write wins)metadata: Optional map with latency, provider info
- Single source of truth for height data
- <1ms lookups for lag calculations
- Supports consensus height derivation
- Lock-free concurrent reads
BlockSync.Supervisor
Singleton interface toBlockSync.DynamicSupervisor.
Location: Lasso.BlockSync.Supervisor
Responsibilities:
- Manages one Worker per
(chain, instance_id)pair - Handles worker lifecycle (start, stop, restart)
- Ensures instance-level deduplication
Dynamic Block Time Measurement
Lasso derives per-chain block intervals using Exponential Moving Average (EMA) for optimistic lag calculation. Location:Lasso.Core.BlockSync.BlockTimeMeasurement
EMA Parameters
Algorithm
Why EMA Over Median?
Chains like Arbitrum have demand-driven block production where block times vary from 100ms (high activity) to 5+ seconds (quiet periods). EMA adapts quickly to these changes, while median requires ~50% of samples to change before the measurement shifts.Multi-Provider Handling
Heights come from multiple providers (WebSocket and HTTP). The measurement tracks the global max height across providers:- Same height: Ignored (height not increasing)
- Heights close together: 50ms floor rejects artificially fast intervals
- Normal operation: Interval recorded normally
Optimistic Lag Calculation
Compensates for observation delay on fast chains to prevent false lag detection.Algorithm
Example: Arbitrum (250ms blocks, 2s poll interval)
- Prevents credit from exceeding reasonable bounds
- Ensures lagging providers are eventually detected
- Typical values: 2-5s for HTTP polling, <1s for WebSocket
Bounded Observation Delay
HTTP polling provides predictable observation delay:- Known staleness:
elapsed_msis exact - Accurate credit: Can precisely calculate expected blocks
- Fair comparison: All providers measured with same methodology
Health Probing
ProbeCoordinator
Per-chain health probe coordinator (one per unique chain). Location:Lasso.Providers.ProbeCoordinator
Responsibilities:
- 200ms tick cycle, probes one instance per tick
- Periodic
eth_chainIdprobes (health check + version detection) - Exponential backoff on failure
- Signals recovery to circuit breakers
- Writes health status to
:lasso_instance_stateETS
probe_interval_ms config parameter (previously per-profile) has been removed.
Exponential Backoff
Reduces probe load on degraded instances:| Consecutive Failures | Backoff |
|---|---|
| 0-1 | 0 (probe on next tick) |
| 2 | 2 seconds |
| 3 | 4 seconds |
| 4 | 8 seconds |
| 5 | 16 seconds |
| 6+ | 30 seconds (capped) |
- Backoff uses monotonic time (avoids wall-clock jump issues)
- ±20% jitter prevents synchronized probe storms
- Backoff resets immediately on success
- Each instance tracks its own backoff state independently
- Probes dispatched as async Tasks (prevent slow instances from blocking cycle)
:open → :half_open for faster recovery.
Configuration
Chain-Level Settings
probe_interval_ms is no longer configurable per profile. ProbeCoordinator uses a fixed 200ms tick interval.
Dynamic vs Static Block Time
Preferred: EMA measurement (after 5 samples)- Adapts to changing block production rates
- Handles demand-driven chains (Arbitrum, Optimism)
- Uses monotonic time (no clock drift)
block_time_ms config
- Used during warmup (first 5 blocks)
- Used if EMA measurement fails
- Static value from chain documentation
Consensus Height Derivation
Used for gap calculation in WebSocket failover. Location:Lasso.RPC.ChainState
Algorithm:
- <1ms (ETS read-only)
- vs 200-500ms for blocking HTTP request
Telemetry Events
Performance Characteristics
Overhead:- Height lookup: <0.1ms (ETS read)
- Consensus calculation: <1ms (ETS scan + median)
- Optimistic lag calculation: <0.5ms (arithmetic)
- HTTP poll latency: 50-200ms per provider
- WebSocket update latency: 10-50ms
- Instance-scoped workers: O(unique_upstreams), not O(profiles × chains)
- ETS-based registry: 10,000+ concurrent reads
- Memory per worker: <5KB
Best Practices
For Fast Chains (Arbitrum, Optimism)
- Rely on dynamic measurement: EMA adapts to variable block times
- Set conservative lag thresholds: 10-20 blocks to account for burst production
- Use WebSocket + HTTP: Sub-second updates for selection, HTTP for reliability
For Slow Chains (Ethereum, Bitcoin)
- HTTP-only mode: WebSocket overhead not worth sub-second updates
- Lower probe frequency: 12s blocks don’t need 200ms ticks
- Tight lag thresholds: 2-3 blocks is a meaningful lag
For Production Monitoring
- Track EMA warmup: Alert if
sample_count < 5persists - Monitor consensus failures: Should be rare (<0.1% of requests)
- Alert on persistent lag: Provider consistently >5 blocks behind
Related Documentation
- WebSocket Subscriptions - Uses consensus height for gap calculation
- Error Classification - Health probe error handling
- Benchmarking - Provider selection based on lag metrics