---
title: "Geyser gRPC - Best Practices"
description: "Essential tips for production-grade Geyser gRPC clients, including ping/pong, reconnection, and filters. Solana Stream SDK supports Geyser gRPC."
canonical: "https://erpc.global/en/doc/geyser-grpc/best-practices/"
locale: "en"
---

# 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
SubscribeRequestFilterTransactions {
    vote: Some(false),   // Exclude 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).  
- 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
