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. Vote transactions are ~70% of traffic—drop them if you do not need them.
rust
SubscribeRequestFilterTransactions { vote: Some(false), // Exclude ~70% vote txs failed: Some(false), // Exclude failed txs ..Default::default() }

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
if matches!(update.update_oneof, Some(UpdateOneof::Ping(_))) { subscribe_tx.send(SubscribeRequest { ping: Some(SubscribeRequestPing { id: 1 }), ..Default::default() }).await?; }
  • Gap recovery: use from_slot after reconnecting to avoid data loss (duplicates are acceptable).
rust
subscribe_request.from_slot = if tracked_slot > 0 { Some(tracked_slot) // Optionally subtract a small buffer to dodge reorgs } else { None };

Architecture patterns

  • Separate ingress and processing with channels to decouple network IO from business logic and add backpressure.
rust
let (tx, rx) = mpsc::channel::<SubscribeUpdate>(10_000); // ingress task reads stream and tx.send(...) // processing task consumes rx and does business logic
  • 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).
  • Exponential backoff on reconnect (start small, cap at a reasonable max).
  • Log/metric dropped updates to track slow-consumer conditions.

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