TL;DR
- Dragon's Mouth streams Solana updates as the node processes them, with no memory of individual sessions.
- Network instability and server restarts can drop the stream, requiring custom reconnect and backfill logic.
yellowstone-grpc-clientnow ships this as a built-in feature, handling reconnects, backfills, and deduplication for you.- To enable it: set
set_reconnect_configon the builder. No other code changes required.
Challenge of stateless streams
A Dragon's Mouth stream is a persistent gRPC connection between your application and a Solana node, with no session state on either side. When a network blip, node restart, or connection reset drops it, there's no record of where you left off.
If your application accumulates state across slots (reconstructing a block, maintaining a real-time view of account changes), a reconnect gap means lost context that your application was actively building when the stream dropped.
For use cases where 100% completeness is non-negotiable, we built Fumarole: a persistent storage layer on top of Dragon's Mouth. Latency-sensitive workloads, however, need something different -- the fastest stream, with reliable reconnect and short-window gap recovery.
With no built-in solution available, teams have always had to build, debug, and maintain their own versions of it on top of Yellowstone gRPC. Until now.
Introducing auto-reconnect
The Yellowstone gRPC client ships built-in auto-reconnect in v13.1. One config call to enable it, and it handles:
- Automatic reconnects with configurable exponential backoff.
- Backfilling the data that arrived during the drop.
- Deduplication of events before they reach your application.
Any application that requires continuous, accurate Solana state benefits from this.
How it works
- You call
subscribe_once, and the stream begins. - In the background,
AutoReconnectmaintains a slot checkpoint that updates each time a slot completes viaBlockMeta. If the stream drops mid-slot, the checkpoint holds at the last fully completed slot. - When the stream encounters a recoverable error (network timeout, server unavailable, connection reset),
AutoReconnectwaits with exponential backoff and then resubscribes usingfrom_slot, starting a few slots before the stored checkpoint to ensure overlap with the gap. DedupStreamthen tracks which messages have already been delivered and filters out any that come through again during replay. Your application receives only the events it actually missed.
The reconnect logic is built as composable Stream layers:
AutoReconnect -- reconnects on failure, resumes from checkpoint
↓
DedupStream -- filters duplicates during replay window
↓
Streaming -- raw gRPC transportEach layer implements Rust's Stream trait and composes without spawned tasks or channels between components.
Reconnect window
The server's replay buffer determines how long you can be offline before auto-reconnect loses coverage. On Dragon's Mouth, that window is currently ~1,000 slots.
If you reconnect within that window, the client cleanly recovers the gap. Otherwise, the requested slot is no longer in the buffer, and the client reconnects from the current position.
Get started
The new feature is live on all Triton endpoints. Since yellowstone-grpc-client is open-source, once your provider ships v13.1, you're good to go.
If you want to build with the team shipping the future of Solana's read layer and don't have an endpoint yet, it takes ~2 minutes to sign up.
Once you have your endpoint and secret token, follow the steps below.
1. Update your dependency:
[dependencies]
yellowstone-grpc-client = "13.1.0" # or higher2. Enable auto-reconnect on the client builder:
use yellowstone_grpc_client::GeyserGrpcClient;
let mut client = GeyserGrpcClient::build_from_shared("https://your-endpoint")?
.x_token(Some("your-token"))?
.set_reconnect_config(ReconnectConfig::default())
.connect()
.await?;
let mut stream = client.subscribe_once(request).await?;
while let Some(msg) = stream.next().await {
match msg {
Ok(update) => handle(update),
Err(e) => {
// Only unrecoverable errors reach here.
// Transient failures are handled internally.
eprintln!("fatal: {e}");
break;
}
}
}Backoff and slot retention are both configurable:
| Parameter | Default | Description |
|---|---|---|
backoff.initial_interval |
10ms | First retry delay |
backoff.multiplier |
2.0 | Exponential growth factor |
backoff.max_retries |
3 | Retries per reconnect attempt |
slot_retention |
250 | Slots tracked for dedup (~100 seconds) |
let config = ReconnectConfig {
backoff: Backoff::new(
Duration::from_millis(100), // initial interval
2.0, // multiplier
10, // max retries
),
slot_retention: 250,
..Default::default()
};If you don't call set_reconnect_config, the client behaves exactly as before. Existing subscriptions work without modification.
Auto-reconnect covers the standard subscribe API. Deshred streams (subscribe_deshred) support is coming in a future release.