mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-08-17 19:08:05 +00:00
perf: Major event processing system refactor for improved performance
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
use super::constants::*;
|
||||
|
||||
/// 背压处理策略
|
||||
/// Backpressure handling strategy
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum BackpressureStrategy {
|
||||
/// 阻塞等待(默认)
|
||||
/// Block and wait (default)
|
||||
Block,
|
||||
/// 丢弃消息
|
||||
/// Drop messages
|
||||
Drop,
|
||||
/// 重试有限次数后丢弃
|
||||
Retry { max_attempts: usize, wait_ms: u64 },
|
||||
/// Execute asynchronously (don't wait for completion)
|
||||
Async,
|
||||
}
|
||||
|
||||
impl Default for BackpressureStrategy {
|
||||
@@ -17,50 +17,29 @@ impl Default for BackpressureStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
/// 批处理配置
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BatchConfig {
|
||||
/// 批处理大小(默认:100)
|
||||
pub batch_size: usize,
|
||||
/// 批处理超时时间(毫秒,默认:5ms)
|
||||
pub batch_timeout_ms: u64,
|
||||
/// 是否启用批处理(默认:true)
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl Default for BatchConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
batch_size: DEFAULT_BATCH_SIZE,
|
||||
batch_timeout_ms: DEFAULT_BATCH_TIMEOUT_MS,
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 背压配置
|
||||
/// Backpressure configuration
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BackpressureConfig {
|
||||
/// 通道大小(默认:1000)
|
||||
pub channel_size: usize,
|
||||
/// 背压处理策略(默认:Block)
|
||||
/// Channel size (default: 1000)
|
||||
pub permits: usize,
|
||||
/// Backpressure handling strategy (default: Block)
|
||||
pub strategy: BackpressureStrategy,
|
||||
}
|
||||
|
||||
impl Default for BackpressureConfig {
|
||||
fn default() -> Self {
|
||||
Self { channel_size: DEFAULT_CHANNEL_SIZE, strategy: BackpressureStrategy::default() }
|
||||
Self { permits: 1, strategy: BackpressureStrategy::default() }
|
||||
}
|
||||
}
|
||||
|
||||
/// 连接配置
|
||||
/// Connection configuration
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConnectionConfig {
|
||||
/// 连接超时时间(秒,默认:10)
|
||||
/// Connection timeout in seconds (default: 10)
|
||||
pub connect_timeout: u64,
|
||||
/// 请求超时时间(秒,默认:60)
|
||||
/// Request timeout in seconds (default: 60)
|
||||
pub request_timeout: u64,
|
||||
/// 最大解码消息大小(字节,默认:10MB)
|
||||
/// Maximum decoding message size in bytes (default: 10MB)
|
||||
pub max_decoding_message_size: usize,
|
||||
}
|
||||
|
||||
@@ -74,16 +53,14 @@ impl Default for ConnectionConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// 通用客户端配置
|
||||
/// Common client configuration
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StreamClientConfig {
|
||||
/// 连接配置
|
||||
/// Connection configuration
|
||||
pub connection: ConnectionConfig,
|
||||
/// 批处理配置
|
||||
pub batch: BatchConfig,
|
||||
/// 背压配置
|
||||
/// Backpressure configuration
|
||||
pub backpressure: BackpressureConfig,
|
||||
/// 是否启用性能监控(默认:false)
|
||||
/// Whether performance monitoring is enabled (default: false)
|
||||
pub enable_metrics: bool,
|
||||
}
|
||||
|
||||
@@ -91,7 +68,6 @@ impl Default for StreamClientConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
connection: ConnectionConfig::default(),
|
||||
batch: BatchConfig::default(),
|
||||
backpressure: BackpressureConfig::default(),
|
||||
enable_metrics: false,
|
||||
}
|
||||
@@ -99,31 +75,57 @@ impl Default for StreamClientConfig {
|
||||
}
|
||||
|
||||
impl StreamClientConfig {
|
||||
/// 创建高性能配置(适合高并发场景)
|
||||
pub fn high_performance() -> Self {
|
||||
/// Creates a high-throughput configuration optimized for high-concurrency scenarios.
|
||||
///
|
||||
/// This configuration prioritizes throughput over latency by:
|
||||
/// - Implementing a drop strategy for backpressure to avoid blocking
|
||||
/// - Setting a large permit buffer (5,000) to handle burst traffic
|
||||
///
|
||||
/// Ideal for scenarios where you need to process large volumes of data
|
||||
/// and can tolerate occasional message drops during peak loads.
|
||||
pub fn high_throughput() -> Self {
|
||||
Self {
|
||||
connection: ConnectionConfig::default(),
|
||||
batch: BatchConfig { batch_size: 200, batch_timeout_ms: 5, enabled: true },
|
||||
backpressure: BackpressureConfig {
|
||||
channel_size: 20000,
|
||||
permits: 5000,
|
||||
strategy: BackpressureStrategy::Drop,
|
||||
},
|
||||
enable_metrics: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建低延迟配置(适合实时场景)
|
||||
/// Creates a low-latency configuration optimized for real-time scenarios.
|
||||
///
|
||||
/// This configuration prioritizes latency over throughput by:
|
||||
/// - Processing events immediately without buffering
|
||||
/// - Implementing a blocking backpressure strategy to ensure no data loss
|
||||
/// - Setting minimal permits (1) to minimize memory usage
|
||||
///
|
||||
/// Ideal for scenarios where every millisecond counts and you cannot
|
||||
/// afford to lose any events, such as trading applications or real-time monitoring.
|
||||
pub fn low_latency() -> Self {
|
||||
Self {
|
||||
connection: ConnectionConfig::default(),
|
||||
batch: BatchConfig {
|
||||
batch_size: 10,
|
||||
batch_timeout_ms: 1,
|
||||
enabled: false, // 禁用批处理,即时处理
|
||||
},
|
||||
backpressure: BackpressureConfig { permits: 1, strategy: BackpressureStrategy::Block },
|
||||
enable_metrics: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an asynchronous processing configuration optimized for high-volume scenarios.
|
||||
///
|
||||
/// This configuration balances throughput and reliability by:
|
||||
/// - Implementing an async backpressure strategy for non-blocking operation
|
||||
/// - Setting a balanced permit buffer (5,000) for steady flow
|
||||
///
|
||||
/// Ideal for scenarios where you need sustained high throughput with
|
||||
/// fire-and-forget semantics, such as data ingestion pipelines or
|
||||
/// event streaming applications where some eventual consistency is acceptable.
|
||||
pub fn async_processing() -> Self {
|
||||
Self {
|
||||
connection: ConnectionConfig::default(),
|
||||
backpressure: BackpressureConfig {
|
||||
channel_size: 1000,
|
||||
strategy: BackpressureStrategy::Block,
|
||||
permits: 5000,
|
||||
strategy: BackpressureStrategy::Async,
|
||||
},
|
||||
enable_metrics: false,
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ pub const DEFAULT_CONNECT_TIMEOUT: u64 = 10;
|
||||
pub const DEFAULT_REQUEST_TIMEOUT: u64 = 60;
|
||||
pub const DEFAULT_CHANNEL_SIZE: usize = 1000;
|
||||
pub const DEFAULT_MAX_DECODING_MESSAGE_SIZE: usize = 1024 * 1024 * 10;
|
||||
pub const DEFAULT_BATCH_SIZE: usize = 100;
|
||||
pub const DEFAULT_BATCH_TIMEOUT_MS: u64 = 5;
|
||||
|
||||
// 性能监控相关常量
|
||||
pub const DEFAULT_METRICS_WINDOW_SECONDS: u64 = 5;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::Signature;
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
use crate::common::AnyResult;
|
||||
use crate::streaming::common::{
|
||||
@@ -14,11 +16,11 @@ use crate::streaming::event_parser::EventParser;
|
||||
use crate::streaming::event_parser::{
|
||||
core::traits::UnifiedEvent, protocols::mutil::parser::MutilEventParser, Protocol,
|
||||
};
|
||||
use crate::streaming::grpc::{BackpressureConfig, BatchConfig, EventPretty};
|
||||
use crate::streaming::grpc::{BackpressureConfig, EventPretty};
|
||||
use crate::streaming::shred::TransactionWithSlot;
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
/// 事件处理器
|
||||
/// Event processor
|
||||
pub struct EventProcessor {
|
||||
pub(crate) metrics_manager: MetricsManager,
|
||||
pub(crate) config: ClientConfig,
|
||||
@@ -27,13 +29,15 @@ pub struct EventProcessor {
|
||||
pub(crate) event_type_filter: Option<EventTypeFilter>,
|
||||
pub(crate) callback: Option<Arc<dyn Fn(Box<dyn UnifiedEvent>) + Send + Sync>>,
|
||||
pub(crate) backpressure_config: BackpressureConfig,
|
||||
pub(crate) batch_config: BatchConfig,
|
||||
/// Backpressure semaphore for controlling concurrent processing count
|
||||
pub(crate) backpressure_semaphore: Arc<Semaphore>,
|
||||
}
|
||||
|
||||
impl EventProcessor {
|
||||
/// 创建新的事件处理器
|
||||
/// Create a new event processor
|
||||
pub fn new(metrics_manager: MetricsManager, config: ClientConfig) -> Self {
|
||||
let backpressure_config = config.backpressure.clone();
|
||||
let backpressure_semaphore = Arc::new(Semaphore::new(backpressure_config.permits));
|
||||
Self {
|
||||
metrics_manager,
|
||||
config,
|
||||
@@ -41,8 +45,8 @@ impl EventProcessor {
|
||||
protocols: vec![],
|
||||
event_type_filter: None,
|
||||
backpressure_config,
|
||||
batch_config: BatchConfig::default(),
|
||||
callback: None,
|
||||
backpressure_semaphore,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,13 +55,15 @@ impl EventProcessor {
|
||||
protocols: Vec<Protocol>,
|
||||
event_type_filter: Option<EventTypeFilter>,
|
||||
backpressure_config: BackpressureConfig,
|
||||
batch_config: BatchConfig,
|
||||
callback: Option<Arc<dyn Fn(Box<dyn UnifiedEvent>) + Send + Sync>>,
|
||||
) {
|
||||
self.protocols = protocols.clone();
|
||||
self.event_type_filter = event_type_filter.clone();
|
||||
// Recreate semaphore if backpressure configuration changes
|
||||
if self.backpressure_config.permits != backpressure_config.permits {
|
||||
self.backpressure_semaphore = Arc::new(Semaphore::new(backpressure_config.permits));
|
||||
}
|
||||
self.backpressure_config = backpressure_config;
|
||||
self.batch_config = batch_config;
|
||||
self.callback = callback;
|
||||
self.parser_cache
|
||||
.get_or_init(|| Arc::new(MutilEventParser::new(protocols, event_type_filter)));
|
||||
@@ -72,8 +78,73 @@ impl EventProcessor {
|
||||
event_pretty: EventPretty,
|
||||
bot_wallet: Option<Pubkey>,
|
||||
) -> AnyResult<()> {
|
||||
self.process_grpc_event_transaction(event_pretty, bot_wallet).await?;
|
||||
Ok(())
|
||||
// Backpressure control logic
|
||||
let backpressure_start = Instant::now();
|
||||
let result = self.apply_backpressure_control(event_pretty, bot_wallet).await;
|
||||
let backpressure_duration = backpressure_start.elapsed();
|
||||
|
||||
// Record backpressure-related metrics
|
||||
self.metrics_manager.record_backpressure_metrics(
|
||||
backpressure_duration,
|
||||
result.is_ok(),
|
||||
self.backpressure_semaphore.available_permits(),
|
||||
);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Apply backpressure control strategy
|
||||
async fn apply_backpressure_control(
|
||||
&self,
|
||||
event_pretty: EventPretty,
|
||||
bot_wallet: Option<Pubkey>,
|
||||
) -> AnyResult<()> {
|
||||
use crate::streaming::common::BackpressureStrategy;
|
||||
|
||||
match self.backpressure_config.strategy {
|
||||
BackpressureStrategy::Block => {
|
||||
// Blocking strategy: acquire semaphore permit
|
||||
let _permit =
|
||||
self.backpressure_semaphore.acquire().await.map_err(|e| {
|
||||
anyhow::anyhow!("Failed to acquire backpressure permit: {}", e)
|
||||
})?;
|
||||
self.process_grpc_event_transaction(event_pretty, bot_wallet).await
|
||||
}
|
||||
BackpressureStrategy::Drop => {
|
||||
// Drop strategy: try to acquire permit, drop if failed
|
||||
match self.backpressure_semaphore.try_acquire() {
|
||||
Ok(_permit) => {
|
||||
let result =
|
||||
self.process_grpc_event_transaction(event_pretty, bot_wallet).await;
|
||||
result
|
||||
}
|
||||
Err(_) => {
|
||||
// Record dropped event
|
||||
self.metrics_manager.increment_dropped_events();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
BackpressureStrategy::Async => {
|
||||
// Async strategy: process asynchronously regardless of permits
|
||||
self.spawn_async_processing(event_pretty, bot_wallet).await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process event asynchronously (without waiting for semaphore permit)
|
||||
async fn spawn_async_processing(&self, event_pretty: EventPretty, bot_wallet: Option<Pubkey>) {
|
||||
let processor = self.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
// Async strategy: no semaphore control, allow unlimited concurrency
|
||||
// Execute actual event processing directly
|
||||
if let Err(e) = processor.process_grpc_event_transaction(event_pretty, bot_wallet).await
|
||||
{
|
||||
log::error!("Error in async event processing: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn process_grpc_event_transaction(
|
||||
@@ -81,6 +152,9 @@ impl EventProcessor {
|
||||
event_pretty: EventPretty,
|
||||
bot_wallet: Option<Pubkey>,
|
||||
) -> AnyResult<()> {
|
||||
if self.callback.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
match event_pretty {
|
||||
EventPretty::Account(account_pretty) => {
|
||||
self.metrics_manager.add_account_process_count();
|
||||
@@ -105,40 +179,36 @@ impl EventProcessor {
|
||||
self.metrics_manager.add_tx_process_count();
|
||||
let slot = transaction_pretty.slot;
|
||||
let signature = transaction_pretty.signature;
|
||||
// 使用缓存获取解析器
|
||||
let tx = transaction_pretty.tx;
|
||||
let block_time = transaction_pretty.block_time;
|
||||
let program_received_time_us = transaction_pretty.program_received_time_us;
|
||||
let transaction_index = transaction_pretty.transaction_index;
|
||||
// Use cache to get parser
|
||||
let parser = self.get_parser();
|
||||
let all_events = parser
|
||||
.parse_transaction(
|
||||
transaction_pretty.tx.clone(),
|
||||
let callback = self.callback.clone().unwrap();
|
||||
let metrics_manager = self.metrics_manager.clone();
|
||||
let adapter_callback = Arc::new(move |event: Box<dyn UnifiedEvent>| {
|
||||
let processing_time_us = event.program_handle_time_consuming_us() as f64;
|
||||
callback(event);
|
||||
metrics_manager.update_metrics(
|
||||
MetricsEventType::Transaction,
|
||||
1,
|
||||
processing_time_us,
|
||||
Some(signature),
|
||||
);
|
||||
});
|
||||
parser
|
||||
.parse_transaction_owned(
|
||||
tx,
|
||||
signature,
|
||||
Some(slot),
|
||||
transaction_pretty.block_time,
|
||||
transaction_pretty.program_received_time_us,
|
||||
block_time,
|
||||
program_received_time_us,
|
||||
bot_wallet,
|
||||
transaction_pretty.transaction_index,
|
||||
transaction_index,
|
||||
adapter_callback,
|
||||
)
|
||||
.await
|
||||
.unwrap_or_else(|_e| vec![]);
|
||||
|
||||
let mut all_time_consuming_us = 0;
|
||||
let event_count = all_events.len();
|
||||
|
||||
// 为所有事件设置交易索引
|
||||
for mut event in all_events {
|
||||
event.set_program_handle_time_consuming_us(
|
||||
chrono::Utc::now().timestamp_micros() - event.program_received_time_us(),
|
||||
);
|
||||
all_time_consuming_us += event.program_handle_time_consuming_us();
|
||||
self.invoke_callback(event);
|
||||
}
|
||||
|
||||
// 更新性能指标
|
||||
self.update_metrics(
|
||||
MetricsEventType::Transaction,
|
||||
event_count as u64,
|
||||
all_time_consuming_us as f64,
|
||||
Some(signature),
|
||||
);
|
||||
.await?;
|
||||
}
|
||||
EventPretty::BlockMeta(block_meta_pretty) => {
|
||||
self.metrics_manager.add_block_meta_process_count();
|
||||
@@ -167,7 +237,7 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
/// 即时处理单个交易
|
||||
/// Process a single transaction immediately
|
||||
pub async fn process_shred_transaction_immediate(
|
||||
&self,
|
||||
transaction_with_slot: TransactionWithSlot,
|
||||
@@ -176,55 +246,129 @@ impl EventProcessor {
|
||||
self.process_shred_transaction(transaction_with_slot, bot_wallet).await
|
||||
}
|
||||
|
||||
/// Process shred transaction with backpressure control and performance monitoring
|
||||
pub async fn process_shred_transaction_with_metrics(
|
||||
&self,
|
||||
transaction_with_slot: TransactionWithSlot,
|
||||
bot_wallet: Option<Pubkey>,
|
||||
) -> AnyResult<()> {
|
||||
// Backpressure control logic
|
||||
let backpressure_start = Instant::now();
|
||||
let result = self.apply_shred_backpressure_control(transaction_with_slot, bot_wallet).await;
|
||||
let backpressure_duration = backpressure_start.elapsed();
|
||||
|
||||
// Record backpressure-related metrics
|
||||
self.metrics_manager.record_backpressure_metrics(
|
||||
backpressure_duration,
|
||||
result.is_ok(),
|
||||
self.backpressure_semaphore.available_permits(),
|
||||
);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Apply shred backpressure control strategy
|
||||
async fn apply_shred_backpressure_control(
|
||||
&self,
|
||||
transaction_with_slot: TransactionWithSlot,
|
||||
bot_wallet: Option<Pubkey>,
|
||||
) -> AnyResult<()> {
|
||||
use crate::streaming::common::BackpressureStrategy;
|
||||
|
||||
match self.backpressure_config.strategy {
|
||||
BackpressureStrategy::Block => {
|
||||
// Blocking strategy: acquire semaphore permit
|
||||
let _permit =
|
||||
self.backpressure_semaphore.acquire().await.map_err(|e| {
|
||||
anyhow::anyhow!("Failed to acquire backpressure permit: {}", e)
|
||||
})?;
|
||||
self.process_shred_transaction(transaction_with_slot, bot_wallet).await
|
||||
}
|
||||
BackpressureStrategy::Drop => {
|
||||
// Drop strategy: try to acquire permit, drop if failed
|
||||
match self.backpressure_semaphore.try_acquire() {
|
||||
Ok(_permit) => {
|
||||
let result =
|
||||
self.process_shred_transaction(transaction_with_slot, bot_wallet).await;
|
||||
result
|
||||
}
|
||||
Err(_) => {
|
||||
// Record dropped event
|
||||
self.metrics_manager.increment_dropped_events();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
BackpressureStrategy::Async => {
|
||||
// Async strategy: process asynchronously regardless of permits
|
||||
self.spawn_async_shred_processing(transaction_with_slot, bot_wallet).await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process shred event asynchronously (without waiting for semaphore permit)
|
||||
async fn spawn_async_shred_processing(
|
||||
&self,
|
||||
transaction_with_slot: TransactionWithSlot,
|
||||
bot_wallet: Option<Pubkey>,
|
||||
) {
|
||||
let processor = self.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
// Async strategy: no semaphore control, allow unlimited concurrency
|
||||
// Execute actual event processing directly
|
||||
if let Err(e) =
|
||||
processor.process_shred_transaction(transaction_with_slot, bot_wallet).await
|
||||
{
|
||||
log::error!("Error in async shred event processing: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn process_shred_transaction(
|
||||
&self,
|
||||
transaction_with_slot: TransactionWithSlot,
|
||||
bot_wallet: Option<Pubkey>,
|
||||
) -> AnyResult<()> {
|
||||
if self.callback.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
self.metrics_manager.add_tx_process_count();
|
||||
let program_received_time_us = chrono::Utc::now().timestamp_micros();
|
||||
let tx = transaction_with_slot.transaction;
|
||||
|
||||
let slot = transaction_with_slot.slot;
|
||||
let versioned_tx = transaction_with_slot.transaction;
|
||||
let signature = versioned_tx.signatures[0];
|
||||
|
||||
// 获取缓存的解析器
|
||||
let signature = tx.signatures[0];
|
||||
let program_received_time_us = transaction_with_slot.program_received_time_us;
|
||||
// Use cache to get parser
|
||||
let parser = self.get_parser();
|
||||
let callback = self.callback.clone().unwrap();
|
||||
let metrics_manager = self.metrics_manager.clone();
|
||||
|
||||
let all_events = parser
|
||||
.parse_versioned_transaction(
|
||||
&versioned_tx,
|
||||
let adapter_callback = Arc::new(move |event: Box<dyn UnifiedEvent>| {
|
||||
let processing_time_us = event.program_handle_time_consuming_us() as f64;
|
||||
callback(event);
|
||||
metrics_manager.update_metrics(
|
||||
MetricsEventType::Transaction,
|
||||
1,
|
||||
processing_time_us,
|
||||
Some(signature),
|
||||
);
|
||||
});
|
||||
|
||||
parser
|
||||
.parse_versioned_transaction_owned(
|
||||
tx,
|
||||
signature,
|
||||
Some(slot),
|
||||
None,
|
||||
program_received_time_us,
|
||||
bot_wallet,
|
||||
None,
|
||||
&[],
|
||||
adapter_callback,
|
||||
)
|
||||
.await
|
||||
.unwrap_or_else(|_e| vec![]);
|
||||
|
||||
let mut max_time_consuming_us = 0;
|
||||
|
||||
// 保存事件数量用于日志记录
|
||||
let event_count = all_events.len();
|
||||
|
||||
// 即时处理事件
|
||||
for mut event in all_events {
|
||||
event.set_program_handle_time_consuming_us(
|
||||
chrono::Utc::now().timestamp_micros() - event.program_received_time_us(),
|
||||
);
|
||||
max_time_consuming_us =
|
||||
max_time_consuming_us.max(event.program_handle_time_consuming_us());
|
||||
self.invoke_callback(event);
|
||||
}
|
||||
|
||||
// 实际调用性能指标更新
|
||||
self.update_metrics(
|
||||
MetricsEventType::Transaction,
|
||||
event_count as u64,
|
||||
max_time_consuming_us as f64,
|
||||
Some(signature),
|
||||
);
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -240,7 +384,7 @@ impl EventProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
// 实现 Clone trait 以支持模块间共享
|
||||
// Implement Clone trait to support sharing between modules
|
||||
impl Clone for EventProcessor {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
@@ -250,8 +394,8 @@ impl Clone for EventProcessor {
|
||||
protocols: self.protocols.clone(),
|
||||
event_type_filter: self.event_type_filter.clone(),
|
||||
backpressure_config: self.backpressure_config.clone(),
|
||||
batch_config: self.batch_config.clone(),
|
||||
callback: self.callback.clone(),
|
||||
backpressure_semaphore: self.backpressure_semaphore.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+247
-10
@@ -108,15 +108,21 @@ impl AtomicEventMetrics {
|
||||
struct AtomicProcessingTimeStats {
|
||||
min_time_bits: AtomicU64,
|
||||
max_time_bits: AtomicU64,
|
||||
total_time_us: AtomicU64, // 存储微秒的整数部分
|
||||
max_time_timestamp_nanos: AtomicU64, // 最大值更新时间戳(纳秒)
|
||||
total_time_us: AtomicU64, // 存储微秒的整数部分
|
||||
total_events: AtomicU64,
|
||||
}
|
||||
|
||||
impl AtomicProcessingTimeStats {
|
||||
fn new() -> Self {
|
||||
let now_nanos =
|
||||
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos()
|
||||
as u64;
|
||||
|
||||
Self {
|
||||
min_time_bits: AtomicU64::new(f64::INFINITY.to_bits()),
|
||||
max_time_bits: AtomicU64::new(0),
|
||||
max_time_timestamp_nanos: AtomicU64::new(now_nanos),
|
||||
total_time_us: AtomicU64::new(0),
|
||||
total_events: AtomicU64::new(0),
|
||||
}
|
||||
@@ -126,6 +132,9 @@ impl AtomicProcessingTimeStats {
|
||||
#[inline]
|
||||
fn update(&self, time_us: f64, event_count: u64) {
|
||||
let time_bits = time_us.to_bits();
|
||||
let now_nanos =
|
||||
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos()
|
||||
as u64;
|
||||
|
||||
// 更新最小值(使用 compare_exchange_weak 循环)
|
||||
let mut current_min = self.min_time_bits.load(Ordering::Relaxed);
|
||||
@@ -141,8 +150,20 @@ impl AtomicProcessingTimeStats {
|
||||
}
|
||||
}
|
||||
|
||||
// 更新最大值
|
||||
// 更新最大值,检查时间差并在超过10秒时清零
|
||||
let mut current_max = self.max_time_bits.load(Ordering::Relaxed);
|
||||
let max_timestamp = self.max_time_timestamp_nanos.load(Ordering::Relaxed);
|
||||
|
||||
// 检查最大值的时间戳是否超过10秒(10_000_000_000纳秒)
|
||||
let time_diff_nanos = now_nanos.saturating_sub(max_timestamp);
|
||||
if time_diff_nanos > 10_000_000_000 {
|
||||
// 超过10秒,清零最大值
|
||||
self.max_time_bits.store(0, Ordering::Relaxed);
|
||||
self.max_time_timestamp_nanos.store(now_nanos, Ordering::Relaxed);
|
||||
current_max = 0;
|
||||
}
|
||||
|
||||
// 如果当前时间大于最大值,更新最大值和时间戳
|
||||
while time_bits > current_max {
|
||||
match self.max_time_bits.compare_exchange_weak(
|
||||
current_max,
|
||||
@@ -150,7 +171,11 @@ impl AtomicProcessingTimeStats {
|
||||
Ordering::Relaxed,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
Ok(_) => break,
|
||||
Ok(_) => {
|
||||
// 成功更新最大值,同时更新时间戳
|
||||
self.max_time_timestamp_nanos.store(now_nanos, Ordering::Relaxed);
|
||||
break;
|
||||
}
|
||||
Err(x) => current_max = x,
|
||||
}
|
||||
}
|
||||
@@ -198,6 +223,18 @@ pub struct EventMetricsSnapshot {
|
||||
pub events_per_second: f64,
|
||||
}
|
||||
|
||||
/// 背压指标快照
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BackpressureMetricsSnapshot {
|
||||
pub total_duration_us: u64,
|
||||
pub success_count: u64,
|
||||
pub failure_count: u64,
|
||||
pub min_permits: u64,
|
||||
pub max_permits: u64,
|
||||
pub avg_duration_us: f64,
|
||||
pub success_rate: f64,
|
||||
}
|
||||
|
||||
/// 兼容性结构 - 完整的性能指标
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PerformanceMetrics {
|
||||
@@ -206,6 +243,8 @@ pub struct PerformanceMetrics {
|
||||
pub account_metrics: EventMetricsSnapshot,
|
||||
pub block_meta_metrics: EventMetricsSnapshot,
|
||||
pub processing_stats: ProcessingTimeStats,
|
||||
pub backpressure_metrics: BackpressureMetricsSnapshot,
|
||||
pub dropped_events_count: u64,
|
||||
}
|
||||
|
||||
impl PerformanceMetrics {
|
||||
@@ -214,6 +253,15 @@ impl PerformanceMetrics {
|
||||
let default_metrics =
|
||||
EventMetricsSnapshot { process_count: 0, events_processed: 0, events_per_second: 0.0 };
|
||||
let default_stats = ProcessingTimeStats { min_us: 0.0, max_us: 0.0, avg_us: 0.0 };
|
||||
let default_backpressure = BackpressureMetricsSnapshot {
|
||||
total_duration_us: 0,
|
||||
success_count: 0,
|
||||
failure_count: 0,
|
||||
min_permits: 0,
|
||||
max_permits: 0,
|
||||
avg_duration_us: 0.0,
|
||||
success_rate: 0.0,
|
||||
};
|
||||
|
||||
Self {
|
||||
uptime: std::time::Duration::ZERO,
|
||||
@@ -221,6 +269,8 @@ impl PerformanceMetrics {
|
||||
account_metrics: default_metrics.clone(),
|
||||
block_meta_metrics: default_metrics,
|
||||
processing_stats: default_stats,
|
||||
backpressure_metrics: default_backpressure,
|
||||
dropped_events_count: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,6 +281,14 @@ pub struct HighPerformanceMetrics {
|
||||
start_nanos: u64,
|
||||
event_metrics: [AtomicEventMetrics; 3],
|
||||
processing_stats: AtomicProcessingTimeStats,
|
||||
// 背压相关指标
|
||||
backpressure_total_duration_us: AtomicU64,
|
||||
backpressure_success_count: AtomicU64,
|
||||
backpressure_failure_count: AtomicU64,
|
||||
backpressure_min_permits: AtomicU64,
|
||||
backpressure_max_permits: AtomicU64,
|
||||
// 丢弃事件指标
|
||||
dropped_events_count: AtomicU64,
|
||||
}
|
||||
|
||||
impl HighPerformanceMetrics {
|
||||
@@ -247,6 +305,14 @@ impl HighPerformanceMetrics {
|
||||
AtomicEventMetrics::new(now_nanos),
|
||||
],
|
||||
processing_stats: AtomicProcessingTimeStats::new(),
|
||||
// 初始化背压相关指标
|
||||
backpressure_total_duration_us: AtomicU64::new(0),
|
||||
backpressure_success_count: AtomicU64::new(0),
|
||||
backpressure_failure_count: AtomicU64::new(0),
|
||||
backpressure_min_permits: AtomicU64::new(u64::MAX), // 初始化为最大值,便于后续比较
|
||||
backpressure_max_permits: AtomicU64::new(0),
|
||||
// 初始化丢弃事件指标
|
||||
dropped_events_count: AtomicU64::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,6 +341,38 @@ impl HighPerformanceMetrics {
|
||||
self.processing_stats.get_stats()
|
||||
}
|
||||
|
||||
/// 获取背压指标快照
|
||||
#[inline]
|
||||
pub fn get_backpressure_metrics(&self) -> BackpressureMetricsSnapshot {
|
||||
let total_duration_us = self.backpressure_total_duration_us.load(Ordering::Relaxed);
|
||||
let success_count = self.backpressure_success_count.load(Ordering::Relaxed);
|
||||
let failure_count = self.backpressure_failure_count.load(Ordering::Relaxed);
|
||||
let min_permits = self.backpressure_min_permits.load(Ordering::Relaxed);
|
||||
let max_permits = self.backpressure_max_permits.load(Ordering::Relaxed);
|
||||
|
||||
let total_count = success_count + failure_count;
|
||||
let avg_duration_us =
|
||||
if total_count > 0 { total_duration_us as f64 / total_count as f64 } else { 0.0 };
|
||||
let success_rate =
|
||||
if total_count > 0 { success_count as f64 / total_count as f64 } else { 0.0 };
|
||||
|
||||
BackpressureMetricsSnapshot {
|
||||
total_duration_us,
|
||||
success_count,
|
||||
failure_count,
|
||||
min_permits: if min_permits == u64::MAX { 0 } else { min_permits },
|
||||
max_permits,
|
||||
avg_duration_us,
|
||||
success_rate,
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取丢弃事件计数
|
||||
#[inline]
|
||||
pub fn get_dropped_events_count(&self) -> u64 {
|
||||
self.dropped_events_count.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// 计算实时每秒事件数(非阻塞)
|
||||
fn calculate_real_time_eps(&self, event_type: EventType) -> f64 {
|
||||
let now_nanos =
|
||||
@@ -443,11 +541,43 @@ impl MetricsManager {
|
||||
self.metrics.get_processing_stats()
|
||||
}
|
||||
|
||||
/// 获取背压指标
|
||||
pub fn get_backpressure_metrics(&self) -> BackpressureMetricsSnapshot {
|
||||
self.metrics.get_backpressure_metrics()
|
||||
}
|
||||
|
||||
/// 获取丢弃事件计数
|
||||
pub fn get_dropped_events_count(&self) -> u64 {
|
||||
self.metrics.get_dropped_events_count()
|
||||
}
|
||||
|
||||
/// 打印性能指标(非阻塞)
|
||||
pub fn print_metrics(&self) {
|
||||
println!("\n📊 {} Performance Metrics", self.stream_name);
|
||||
println!(" Run Time: {:?}", self.get_uptime());
|
||||
|
||||
// 打印背压指标表格
|
||||
let backpressure = self.get_backpressure_metrics();
|
||||
if backpressure.success_count > 0 || backpressure.failure_count > 0 {
|
||||
println!("\n🚦 Backpressure Metrics");
|
||||
println!("┌──────────────────────┬─────────────┐");
|
||||
println!("│ Metric │ Value │");
|
||||
println!("├──────────────────────┼─────────────┤");
|
||||
println!("│ Success Count │ {:11} │", backpressure.success_count);
|
||||
println!("│ Failure Count │ {:11} │", backpressure.failure_count);
|
||||
println!("│ Success Rate │ {:11.2} │", backpressure.success_rate * 100.0);
|
||||
println!("│ Avg Duration (ms) │ {:11.2} │", backpressure.avg_duration_us / 1000.0);
|
||||
println!("│ Min Permits │ {:11} │", backpressure.min_permits);
|
||||
println!("│ Max Permits │ {:11} │", backpressure.max_permits);
|
||||
println!("└──────────────────────┴─────────────┘");
|
||||
}
|
||||
|
||||
// 打印丢弃事件指标
|
||||
let dropped_count = self.get_dropped_events_count();
|
||||
if dropped_count > 0 {
|
||||
println!("\n⚠️ Dropped Events: {}", dropped_count);
|
||||
}
|
||||
|
||||
// 打印事件指标表格
|
||||
println!("┌─────────────┬──────────────┬──────────────────┬─────────────────┐");
|
||||
println!("│ Event Type │ Process Count│ Events Processed │ Events/Second │");
|
||||
@@ -469,13 +599,14 @@ impl MetricsManager {
|
||||
// 打印处理时间统计表格
|
||||
let stats = self.get_processing_stats();
|
||||
println!("\n⏱️ Processing Time Statistics");
|
||||
println!("┌─────────────────────┬─────────────┐");
|
||||
println!("│ Metric │ Value (us) │");
|
||||
println!("├─────────────────────┼─────────────┤");
|
||||
println!("│ Average │ {:9.2} │", stats.avg_us);
|
||||
println!("│ Minimum │ {:9.2} │", stats.min_us);
|
||||
println!("│ Maximum │ {:9.2} │", stats.max_us);
|
||||
println!("└─────────────────────┴─────────────┘");
|
||||
println!("┌───────────────────────┬─────────────┐");
|
||||
println!("│ Metric │ Value (us) │");
|
||||
println!("├───────────────────────┼─────────────┤");
|
||||
println!("│ Average │ {:9.2} │", stats.avg_us);
|
||||
println!("│ Minimum │ {:9.2} │", stats.min_us);
|
||||
println!("│ Maximum within 10s │ {:9.2} │", stats.max_us);
|
||||
println!("└───────────────────────┴─────────────┘");
|
||||
|
||||
println!();
|
||||
}
|
||||
|
||||
@@ -517,6 +648,8 @@ impl MetricsManager {
|
||||
account_metrics: self.get_event_metrics(EventType::Account),
|
||||
block_meta_metrics: self.get_event_metrics(EventType::BlockMeta),
|
||||
processing_stats: self.get_processing_stats(),
|
||||
backpressure_metrics: self.metrics.get_backpressure_metrics(),
|
||||
dropped_events_count: self.metrics.get_dropped_events_count(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,6 +683,110 @@ impl MetricsManager {
|
||||
self.record_events(event_type, events_processed, processing_time_us);
|
||||
self.log_slow_processing(processing_time_us, events_processed as usize, signature);
|
||||
}
|
||||
|
||||
/// 记录背压相关的metrics
|
||||
#[inline]
|
||||
pub fn record_backpressure_metrics(
|
||||
&self,
|
||||
backpressure_duration: std::time::Duration,
|
||||
success: bool,
|
||||
available_permits: usize,
|
||||
) {
|
||||
if !self.enable_metrics {
|
||||
return;
|
||||
}
|
||||
|
||||
let duration_us = backpressure_duration.as_micros() as u64;
|
||||
let permits = available_permits as u64;
|
||||
|
||||
// 记录总持续时间
|
||||
self.metrics.backpressure_total_duration_us.fetch_add(duration_us, Ordering::Relaxed);
|
||||
|
||||
// 记录成功/失败计数
|
||||
if success {
|
||||
self.metrics.backpressure_success_count.fetch_add(1, Ordering::Relaxed);
|
||||
} else {
|
||||
self.metrics.backpressure_failure_count.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
// 更新最小许可数(使用 compare_exchange_weak 循环)
|
||||
let mut current_min = self.metrics.backpressure_min_permits.load(Ordering::Relaxed);
|
||||
while permits < current_min {
|
||||
match self.metrics.backpressure_min_permits.compare_exchange_weak(
|
||||
current_min,
|
||||
permits,
|
||||
Ordering::Relaxed,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
Ok(_) => break,
|
||||
Err(x) => current_min = x,
|
||||
}
|
||||
}
|
||||
|
||||
// 更新最大许可数
|
||||
let mut current_max = self.metrics.backpressure_max_permits.load(Ordering::Relaxed);
|
||||
while permits > current_max {
|
||||
match self.metrics.backpressure_max_permits.compare_exchange_weak(
|
||||
current_max,
|
||||
permits,
|
||||
Ordering::Relaxed,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
Ok(_) => break,
|
||||
Err(x) => current_max = x,
|
||||
}
|
||||
}
|
||||
|
||||
// 记录慢背压操作的日志
|
||||
if duration_us > 10_000 {
|
||||
// 超过10ms的背压认为是慢操作
|
||||
log::warn!(
|
||||
"{} slow backpressure: {:.2}ms, success: {}, available_permits: {}",
|
||||
self.stream_name,
|
||||
duration_us as f64 / 1000.0,
|
||||
success,
|
||||
available_permits
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 增加丢弃事件计数
|
||||
#[inline]
|
||||
pub fn increment_dropped_events(&self) {
|
||||
if !self.enable_metrics {
|
||||
return;
|
||||
}
|
||||
|
||||
// 原子地增加丢弃事件计数
|
||||
let new_count = self.metrics.dropped_events_count.fetch_add(1, Ordering::Relaxed) + 1;
|
||||
|
||||
// 每丢弃1000个事件记录一次警告日志
|
||||
if new_count % 1000 == 0 {
|
||||
log::warn!("{} dropped events count reached: {}", self.stream_name, new_count);
|
||||
}
|
||||
}
|
||||
|
||||
/// 批量增加丢弃事件计数
|
||||
#[inline]
|
||||
pub fn increment_dropped_events_by(&self, count: u64) {
|
||||
if !self.enable_metrics || count == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// 原子地增加丢弃事件计数
|
||||
let new_count = self.metrics.dropped_events_count.fetch_add(count, Ordering::Relaxed) + count;
|
||||
|
||||
// 记录批量丢弃事件的日志
|
||||
if count > 1 {
|
||||
log::warn!("{} dropped batch of {} events, total dropped: {}",
|
||||
self.stream_name, count, new_count);
|
||||
}
|
||||
|
||||
// 每丢弃1000个事件记录一次警告日志
|
||||
if new_count % 1000 == 0 || (new_count / 1000) != ((new_count - count) / 1000) {
|
||||
log::warn!("{} dropped events count reached: {}", self.stream_name, new_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for MetricsManager {
|
||||
|
||||
Reference in New Issue
Block a user