Geyser gRPC - Best Practices
This guide covers essential patterns for building robust, production-ready applications with Geyser gRPC. Solana Stream SDK supports Geyser gRPC.
Getting started
- Start simple: begin with slot subscriptions to get familiar with streaming before adding complex filters.
- Use filters wisely: subscribe only to what you need to reduce bandwidth/CPU.
rust
Connection management
- Automatic reconnection: expect network issues; reconnect with exponential backoff.
- Ping/pong handling: Yellowstone gRPC servers send ping. Always respond with pong or the server can close the connection (common cause of ~30s disconnects).
rust
- Gap recovery: use
from_slotafter reconnecting to avoid data loss (duplicates are acceptable).
rust
Architecture patterns
- Separate ingress and processing with channels to decouple network IO from business logic and add backpressure.
rust
- Use bounded channels: choose capacity based on processing speed and data-loss tolerance (smaller = less memory, more drops; larger = more memory, fewer drops).
Performance and resiliency
- Monitor processing latency and log when thresholds are exceeded.
- Batch DB writes; flush on batch size or interval.
- Use async IO for external calls; offload heavy compute to worker tasks/threads.
- Reuse subscription requests to reduce allocations.
Error handling
- Distinguish error types (stream vs processing vs channel).
- Repair an invalid request before retrying it; use a pause-aware exponential backoff only when a response calls for retry.
- Log/metric dropped updates to track slow-consumer conditions.
Repair before retry and pause-aware backoff
Keep the first gRPC error before a reconnect loop replaces it. Record its status and message, plus any
x-erpc-error-code, x-erpc-error-reason, x-erpc-doc-url, x-erpc-retry-after-seconds, and x-erpc-retry-at metadata that are present. An early connection close can look like a generic transport error, so it may not include every trailer.Repair an invalid request, rejected filter shape, or IP-registration mismatch before retrying. For an ERPC pause, wait for the returned deadline; it is more specific than a local backoff. Use a bounded exponential backoff for availability failures and other retryable responses. Do not repeatedly retry an unchanged request merely because it returned a non-zero status.
Resource use and processing capacity
Shared protections keep one workload from consuming delivery capacity needed by other ERPC customers. A stream can become slow because of effective bandwidth, CPU, memory, the consumer queue, database writes, or external calls. Sufficient resources must match the workload; a slow receiver does not by itself identify which part is constrained.
Measure the peak data rate at the consumer. Convert MB/s to Mbps with
MB/s × 8, then leave headroom above the measured peak. There is no fixed unmeasured minimum. Keep receive work short, hand updates to a bounded queue, and scale or reduce the subscription if processing cannot keep up. A bounded queue limits memory use; it cannot resolve a sustained slow consumer.Data management
- Handle duplicates when using
from_slot(time-bounded cache or DB constraints). - Pick commitment per use case:
- processed: fastest dashboards, may roll back
- confirmed: good default for most apps/indexers
- finalized: when absolute certainty is required
Dynamic subscriptions
Use the bidirectional stream to update subscriptions at runtime (hot-swap filters, expand coverage) without reconnecting.
Production checklist
- ✅ Auto reconnection with exponential backoff
- ✅ Gap recovery using
from_slot - ✅ Ping/pong handling (avoid 30s disconnects)
- ✅ Separate ingress and processing with bounded channels/backpressure
- ✅ Error logging and metrics/alerting
- ✅ Processing latency tracking
- ✅ Duplicate handling and filter optimization
- ✅ Batch writes (if persisting)
- ✅ Graceful shutdown and health checks
Additional resources
- Solana Stream SDK (Geyser gRPC support): https://github.com/ValidatorsDAO/solana-stream






