mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-08-21 12:58:11 +00:00
fix grpc reconnect after stream death and skip bad protobuf updates
clear active_subscription when the yellowstone stream task exits or subscribe fails, so callers are not stuck on "already subscribed" after a disconnect. replace unwrap/expect in grpc pool parsing with validation and skip malformed account or transaction updates instead of panicking the whole stream.
This commit is contained in:
+85
-18
@@ -1,5 +1,6 @@
|
||||
use super::types::{AccountPretty, BlockMetaPretty, TransactionPretty};
|
||||
use crate::streaming::event_parser::common::high_performance_clock::get_high_perf_clock;
|
||||
use log::warn;
|
||||
use solana_sdk::{pubkey::Pubkey, signature::Signature};
|
||||
use std::collections::VecDeque;
|
||||
use std::ops::DerefMut;
|
||||
@@ -93,20 +94,61 @@ pub struct PooledAccountPretty {
|
||||
|
||||
impl PooledAccountPretty {
|
||||
/// 从 gRPC 更新重置数据
|
||||
pub fn reset_from_update(&mut self, account_update: SubscribeUpdateAccount) {
|
||||
let account_info = account_update.account.unwrap();
|
||||
pub fn reset_from_update(&mut self, account_update: SubscribeUpdateAccount) -> bool {
|
||||
let Some(account_info) = account_update.account else {
|
||||
warn!("grpc account update missing nested account, skipping");
|
||||
return false;
|
||||
};
|
||||
|
||||
self.account.slot = account_update.slot;
|
||||
self.account.signature = if let Some(txn_signature) = account_info.txn_signature {
|
||||
Signature::try_from(txn_signature.as_slice()).expect("valid signature")
|
||||
if txn_signature.len() != 64 {
|
||||
warn!(
|
||||
"grpc account update has invalid signature length {}, skipping",
|
||||
txn_signature.len()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
match Signature::try_from(txn_signature.as_slice()) {
|
||||
Ok(sig) => sig,
|
||||
Err(_) => {
|
||||
warn!("grpc account update has invalid signature bytes, skipping");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Signature::default()
|
||||
};
|
||||
self.account.pubkey =
|
||||
Pubkey::try_from(account_info.pubkey.as_slice()).expect("valid pubkey");
|
||||
if account_info.pubkey.len() != 32 {
|
||||
warn!(
|
||||
"grpc account update has invalid pubkey length {}, skipping",
|
||||
account_info.pubkey.len()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
self.account.pubkey = match Pubkey::try_from(account_info.pubkey.as_slice()) {
|
||||
Ok(pubkey) => pubkey,
|
||||
Err(_) => {
|
||||
warn!("grpc account update has invalid pubkey bytes, skipping");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
self.account.executable = account_info.executable;
|
||||
self.account.lamports = account_info.lamports;
|
||||
self.account.owner = Pubkey::try_from(account_info.owner.as_slice()).expect("valid pubkey");
|
||||
if account_info.owner.len() != 32 {
|
||||
warn!(
|
||||
"grpc account update has invalid owner length {}, skipping",
|
||||
account_info.owner.len()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
self.account.owner = match Pubkey::try_from(account_info.owner.as_slice()) {
|
||||
Ok(owner) => owner,
|
||||
Err(_) => {
|
||||
warn!("grpc account update has invalid owner bytes, skipping");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
self.account.rent_epoch = account_info.rent_epoch;
|
||||
|
||||
// 优化数据字段的重用
|
||||
@@ -119,6 +161,7 @@ impl PooledAccountPretty {
|
||||
}
|
||||
|
||||
self.account.recv_us = get_high_perf_clock();
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,18 +315,35 @@ impl PooledTransactionPretty {
|
||||
&mut self,
|
||||
tx_update: SubscribeUpdateTransaction,
|
||||
block_time: Option<Timestamp>,
|
||||
) {
|
||||
let tx = tx_update.transaction.expect("should be defined");
|
||||
) -> bool {
|
||||
let Some(tx) = tx_update.transaction else {
|
||||
warn!("grpc transaction update missing nested transaction, skipping");
|
||||
return false;
|
||||
};
|
||||
|
||||
if tx.signature.len() != 64 {
|
||||
warn!(
|
||||
"grpc transaction update has invalid signature length {}, skipping",
|
||||
tx.signature.len()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
self.transaction.slot = tx_update.slot;
|
||||
self.transaction.tx_index = Some(tx.index);
|
||||
self.transaction.block_time = block_time;
|
||||
self.transaction.block_hash.clear(); // 重置 block_hash
|
||||
self.transaction.signature =
|
||||
Signature::try_from(tx.signature.as_slice()).expect("valid signature");
|
||||
self.transaction.signature = match Signature::try_from(tx.signature.as_slice()) {
|
||||
Ok(signature) => signature,
|
||||
Err(_) => {
|
||||
warn!("grpc transaction update has invalid signature bytes, skipping");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
self.transaction.is_vote = tx.is_vote;
|
||||
self.transaction.recv_us = get_high_perf_clock();
|
||||
self.transaction.grpc_tx = tx;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,12 +430,17 @@ impl Default for PoolManager {
|
||||
/// 工厂函数用于创建优化的 EventPretty
|
||||
impl EventPrettyPool {
|
||||
/// 创建账户事件 - 使用对象池优化
|
||||
pub fn create_account_event_optimized(&self, update: SubscribeUpdateAccount) -> AccountPretty {
|
||||
pub fn create_account_event_optimized(
|
||||
&self,
|
||||
update: SubscribeUpdateAccount,
|
||||
) -> Option<AccountPretty> {
|
||||
let mut pooled_account = self.acquire_account();
|
||||
pooled_account.reset_from_update(update);
|
||||
if !pooled_account.reset_from_update(update) {
|
||||
return None;
|
||||
}
|
||||
// 移动数据而不是克隆,避免多余的内存分配
|
||||
let result = std::mem::replace(pooled_account.deref_mut(), AccountPretty::default());
|
||||
result
|
||||
Some(result)
|
||||
}
|
||||
|
||||
/// 创建区块事件 - 使用对象池优化
|
||||
@@ -396,12 +461,14 @@ impl EventPrettyPool {
|
||||
&self,
|
||||
update: SubscribeUpdateTransaction,
|
||||
block_time: Option<Timestamp>,
|
||||
) -> TransactionPretty {
|
||||
) -> Option<TransactionPretty> {
|
||||
let mut pooled_tx = self.acquire_transaction();
|
||||
pooled_tx.reset_from_update(update, block_time);
|
||||
if !pooled_tx.reset_from_update(update, block_time) {
|
||||
return None;
|
||||
}
|
||||
// 移动数据而不是克隆
|
||||
let result = std::mem::replace(pooled_tx.deref_mut(), TransactionPretty::default());
|
||||
result
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,7 +481,7 @@ pub mod factory {
|
||||
use super::*;
|
||||
|
||||
/// 使用对象池创建账户事件(推荐用于高性能场景)
|
||||
pub fn create_account_pretty_pooled(update: SubscribeUpdateAccount) -> AccountPretty {
|
||||
pub fn create_account_pretty_pooled(update: SubscribeUpdateAccount) -> Option<AccountPretty> {
|
||||
GLOBAL_POOL_MANAGER.get_event_pool().create_account_event_optimized(update)
|
||||
}
|
||||
|
||||
@@ -430,7 +497,7 @@ pub mod factory {
|
||||
pub fn create_transaction_pretty_pooled(
|
||||
update: SubscribeUpdateTransaction,
|
||||
block_time: Option<Timestamp>,
|
||||
) -> TransactionPretty {
|
||||
) -> Option<TransactionPretty> {
|
||||
GLOBAL_POOL_MANAGER.get_event_pool().create_transaction_event_optimized(update, block_time)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user