
Plugins Geyser da Solana: streaming de dados à velocidade da luz
Índice
- Sobre o que é este artigo?
- Réplicas do AccountsDB: uma abordagem abandonada para replicação de dados e carga de RPC
- O que são os plugins Geyser da Solana?
- A interface de plugins Geyser
- Código-fonte
- Declaração do trait
- Método obrigatório
- Métodos fornecidos
- Observação sobre os níveis de compromisso
- Plugins Geyser comuns da Solana
- Crie seu próprio plugin Geyser da Solana
- O scaffold de plugins Geyser da Solana
- O gerenciador de plugins
- Como criar um plugin Geyser do zero
- Streaming Geyser da Helius
- Conclusão
- Recursos adicionais
Sobre o que é este artigo?
Os plugins Geyser são componentes modulares projetados para transmitir dados sobre contas, slots, blocos e transações para armazenamentos de dados externos, permitindo que os desenvolvedores removam a carga de RPC (Chamada de Procedimento Remoto) de um validador. Os plugins Geyser oferecem uma solução flexível para desenvolvedores que desejam personalizar o streaming e o processamento de dados.
Neste artigo, vamos explorar em detalhes os plugins Geyser da Solana. Começaremos pelas réplicas do AccountsDB, uma abordagem proposta para replicação de dados e gerenciamento de carga que acabou sendo abandonada em favor dos plugins Geyser.
Em seguida, explicaremos o que são os plugins Geyser, como funcionam e como são estruturados por meio da interface de plugins.
Depois, abordaremos os plugins Geyser mais comuns e guiaremos você pelo complexo processo de criação do seu próprio plugin. Por fim, falaremos sobre a Helius e como simplificamos o streaming de dados na Solana.
Réplicas do AccountsDB: uma abordagem abandonada para replicação de dados e carga de RPC
A Solana explorou vários caminhos para enfrentar o desafio da alta carga de RPC e da replicação de dados. Uma abordagem promissora foi o uso de réplicas do AccountsDB. Essas réplicas foram projetadas para transferir as solicitações de varredura de contas do validador principal para réplicas do AccountsDB. Embora promissor, o sistema era inerentemente complexo e exigia um novo conjunto de serviços para garantir a sincronização entre o validador principal e as réplicas. Por fim, essa proposta foi abandonada em favor do sistema de plugins Geyser — uma solução mais simples de ser compatibilizada com o cliente validador e que oferece aos desenvolvedores mais flexibilidade ao implementar suas aplicações.
Então, o que exatamente são os plugins Geyser da Solana?
O que são os plugins Geyser da Solana?
Os plugins Geyser da Solana oferecem acesso de baixa latência aos dados da Solana e podem atender aplicações, eliminando a necessidade de fazer chamadas RPC aos validadores. Por exemplo, se um validador precisasse atender a várias chamadas getProgramAccounts em rápida sucessão, esse tráfego intenso poderia fazer com que ele ficasse atrasado em relação à rede.
Os plugins Geyser resolvem esse problema redirecionando informações sobre contas, blocos, slots e transações para armazenamentos de dados externos, como bancos de dados relacionais, bancos de dados NoSQL ou Kafka.
Esse redirecionamento de dados permite que os serviços RPC ofereçam otimizações mais flexíveis e direcionadas, como cache e indexação, para quem precisa buscar dados nesses armazenamentos externos.
Os plugins Geyser atuam como uma ponte entre a Solana e soluções externas de armazenamento de dados. Eles permitem que os desenvolvedores retirem dos validadores uma parte significativa das tarefas de gerenciamento de dados, melhorando o desempenho e reduzindo o risco de possíveis gargalos.
Os plugins Geyser garantem que os validadores permaneçam sincronizados com a rede, independentemente do volume de tráfego RPC.
A interface de plugins Geyser
Os desenvolvedores podem criar plugins Geyser usando a interface de plugins Geyser da Solana. A interface fornece acesso a contas, transações, slots, metadados de blocos e entradas. Ela é declarada no crate solana-geyser-plugin-interface e definida pelo trait GeyserPlugin.
O trait define métodos, cada um com o prefixo update_, que são invocados sempre que novos dados são criados ou dados existentes são atualizados. Os plugins Geyser também precisam especificar seu comportamento durante os processos de carregamento e descarregamento. O trait descreve os métodos essenciais que um plugin Geyser deve implementar para garantir um streaming de dados eficiente de acordo com o comportamento desejado para o plugin.
Código-fonte
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 { ... }
}Declaração do trait
O trait GeyserPlugin serve como interface fundamental para todos os plugins do ecossistema de plugins Geyser da Solana. Ele é declarado como um trait público com os limites de trait Any, Send, Sync e Debug da biblioteca padrão do Rust. Os limites de trait são os seguintes:
Anypermite a reflexão de tipos, possibilitando o downcasting para um tipo concretoSendindica que a propriedade do tipo que implementa esse trait pode ser transferida entre threadsSyncimplica que as referências do tipo que implementa esse trait podem ser compartilhadas entre threadsDebugpermite formatar o tipo para saída, especificamente para fins de depuração
Any e Debug não são tão importantes para nós.
O que importa de verdade é que GeyserPlugin precisa de Send e Sync para tornar o programa seguro para threads.
Método obrigatório
fn name(&self) -> &'static str;O método name é obrigatório para qualquer tipo que implemente GeyserPlugin. Esse método atua como um identificador do plugin Geyser. Ele retorna uma fatia de string estática que representa o nome do plugin Geyser.
O fato de esse método e todos os outros métodos, exceto on_load e on_unload, usarem &self em vez de &mut self é uma novidade da atualização 1.16 da Solana. Isso melhora drasticamente o desempenho ao eliminar a necessidade de envolver o plugin Geyser em um bloqueio de leitura e escrita e obter um bloqueio de escrita toda vez que uma de suas funções é chamada.
Métodos fornecidos
O trait tem vários métodos fornecidos que contêm implementações padrão, que podem ser sobrescritas pelas implementações de GeyserPlugin.
fn on_load(&mut self, _config_file: &str) ->Result<()> { ... }O método on_load é o callback invocado quando um plugin é carregado pelo sistema e é usado para qualquer inicialização exigida pelo plugin. Ele aceita uma referência a um string que representa o caminho para um arquivo de configuração. A configuração deve estar no formato JSON5 e incluir um campo libpath que indique o nome completo do caminho da biblioteca compartilhada que implementa essa interface.
fn on_unload(&mut self) { ... }O método on_unload é um callback invocado para realizar qualquer limpeza antes que um plugin seja descarregado pelo sistema.
fn update_account(
&self,
account:ReplicaAccountInfoVersions<'_>,
slot: Slot,
is_startup:bool
) ->Result<()> { ... }O método update_account é chamado quando uma conta é atualizada no nível de confirmação processed, o que pode acontecer várias vezes dentro de um slot. Nesse caso, é essencial acompanhar os slots que são confirmados para obter as atualizações de conta registradas na cadeia canônica.
A struct ReplicaAccountInfoVersions contém os metadados e os dados da conta transmitida.
O parâmetro slot aponta para o slot no qual a conta está sendo atualizada.
Quando is_startup é true, isso indica que a conta foi carregada de snapshots durante a inicialização do validador. Quando is_startup é false, a conta é atualizada durante o processamento de transações.
fn notify_end_of_startup(&self) ->Result<()> { ... }O método notify_end_of_startup é invocado para sinalizar o fim da fase de inicialização. Isso ocorre quando o validador restaura o banco de dados de contas a partir de snapshots e todas as contas são atualizadas de acordo.
fn update_slot_status(
&self,
slot: Slot,
parent:Option,
status:SlotStatus
) ->Result<()> { ... }O método update_slot_status é chamado quando o status de um slot é atualizado. Ele aceita um Slot, um Option<u64> para o slot pai e uma instância de SlotStatus enum.
O SlotStatus descreve os três estados de um slot na Solana:
Processed- o slot mais alto em que o nó trabalhou. Embora o slot ainda não esteja confirmado nem finalizado, ele faz parte da cadeia que o validador considera mais propensa a se tornar canônicaConfirmed- o slot recebeu votos suficientes para ser considerado seguro e fazer parte da cadeia. Esse slot tem o apoio de uma supermaioria dos validadores da SolanaRooted- o slot agora é uma parte permanente da blockchain, e todas as outras versões ou bifurcações da cadeia devem ser construídas sobre ele. Isso significa que todas as ramificações da rede descendem desse bloco
fn notify_transaction(
&self,
transaction:ReplicaTransactionInfoVersions<'_>,
slot: Slot
) ->Result<()> { ... }O método notify_transaction é chamado quando uma transação é processada em um slot, informando ao plugin os detalhes da transação.
ReplicaTransactionInfoVersions é um wrapper enum que processa ReplicaTransactionInfo. Se houvesse uma alteração na estrutura de RepicaTransactionInfo, uma nova entrada enum seria criada para a versão mais recente. Isso obrigaria as implementações do plugin a processar a mudança acomodando uma nova entrada de enum. Atualmente, enum encapsula duas variantes:
V0_0_1(&'a ReplicaTransactionInfo<'a>)V0_0_2(&'a ReplicaTransactionInfoV2<'a>)
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,
}A principal diferença entre as variantes é que a segunda armazena o índice da transação no bloco.
fn notify_entry(&self, entry:ReplicaEntryInfoVersions<'_>) ->Result<()> { ... }notify_entry notifica o plugin sobre uma nova entrada. Ele aceita uma instância de ReplicaEntryInfoVersions, que é um wrapper para preparar o processamento de ReplicaEntryInfo para o futuro. Atualmente, ele contém a variante V0_0_1(&'a ReplicaEntryInfo<'a>).
Essa variante é uma struct que contém informações sobre o slot da entrada, seu índice no bloco, o número de hashes desde a entrada anterior, o hash SHA-256 da entrada e o número de transações executadas na entrada.
fn notify_block_metadata(
&self,
blockinfo:ReplicaBlockInfoVersions<'_>
) ->Result<()> { ... }O método notify_block_metadata é chamado quando os metadados de um bloco são atualizados. Ele aceita uma instância de ReplicaBlockInfoVersions enum com as informações do bloco. Esse enum é um wrapper para as diversas versões de ReplicaBlockInfo, que contêm informações sobre o bloco, como slot, hash, recompensas, horário do bloco, altura do bloco etc.
fn account_data_notifications_enabled(&self) ->bool { ... }
fn transaction_notifications_enabled(&self) ->bool { ... }
fn entry_notifications_enabled(&self) ->bool { ... }Esses métodos retornam valores booleanos que indicam se o plugin deseja habilitar notificações para dados de contas, transações e entradas, respectivamente.
Observação sobre os níveis de compromisso
O Geyser envia imediatamente as atualizações de dados de contas e transações assim que elas são processadas. Isso beneficia a velocidade de indexação de ponta a ponta. No entanto, existe o risco de um slot processado ser ignorado.
Um slot ignorado é um slot passado que não produziu um bloco, seja porque o líder estava offline ou porque a bifurcação que continha o slot foi abandonada em favor de uma alternativa melhor. É fundamental que os sistemas de armazenamento de dados que recebem o streaming reconheçam essa possibilidade e gerenciem as atualizações de acordo.
Plugins Geyser comuns da Solana
Há uma grande variedade de plugins Geyser da Solana disponíveis para os desenvolvedores usarem e até criarem forks para atender às suas necessidades específicas. Alguns plugins de destaque incluem:
PostgreSQL Plugin: para gerenciar e consultar dados usando PostgreSQLgRPC Service Streaming Plugin: para transmitir atualizações de contas da Solana a um serviço gRPCRabbitMQ Producer Plugin: para facilitar o enfileiramento de mensagens com RabbitMQKafka Producer Plugin: para transmitir dados usando KafkaAmazon SQS Plugin: para enfileiramento de mensagens usando o Simple Queue Service da AmazonGoogle BigTable Plugin: para gerenciar e consultar dados usando o Google BigTable
Esses plugins podem ser adaptados para atender a uma infinidade de casos de uso.
A Clockwork, por exemplo, usou um plugin Geyser para agendar transações e criar programas Solana automatizados e orientados a eventos. Embora o projeto tenha sido encerrado, seu código aberto continua sendo um recurso valioso e pode ser consultado no GitHub.
Outros casos de uso possíveis incluem usar plugins Geyser para monitorar saldos de contas em uma plataforma DeFi, fornecer métricas de integridade da rede ou monitorar eventos da cadeia de suprimentos em tempo real.
Crie seu próprio plugin Geyser da Solana
Confira alguns recursos e componentes para criar seu próprio plugin:
O scaffold de plugins Geyser da Solana
O scaffold de plugins Geyser da Solana é o recurso mais fácil para começar sua jornada no desenvolvimento de plugins Geyser da Solana. Esse scaffold funciona como um modelo minimalista que registra as interações entre o gerenciador de plugins e o próprio plugin. É um excelente ponto de partida para conhecer o fluxo de trabalho do plugin e as técnicas de depuração.
O gerenciador de plugins
O gerenciador de plugins é o componente central que controla o ciclo de vida e as interações de todos os plugins Geyser. Ele pode carregar e descarregar plugins dinamicamente durante a execução, proporcionando maior flexibilidade e modularidade.
Durante a execução, o gerenciador de plugins transmite o caminho do arquivo de configuração ao seu plugin. Isso permite usar configurações personalizáveis nos plugins Geyser, que podem ser modificadas sem alterar o código do plugin.
Para integrar um plugin a um validador, você precisará especificar o caminho da biblioteca dinâmica usando o parâmetro --geyser-plugin-config. Isso informa ao validador onde encontrar o plugin e sua configuração associada.
No mínimo, o arquivo de configuração deve estar no formato JSON e conter o caminho para a biblioteca dinâmica do plugin Geyser — .so no Linux. Um arquivo de configuração mínimo seria assim:
{
"libpath": "/.so"
}Como criar um plugin Geyser do zero
Se você quiser seguir um caminho menos convencional e criar seu próprio plugin Geyser sem usar o scaffold nem modificar um plugin existente, precisará programá-lo usando a interface de plugins Geyser.
Um plugin deve implementar o trait GeyserPlugin para funcionar com o runtime. Além disso, a biblioteca dinâmica deve exportar uma função “C” _create_plugin que crie a implementação do plugin.
Um exemplo seria criar um plugin de Webhook que implemente o trait GeyserPlugin:
#[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)
}Aqui, estamos criando uma função pública unsafe que usa a convenção de chamada C, extern "C", tornando-a compatível com C e outras linguagens. A função fn _create*_*plugin() -> *mut dyn GeyserPlugin retorna um ponteiro bruto mutável para um dynGeyserPlugin, que é o trait GeyserPlugin. O corpo da função cria uma nova instância de WebhookPlugin, coloca essa instância em uma box como um objeto de trait e converte o objeto de trait em box para um ponteiro bruto, permitindo que ele seja retornado pela função.
Portanto, as etapas para criar seu próprio plugin Geyser são:
- Crie seu plugin implementando a interface de plugins Geyser da Solana
- Obtenha a biblioteca dinâmica (arquivo
.so) na pastatarget/releaseoutarget/debug - Crie um arquivo
geyser-config.json, que deve conter o caminho para a biblioteca dinâmica do plugin Geyser em um campo “libpath” - Inicie seu validador com a flag
--geyser-plugin-config geyser-config.json
Essas etapas parecem relativamente simples. No entanto, o processo de executar e manter um plugin Geyser da Solana pode ser bastante trabalhoso.
Streaming Geyser da Helius
A Helius é reconhecida por oferecer uma experiência de desenvolvimento incomparável na Solana. Esse foco exclusivo na Solana proporcionou à Helius uma vasta experiência, após superar uma grande variedade de desafios e viabilizar inúmeras integrações em larga escala. A Helius está em uma posição única para solucionar qualquer problema que um desenvolvedor possa enfrentar.
Na Helius, gerenciamos plugins Geyser para várias equipes de alto desempenho do ecossistema Solana. Operamos clusters Geyser especializados com redundância e tolerância a falhas adicionais para garantir que você nunca precise se preocupar com perda de dados ou indisponibilidade. Nosso acesso programático por API permite modificar seus plugins Geyser dinamicamente sem se preocupar com a confiabilidade. Gerenciar plugins Geyser costuma ser uma tarefa desafiadora, pois você é responsável por garantir a consistência, a confiabilidade e a disponibilidade dos dados. Por que não deixar a Helius cuidar disso para você?
Se você tem interesse em streaming Geyser, solicite um nó dedicado no seu painel da Helius ou entre em contato conosco pelo Discord para começar hoje mesmo.
Conclusão
Parabéns!
Neste artigo, exploramos as complexidades da replicação de dados e do gerenciamento de carga de RPC analisando os plugins Geyser da Solana. Entender esse sistema não é uma tarefa fácil — trata-se de uma arquitetura sofisticada com pouquíssima documentação, mas que oferece inúmeras oportunidades de personalização e otimização de desempenho para desenvolvedores da Solana.
O conhecimento adquirido neste artigo é inestimável, especialmente se você é um desenvolvedor ou faz parte de uma equipe que deseja criar ou gerenciar aplicações de alto desempenho na Solana. É fundamental entender os plugins Geyser, pois eles oferecem uma solução escalável e confiável para o ecossistema Solana.
Se você chegou até aqui, anon, muito obrigado!
Recursos adicionais
Artigos relacionados
Assine a Helius
Acompanhe as novidades mais recentes do desenvolvimento Solana e receba atualizações quando publicarmos


