Geyser gRPC - Best practice

Questa guida copre i pattern essenziali per costruire applicazioni robuste e pronte per la produzione con Geyser gRPC. Solana Stream SDK supporta Geyser gRPC.

Per iniziare

  • Inizia in modo semplice: parti dalle sottoscrizioni di slot per familiarizzare con lo streaming prima di aggiungere filtri complessi.
  • Usa i filtri con criterio: sottoscrivi solo ciò che ti serve per ridurre banda/CPU. Le transazioni di voto sono circa il 70% del traffico: eliminale se non ti servono.
rust
SubscribeRequestFilterTransactions {
    vote: Some(false),   // Exclude ~70% vote txs
    failed: Some(false), // Exclude failed txs
    ..Default::default()
}

Gestione della connessione

  • Riconnessione automatica: aspettati problemi di rete; riconnettiti con backoff esponenziale.
  • Gestione di ping/pong: i server Yellowstone gRPC inviano ping. Rispondi sempre con pong, altrimenti il server può chiudere la connessione (causa comune delle disconnessioni dopo ~30s).
rust
if matches!(update.update_oneof, Some(UpdateOneof::Ping(_))) {
    subscribe_tx.send(SubscribeRequest {
        ping: Some(SubscribeRequestPing { id: 1 }),
        ..Default::default()
    }).await?;
}
  • Recupero dei gap: usa from_slot dopo la riconnessione per evitare la perdita di dati (i duplicati sono accettabili).
rust
subscribe_request.from_slot = if tracked_slot > 0 {
    Some(tracked_slot) // Optionally subtract a small buffer to dodge reorgs
} else {
    None
};

Pattern architetturali

  • Separa l'ingresso dall'elaborazione con i canali per disaccoppiare l'IO di rete dalla business logic e aggiungere 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
  • Usa canali bounded: scegli la capacità in base alla velocità di elaborazione e alla tolleranza alla perdita di dati (più piccola = meno memoria, più drop; più grande = più memoria, meno drop).

Prestazioni e resilienza

  • Monitora la latenza di elaborazione e registra un log quando vengono superate le soglie.
  • Raggruppa le scritture sul DB in batch; esegui il flush in base alla dimensione del batch o a intervalli.
  • Usa IO asincrono per le chiamate esterne; scarica i calcoli pesanti su task/thread worker.
  • Riutilizza le richieste di sottoscrizione per ridurre le allocazioni.

Gestione degli errori

  • Distingui i tipi di errore (stream vs elaborazione vs canale).
  • Backoff esponenziale alla riconnessione (inizia con valori bassi, con un tetto massimo ragionevole).
  • Registra log/metriche degli update scartati per tracciare le condizioni di consumer lento.

Gestione dei dati

  • Gestisci i duplicati quando usi from_slot (cache a tempo limitato o vincoli sul DB).
  • Scegli il commitment in base al caso d'uso:
    • processed: dashboard più veloci, può essere annullato
    • confirmed: buona scelta predefinita per la maggior parte di app/indexer
    • finalized: quando è richiesta certezza assoluta

Sottoscrizioni dinamiche

Usa lo stream bidirezionale per aggiornare le sottoscrizioni a runtime (hot-swap dei filtri, espansione della copertura) senza riconnetterti.

Checklist di produzione

  • ✅ Riconnessione automatica con backoff esponenziale
  • ✅ Recupero dei gap con from_slot
  • ✅ Gestione di ping/pong (evita le disconnessioni a 30s)
  • ✅ Ingresso ed elaborazione separati con canali bounded/backpressure
  • ✅ Logging degli errori e metriche/alerting
  • ✅ Tracciamento della latenza di elaborazione
  • ✅ Gestione dei duplicati e ottimizzazione dei filtri
  • ✅ Scritture in batch (se persisti i dati)
  • ✅ Shutdown graceful e health check

Risorse aggiuntive