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.
rust
SubscribeRequestFilterTransactions {
    vote: Some(false),   // Exclude 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).
  • Correggi una richiesta non valida prima di riprovarla; usa backoff esponenziale consapevole della pausa solo quando una risposta indica di riprovare.
  • Registra log/metriche degli update scartati per tracciare le condizioni di consumer lento.

Correggi prima di riprovare e backoff consapevole della pausa

Conserva il primo errore gRPC prima che un ciclo di riconnessione lo sostituisca. Registra stato e messaggio, insieme ai metadati presenti x-erpc-error-code, x-erpc-error-reason, x-erpc-doc-url, x-erpc-retry-after-seconds e x-erpc-retry-at. Una chiusura anticipata può sembrare un errore di trasporto generico e quindi non includere tutti i trailer.
Correggi una richiesta non valida, una forma di filtro rifiutata o una discrepanza nella registrazione IP prima di riprovare. In caso di pausa ERPC, attendi la scadenza restituita; è più specifica del backoff locale. Usa backoff esponenziale limitato per errori di disponibilità e altre risposte ritentabili. Non ripetere una richiesta invariata solo perché ha restituito uno stato diverso da zero.

Uso delle risorse e capacità di elaborazione

Le protezioni Shared evitano che un carico consumi la capacità di consegna necessaria agli altri clienti ERPC. Uno stream può diventare lento per banda effettiva, CPU, memoria, coda del consumer, scritture nel database o chiamate esterne. Le risorse devono corrispondere al carico; un ricevitore lento non identifica da solo la parte vincolata.
Misura la velocità di picco dei dati presso il consumer. Converti MB/s in Mbps con MB/s × 8 e lascia margine sopra il picco misurato. Non esiste un minimo fisso non misurato. Mantieni breve il lavoro di ricezione, passa gli update a una coda limitata e scala o riduci la sottoscrizione se l'elaborazione non riesce a tenere il passo. Una coda limitata limita la memoria; non risolve un consumer persistentemente 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