NOUVEAU : Helius acquiert Light Protocol
plugins Solana Geyser
Blog/Fondamentaux

Plugins Solana Geyser : le streaming de données à la vitesse de la lumière

Developer Experience Engineer0xIchigo sur X0xIchigo sur LinkedIn0xIchigo sur GitHub
13 min de lecture

De quoi parle cet article ?

Les plugins Geyser sont des composants modulaires conçus pour transmettre des données sur les comptes, les slots, les blocs et les transactions à des systèmes de stockage de données externes. Ils permettent ainsi aux développeurs de décharger un validateur des requêtes RPC (Remote Procedure Call). Les plugins Geyser offrent une solution flexible aux développeurs qui souhaitent personnaliser le streaming et le traitement de leurs données.

Dans cet article, nous examinerons en détail les plugins Solana Geyser. Nous commencerons par explorer les réplicas AccountsDB, une approche proposée pour la réplication des données et la gestion de la charge, finalement abandonnée au profit des plugins Geyser.

Nous expliquerons ensuite ce que sont les plugins Geyser, comment ils fonctionnent et comment ils sont structurés au moyen de l'interface de plugin.

Puis, nous présenterons les plugins Geyser courants et vous guiderons dans le processus complexe de création de votre propre plugin. Enfin, nous parlerons de Helius et de la manière dont nous simplifions le streaming de données sur Solana.

Réplicas AccountsDB : une approche abandonnée pour la réplication des données et la charge RPC

Solana a exploré plusieurs pistes pour répondre aux défis liés à une charge RPC élevée et à la réplication des données. L'une des approches prometteuses reposait sur des réplicas AccountsDB. Ces réplicas étaient conçus pour transférer les requêtes d'analyse de comptes du validateur principal vers des réplicas AccountsDB. Malgré son potentiel, ce système était intrinsèquement complexe et nécessitait un nouvel ensemble de services pour assurer la synchronisation entre le validateur principal et les réplicas. Cette proposition a finalement été abandonnée au profit du système de plugins Geyser, une solution plus simple à prendre en charge pour le client du validateur et offrant davantage de flexibilité aux développeurs lors de l'implémentation de leurs applications.

Mais que sont exactement les plugins Solana Geyser ?

Que sont les plugins Solana Geyser ?

Les plugins Solana Geyser fournissent un accès à faible latence aux données de Solana et peuvent servir des applications qui évitent d'avoir à effectuer des appels RPC sur les validateurs. Par exemple, si un validateur devait traiter de nombreux appels getProgramAccounts en succession rapide, ce trafic intense pourrait lui faire prendre du retard sur le réseau.

Les plugins Geyser résolvent ce problème en redirigeant les informations sur les comptes, les blocs, les slots et les transactions vers des systèmes de stockage de données externes, comme des bases de données relationnelles, des bases de données NoSQL ou Kafka.

Cette redirection des données permet aux services RPC de proposer des optimisations plus flexibles et ciblées, comme la mise en cache et l'indexation, aux utilisateurs qui souhaitent récupérer des données depuis ces systèmes externes.

Les plugins Geyser servent de pont entre Solana et les solutions externes de stockage de données. Ils permettent aux développeurs de décharger les validateurs d'une part importante des tâches de gestion des données, ce qui améliore les performances et réduit le risque de goulots d'étranglement.

Les plugins Geyser veillent à ce que les validateurs restent synchronisés avec le réseau, quel que soit le volume de trafic RPC.

L'interface de plugin Geyser

Les développeurs peuvent créer des plugins Geyser à l'aide de l'interface de plugin Solana Geyser. Cette interface donne accès aux comptes, aux transactions, aux slots, aux métadonnées de blocs et aux entrées. Elle est déclarée dans le crate solana-geyser-plugin-interface et définie par le trait GeyserPlugin.

Le trait définit des méthodes, chacune préfixée par update_, qui sont invoquées lors de la création de nouvelles données ou de la mise à jour de données existantes. Les plugins Geyser doivent également préciser leur comportement pendant les processus de chargement et de déchargement. Le trait décrit les méthodes essentielles qu'un plugin Geyser doit implémenter pour assurer un streaming efficace des données selon le comportement souhaité du plugin.

Code source

Code
pub trait GeyserPlugin:Any +Send +Sync +Debug {
    // Required method
    fn name(&self) -> &'static str;

    // Provided methods
    fn on_load(&mut self, _config_file: &str) ->Result<()> { ... }
    fn on_unload(&mut self) { ... }
    fn update_account(
        &self,
        account:ReplicaAccountInfoVersions<'_>,
        slot: Slot,
        is_startup:bool
    ) ->Result<()> { ... }
    fn notify_end_of_startup(&self) ->Result<()> { ... }
    fn update_slot_status(
        &self,
        slot: Slot,
        parent:Option,
        status:SlotStatus
    ) ->Result<()> { ... }
    fn notify_transaction(
        &self,
        transaction:ReplicaTransactionInfoVersions<'_>,
        slot: Slot
    ) ->Result<()> { ... }
    fn notify_entry(&self, entry:ReplicaEntryInfoVersions<'_>) ->Result<()> { ... }
    fn notify_block_metadata(
        &self,
        blockinfo:ReplicaBlockInfoVersions<'_>
    ) ->Result<()> { ... }
    fn account_data_notifications_enabled(&self) ->bool { ... }
    fn transaction_notifications_enabled(&self) ->bool { ... }
    fn entry_notifications_enabled(&self) ->bool { ... }
}

Déclaration du trait

Le trait GeyserPlugin sert d'interface fondamentale à tous les plugins de l'écosystème Solana Geyser. Il est déclaré comme trait public avec les limites de trait Any, Send, Sync et Debug de la bibliothèque standard Rust. Ces limites de trait sont les suivantes :

  • Any permet la réflexion de type, ce qui autorise la conversion descendante vers un type concret
  • Send indique que la propriété du type implémentant ce trait peut être transférée entre les threads
  • Sync implique que les références du type implémentant ce trait peuvent être partagées entre les threads
  • Debug permet de formater le type en sortie, notamment à des fins de débogage

Any et Debug ne sont pas particulièrement importants pour nous.

Ce qui compte vraiment, c'est que GeyserPlugin a besoin de Send et Sync pour garantir la sûreté du programme vis-à-vis des threads.

Méthode requise

Code
fn name(&self) -> &'static str;

La méthode name est requise pour tout type qui implémente GeyserPlugin. Cette méthode sert d'identifiant au plugin Geyser. Elle renvoie une tranche de chaîne statique représentant le nom du plugin Geyser.

Le fait que cette méthode et toutes les autres, à l'exception de on_load et on_unload, utilisent &self au lieu de &mut self est une nouveauté de la mise à jour 1.16 de Solana. Cela améliore considérablement les performances en supprimant la nécessité d'encapsuler le plugin Geyser dans un verrou de lecture-écriture et d'obtenir un verrou d'écriture à chaque appel de l'une de ses fonctions.

Méthodes fournies

Le trait comporte plusieurs méthodes fournies contenant des implémentations par défaut, qui peuvent être remplacées par les implémentations de GeyserPlugin.

Code
fn on_load(&mut self, _config_file: &str) ->Result<()> { ... }

La méthode on_load est le rappel invoqué lorsqu'un plugin est chargé par le système. Elle sert à effectuer toute initialisation requise par le plugin. Elle accepte une référence à un string représentant le chemin vers un fichier de configuration. La configuration doit être au format JSON5 et inclure un champ libpath indiquant le chemin complet de la bibliothèque partagée qui implémente cette interface.

Code
fn on_unload(&mut self) { ... }

La méthode on_unload est un rappel invoqué pour effectuer les opérations de nettoyage nécessaires avant qu'un plugin soit déchargé par le système.

Code
fn update_account(
        &self,
        account:ReplicaAccountInfoVersions<'_>,
        slot: Slot,
        is_startup:bool
    ) ->Result<()> { ... }

La méthode update_account est appelée lorsqu'un compte est mis à jour au niveau de confirmation processed, ce qui peut se produire plusieurs fois dans un même slot. Il est alors essentiel de suivre les slots confirmés afin d'obtenir les mises à jour de comptes validées dans la chaîne canonique.

La structure ReplicaAccountInfoVersions contient les métadonnées et les données du compte transmis en streaming.

Le paramètre slot pointe vers le slot dans lequel le compte est mis à jour.

Lorsque is_startup est vrai, cela indique que le compte est chargé depuis des instantanés au démarrage du validateur. Lorsque is_startup est faux, le compte est mis à jour pendant le traitement des transactions.

Code
fn notify_end_of_startup(&self) ->Result<()> { ... }

La méthode notify_end_of_startup est invoquée pour signaler la fin de la phase de démarrage. Cela se produit lorsque le validateur a restauré la base de données des comptes depuis des instantanés et que tous les comptes ont été mis à jour en conséquence.

Code
fn update_slot_status(
        &self,
        slot: Slot,
        parent:Option,
        status:SlotStatus
    ) ->Result<()> { ... }

La méthode update_slot_status est appelée lorsque le statut d'un slot est mis à jour. Elle accepte un Slot, un Option<u64> pour le slot parent et une instance de SlotStatus enum.

SlotStatus décrit les trois états possibles d'un slot dans Solana :

  • Processed - le slot le plus élevé sur lequel le nœud a travaillé. Tant que le slot n'est ni confirmé ni finalisé, il appartient à la chaîne que le validateur considère comme la plus susceptible de devenir canonique
  • Confirmed - le slot a reçu suffisamment de votes pour être considéré comme sécurisé et appartenir à la chaîne. Ce slot bénéficie du soutien d'une majorité qualifiée des validateurs de Solana
  • Rooted - le slot fait désormais partie de façon permanente de la blockchain, et toutes les autres versions ou forks de la chaîne doivent se construire à partir de ce slot. Cela signifie que toutes les branches du réseau descendent de ce bloc
Code
fn notify_transaction(
        &self,
        transaction:ReplicaTransactionInfoVersions<'_>,
        slot: Slot
    ) ->Result<()> { ... }

La méthode notify_transaction est appelée lorsqu'une transaction est traitée dans un slot afin de communiquer au plugin les détails de cette transaction.

ReplicaTransactionInfoVersions est un wrapper enum qui gère ReplicaTransactionInfo. Si la structure de RepicaTransactionInfo venait à changer, une nouvelle entrée enum serait créée pour la nouvelle version. Les implémentations du plugin seraient alors obligées de gérer ce changement en prenant en charge une nouvelle entrée d'enum. Actuellement, enum encapsule deux variantes :

  1. V0_0_1(&'a ReplicaTransactionInfo<'a>)
  2. V0_0_2(&'a ReplicaTransactionInfoV2<'a>)
Code
pub struct ReplicaTransactionInfo<'a> {
    pub signature: &'a Signature,
    pub is_vote: bool,
    pub transaction: &'a SanitizedTransaction,
    pub transaction_status_meta: &'a TransactionStatusMeta,
}

pub struct ReplicaTransactionInfoV2<'a> {
    pub signature: &'a Signature,
    pub is_vote: bool,
    pub transaction: &'a SanitizedTransaction,
    pub transaction_status_meta: &'a TransactionStatusMeta,
    pub index: usize,
}

La principale différence entre les variantes est que la seconde stocke l'index de la transaction dans le bloc.

Code
fn notify_entry(&self, entry:ReplicaEntryInfoVersions<'_>) ->Result<()> { ... }

notify_entry informe le plugin d'une nouvelle entrée. Cette méthode accepte une instance de ReplicaEntryInfoVersions, un wrapper conçu pour pérenniser la gestion de ReplicaEntryInfo. Elle contient actuellement la variante V0_0_1(&'a ReplicaEntryInfo<'a>).

Cette variante est une struct contenant des informations sur le slot de l'entrée, son index dans le bloc, le nombre de hachages depuis l'entrée précédente, le hachage SHA-256 de l'entrée et le nombre de transactions exécutées dans l'entrée.

Code
fn notify_block_metadata(
        &self,
        blockinfo:ReplicaBlockInfoVersions<'_>
    ) ->Result<()> { ... }

La méthode notify_block_metadata est appelée lorsque les métadonnées d'un bloc sont mises à jour. Elle accepte une instance de ReplicaBlockInfoVersions enum pour les informations du bloc. Cet enum est un wrapper pour les différentes versions de ReplicaBlockInfo, qui contiennent des informations sur le bloc, comme son slot, son hachage, ses récompenses, son horodatage, sa hauteur, etc.

Code
fn account_data_notifications_enabled(&self) ->bool { ... }
fn transaction_notifications_enabled(&self) ->bool { ... }
fn entry_notifications_enabled(&self) ->bool { ... }

Ces méthodes renvoient des valeurs booléennes indiquant si le plugin souhaite activer les notifications respectivement pour les données de comptes, les transactions et les entrées.

Remarque sur les niveaux d'engagement

Geyser envoie immédiatement les mises à jour des données de comptes et des transactions dès qu'elles sont traitées. Cette approche améliore la vitesse d'indexation de bout en bout, mais comporte un risque qu'un slot traité soit ignoré.

Un slot ignoré désigne un slot passé qui n'a produit aucun bloc, soit parce que le leader était hors ligne, soit parce que le fork contenant le slot a été abandonné au profit d'une meilleure alternative. Il est essentiel que les systèmes de stockage recevant les données en streaming tiennent compte de cette possibilité et gèrent les mises à jour en conséquence.

Plugins Solana Geyser courants

Les développeurs disposent d'un large éventail de plugins Solana Geyser qu'ils peuvent utiliser et même forker pour répondre à leurs besoins spécifiques. Parmi les plugins notables figurent :

  • PostgreSQL Plugin : pour gérer et interroger les données avec PostgreSQL
  • gRPC Service Streaming Plugin : pour transmettre en streaming les mises à jour de comptes Solana à un service gRPC
  • RabbitMQ Producer Plugin : pour faciliter la mise en file d'attente des messages avec RabbitMQ
  • Kafka Producer Plugin : pour transmettre des données en streaming avec Kafka
  • Amazon SQS Plugin : pour mettre en file d'attente des messages à l'aide du Simple Queue Service d'Amazon
  • Google BigTable Plugin : pour gérer et interroger les données avec Google BigTable

Ces plugins peuvent être adaptés à une multitude de cas d'usage.

Par exemple, Clockwork s'est appuyé sur un plugin Geyser pour planifier des transactions et créer des programmes Solana automatisés et pilotés par des événements. Bien que le projet ait cessé ses activités, son code open source reste une ressource précieuse consultable sur son GitHub.

Parmi les autres cas d'usage possibles, les plugins Geyser peuvent servir à surveiller les soldes des comptes sur une plateforme DeFi, à fournir des indicateurs sur l'état du réseau ou à suivre les événements d'une chaîne d'approvisionnement en temps réel.

Créez votre propre plugin Solana Geyser

Voici quelques ressources et composants pour créer votre propre plugin :

Le scaffold de plugin Solana Geyser

Le scaffold de plugin Solana Geyser est la ressource la plus simple pour vous lancer dans le développement de plugins Solana Geyser. Ce scaffold sert de modèle minimaliste qui journalise les interactions entre le gestionnaire de plugins et le plugin lui-même. C'est un excellent point de départ pour vous familiariser avec le workflow du plugin et les techniques de débogage.

Le gestionnaire de plugins

Le gestionnaire de plugins est le composant central qui orchestre le cycle de vie et les interactions de tous les plugins Geyser. Il peut charger et décharger dynamiquement des plugins pendant l'exécution, ce qui apporte davantage de flexibilité et de modularité.

Pendant l'exécution, le gestionnaire de plugins transmet à votre plugin le chemin du fichier de configuration. Les plugins Geyser disposent ainsi de paramètres personnalisables qui peuvent être modifiés sans changer leur code.

Pour intégrer un plugin à un validateur, vous devez spécifier le chemin de la bibliothèque dynamique au moyen du paramètre --geyser-plugin-config. Cela indique au validateur où trouver le plugin et sa configuration associée.

Au minimum, le fichier de configuration doit être au format JSON et contenir le chemin vers la bibliothèque dynamique du plugin Geyser, soit .so sous Linux. Un fichier de configuration minimal se présenterait comme suit :

Code
{
    "libpath": "/.so"
}

Créer un plugin Geyser à partir de zéro

Si vous souhaitez sortir des sentiers battus et créer votre propre plugin Geyser sans utiliser le scaffold ni modifier un plugin existant, vous devez le programmer avec l'interface de plugin Geyser.

Un plugin doit implémenter le trait GeyserPlugin pour fonctionner avec le runtime. De plus, la bibliothèque dynamique doit exporter une fonction « C » _create_plugin qui crée l'implémentation du plugin.

Vous pourriez, par exemple, créer un plugin Webhook qui implémente le trait GeyserPlugin :

Code
#[no_mangle]
#[allow(improper_ctypes_definitions)]
/// # Safety
///
/// This function returns the WebhookPlugin pointer as trait GeyserPlugin.
pub unsafe extern "C" fn _create_plugin() -> *mut dyn GeyserPlugin {
    let plugin = WebhookPlugin::new();
    let plugin: Box = Box::new(plugin);
    Box::into_raw(plugin)
}

Ici, nous créons une fonction publique unsafe qui utilise la convention d'appel C, extern "C", ce qui la rend compatible avec C et d'autres langages. La fonction fn _create*_*plugin() -> *mut dyn GeyserPlugin elle-même renvoie un pointeur brut mutable vers un dynGeyserPlugin, qui correspond au trait GeyserPlugin. Le corps de la fonction crée une nouvelle instance de WebhookPlugin, place cette instance dans une boîte en tant qu'objet trait, puis convertit cet objet trait encapsulé en pointeur brut afin que la fonction puisse le renvoyer.

Les étapes de création de votre propre plugin Geyser sont donc les suivantes :

  • Créez votre plugin en implémentant l'interface de plugin Solana Geyser
  • Récupérez la bibliothèque dynamique (fichier .so) depuis le dossier target/release ou target/debug
  • Créez un fichier geyser-config.json, qui doit contenir le chemin vers la bibliothèque dynamique du plugin Geyser dans un champ « libpath »
  • Démarrez votre validateur avec le flag --geyser-plugin-config geyser-config.json

Ces étapes semblent assez simples, mais l'exécution et la maintenance d'un plugin Solana Geyser peuvent en pratique s'avérer particulièrement laborieuses.

Streaming Geyser avec Helius

Helius est réputé pour offrir une expérience développeur inégalée sur Solana. En se consacrant exclusivement à Solana, Helius a acquis une vaste expérience, relevé de nombreux défis et facilité de multiples intégrations à grande échelle. Helius est particulièrement bien placé pour résoudre tous les problèmes auxquels un développeur peut être confronté.

Chez Helius, nous gérons les plugins Geyser de plusieurs équipes très performantes de l'écosystème Solana. Nous exploitons des clusters Geyser spécialisés avec une redondance et une tolérance aux pannes renforcées, afin que vous n'ayez jamais à vous soucier de données manquantes ou d'interruptions de service. Notre accès programmatique par API vous permet de modifier dynamiquement vos plugins Geyser sans jamais vous préoccuper de leur fiabilité. La gestion des plugins Geyser est souvent une tâche intimidante, car vous devez garantir la cohérence, la fiabilité et la disponibilité des données. Pourquoi ne pas laisser Helius s'en charger pour vous ?

Si le streaming Geyser vous intéresse, commandez un nœud dédié depuis votre tableau de bord Helius ou contactez-nous sur Discord pour commencer dès aujourd'hui.

Conclusion

Félicitations !

Dans cet article, nous avons abordé les complexités de la réplication des données et de la gestion de la charge RPC en examinant les plugins Solana Geyser. Comprendre ce système n'est pas chose aisée : il repose sur une architecture sophistiquée et très peu documentée, mais offre aux développeurs Solana de nombreuses possibilités de personnalisation et d'optimisation des performances.

Les connaissances acquises dans cet article sont inestimables, en particulier si vous êtes un développeur ou une équipe souhaitant créer ou gérer des applications hautes performances sur Solana. Il est essentiel de comprendre les plugins Geyser, car ils offrent une solution évolutive et fiable à l'écosystème Solana.

Si vous avez lu jusqu'ici, anon, merci !

Ressources supplémentaires

Abonnez-vous à Helius

Suivez les dernières actualités du développement sur Solana et recevez une notification à chaque publication