1use anyhow::{anyhow, Result};
8use futures_util::{pin_mut, stream::StreamExt, Stream};
9use serde::{Deserialize, Serialize};
10use std::collections::{HashMap, VecDeque};
11use std::future::Future;
12use std::pin::Pin;
13use std::sync::Arc;
14use std::task::{Context, Poll};
15use std::time::{Duration, Instant, SystemTime};
16use tokio::sync::{broadcast, mpsc, Mutex as AsyncMutex, RwLock as AsyncRwLock};
17use tokio::time::interval;
18use tracing::{debug, error, info, warn};
19
20use crate::ast::Value;
21
22#[derive(Debug, Clone)]
24pub struct StreamingConfig {
25 pub enable_adaptive_streaming: bool,
26 pub enable_backpressure_control: bool,
27 pub enable_stream_multiplexing: bool,
28 pub enable_data_compression: bool,
29 pub enable_priority_queuing: bool,
30 pub buffer_size: usize,
31 pub max_concurrent_streams: usize,
32 pub stream_timeout: Duration,
33 pub heartbeat_interval: Duration,
34 pub backpressure_threshold: f64,
35 pub compression_config: CompressionConfig,
36 pub priority_config: PriorityConfig,
37}
38
39impl Default for StreamingConfig {
40 fn default() -> Self {
41 Self {
42 enable_adaptive_streaming: true,
43 enable_backpressure_control: true,
44 enable_stream_multiplexing: true,
45 enable_data_compression: true,
46 enable_priority_queuing: true,
47 buffer_size: 1000,
48 max_concurrent_streams: 100,
49 stream_timeout: Duration::from_secs(30),
50 heartbeat_interval: Duration::from_secs(10),
51 backpressure_threshold: 0.8,
52 compression_config: CompressionConfig::default(),
53 priority_config: PriorityConfig::default(),
54 }
55 }
56}
57
58#[derive(Debug, Clone)]
60pub struct CompressionConfig {
61 pub algorithm: CompressionAlgorithm,
62 pub compression_level: u8,
63 pub min_compression_size: usize,
64 pub adaptive_compression: bool,
65}
66
67impl Default for CompressionConfig {
68 fn default() -> Self {
69 Self {
70 algorithm: CompressionAlgorithm::Gzip,
71 compression_level: 6,
72 min_compression_size: 1024,
73 adaptive_compression: true,
74 }
75 }
76}
77
78#[derive(Debug, Clone)]
79pub enum CompressionAlgorithm {
80 Gzip,
81 Zstd,
82 Lz4,
83 Brotli,
84}
85
86#[derive(Debug, Clone)]
88pub struct PriorityConfig {
89 pub enable_priority_inheritance: bool,
90 pub default_priority: StreamPriority,
91 pub priority_queues: usize,
92 pub starvation_prevention: bool,
93}
94
95impl Default for PriorityConfig {
96 fn default() -> Self {
97 Self {
98 enable_priority_inheritance: true,
99 default_priority: StreamPriority::Medium,
100 priority_queues: 5,
101 starvation_prevention: true,
102 }
103 }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
108pub enum StreamPriority {
109 Critical,
110 High,
111 Medium,
112 Low,
113 Background,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub enum StreamData {
119 GraphQLResponse {
120 query_id: String,
121 data: Value,
122 errors: Vec<String>,
123 extensions: HashMap<String, Value>,
124 },
125 SubscriptionUpdate {
126 subscription_id: String,
127 data: Value,
128 revision: u64,
129 },
130 FederationUpdate {
131 service_id: String,
132 schema_changes: Vec<SchemaChange>,
133 timestamp: SystemTime,
134 },
135 MetricsUpdate {
136 metrics: PerformanceMetrics,
137 timestamp: SystemTime,
138 },
139 HealthUpdate {
140 service_id: String,
141 health_status: HealthStatus,
142 timestamp: SystemTime,
143 },
144 ConfigUpdate {
145 config_changes: HashMap<String, Value>,
146 timestamp: SystemTime,
147 },
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct SchemaChange {
153 pub change_type: SchemaChangeType,
154 pub affected_types: Vec<String>,
155 pub affected_fields: Vec<String>,
156 pub description: String,
157 pub breaking: bool,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
161pub enum SchemaChangeType {
162 TypeAdded,
163 TypeRemoved,
164 TypeModified,
165 FieldAdded,
166 FieldRemoved,
167 FieldModified,
168 DirectiveAdded,
169 DirectiveRemoved,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct PerformanceMetrics {
175 pub throughput_mbps: f64,
176 pub latency_ms: f64,
177 pub error_rate: f64,
178 pub active_streams: usize,
179 pub backpressure_events: u64,
180 pub compression_ratio: f64,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
185pub enum HealthStatus {
186 Healthy,
187 Degraded,
188 Unhealthy,
189 Unknown,
190}
191
192#[derive(Debug, Clone)]
194pub struct StreamMetadata {
195 pub stream_id: String,
196 pub priority: StreamPriority,
197 pub created_at: Instant,
198 pub last_activity: Instant,
199 pub total_messages: u64,
200 pub total_bytes: u64,
201 pub compression_enabled: bool,
202 pub client_info: ClientInfo,
203}
204
205#[derive(Debug, Clone)]
207pub struct ClientInfo {
208 pub client_id: String,
209 pub connection_type: ConnectionType,
210 pub capabilities: HashSet<StreamCapability>,
211}
212
213#[derive(Debug, Clone)]
214pub enum ConnectionType {
215 WebSocket,
216 ServerSentEvents,
217 GraphQLSubscription,
218 CustomProtocol,
219}
220
221#[derive(Debug, Clone, Hash, PartialEq, Eq)]
222pub enum StreamCapability {
223 Compression,
224 Multiplexing,
225 PriorityStreaming,
226 BackpressureControl,
227 StreamResumption,
228 DeltaUpdates,
229}
230
231use std::collections::HashSet;
232
233pub struct AsyncStreamingManager {
235 config: StreamingConfig,
236 active_streams: Arc<AsyncRwLock<HashMap<String, StreamHandle>>>,
237 stream_multiplexer: Arc<AsyncMutex<StreamMultiplexer>>,
238 backpressure_controller: Arc<AsyncMutex<BackpressureController>>,
239 compression_manager: Arc<AsyncMutex<CompressionManager>>,
240 priority_scheduler: Arc<AsyncMutex<PriorityScheduler>>,
241 metrics_collector: Arc<AsyncMutex<MetricsCollector>>,
242 data_sender: broadcast::Sender<StreamData>,
243}
244
245impl AsyncStreamingManager {
246 pub fn new(config: StreamingConfig) -> (Self, broadcast::Receiver<StreamData>) {
248 let (data_sender, data_receiver) = broadcast::channel(config.buffer_size);
249
250 let manager = Self {
251 config: config.clone(),
252 active_streams: Arc::new(AsyncRwLock::new(HashMap::new())),
253 stream_multiplexer: Arc::new(AsyncMutex::new(StreamMultiplexer::new(&config))),
254 backpressure_controller: Arc::new(AsyncMutex::new(BackpressureController::new(
255 &config,
256 ))),
257 compression_manager: Arc::new(AsyncMutex::new(CompressionManager::new(
258 &config.compression_config,
259 ))),
260 priority_scheduler: Arc::new(AsyncMutex::new(PriorityScheduler::new(
261 &config.priority_config,
262 ))),
263 metrics_collector: Arc::new(AsyncMutex::new(MetricsCollector::new())),
264 data_sender,
265 };
266
267 (manager, data_receiver)
268 }
269
270 pub async fn create_adaptive_stream(
272 &self,
273 stream_id: String,
274 priority: StreamPriority,
275 client_info: ClientInfo,
276 ) -> Result<AdaptiveStream> {
277 info!("Creating adaptive stream: {}", stream_id);
278
279 let active_streams = self.active_streams.read().await;
281 if active_streams.len() >= self.config.max_concurrent_streams {
282 return Err(anyhow!("Maximum concurrent streams exceeded"));
283 }
284 drop(active_streams);
285
286 let (data_sender, data_receiver) = mpsc::channel(self.config.buffer_size);
288 let (control_sender, control_receiver) = mpsc::channel(100);
289
290 let metadata = StreamMetadata {
292 stream_id: stream_id.clone(),
293 priority: priority.clone(),
294 created_at: Instant::now(),
295 last_activity: Instant::now(),
296 total_messages: 0,
297 total_bytes: 0,
298 compression_enabled: self.config.enable_data_compression,
299 client_info: client_info.clone(),
300 };
301
302 let handle = StreamHandle {
304 metadata: Arc::new(AsyncRwLock::new(metadata)),
305 data_sender,
306 control_sender,
307 backpressure_state: Arc::new(AsyncRwLock::new(BackpressureState::Normal)),
308 };
309
310 self.active_streams
312 .write()
313 .await
314 .insert(stream_id.clone(), handle.clone());
315
316 let mut multiplexer = self.stream_multiplexer.lock().await;
318 multiplexer.register_stream(&stream_id, &priority).await?;
319
320 let mut scheduler = self.priority_scheduler.lock().await;
322 scheduler.register_stream(&stream_id, priority).await?;
323
324 let adaptive_stream = AdaptiveStream::new(
326 stream_id,
327 data_receiver,
328 control_receiver,
329 handle.clone(),
330 Arc::clone(&self.compression_manager),
331 Arc::clone(&self.metrics_collector),
332 self.config.clone(),
333 );
334
335 Ok(adaptive_stream)
336 }
337
338 pub async fn send_to_stream(&self, stream_id: &str, data: StreamData) -> Result<()> {
340 let streams = self.active_streams.read().await;
341 if let Some(handle) = streams.get(stream_id) {
342 let backpressure_state = handle.backpressure_state.read().await;
344 if matches!(*backpressure_state, BackpressureState::Blocked) {
345 warn!("Stream {} is backpressured, dropping message", stream_id);
346 return Ok(()); }
348 drop(backpressure_state);
349
350 let compressed_data = if handle.metadata.read().await.compression_enabled {
352 let compression_manager = self.compression_manager.lock().await;
353 compression_manager.compress_data(&data).await?
354 } else {
355 data
356 };
357
358 if (handle.data_sender.send(compressed_data).await).is_err() {
360 warn!("Failed to send data to stream {}, removing", stream_id);
361 drop(streams);
363 self.remove_stream(stream_id).await?;
364 } else {
365 let mut metadata = handle.metadata.write().await;
367 metadata.total_messages += 1;
368 metadata.last_activity = Instant::now();
369 }
370 }
371
372 Ok(())
373 }
374
375 pub async fn broadcast(&self, data: StreamData) -> Result<()> {
377 let mut scheduler = self.priority_scheduler.lock().await;
379 let scheduled_streams = scheduler.schedule_broadcast(&data).await?;
380 drop(scheduler);
381
382 for stream_id in scheduled_streams {
384 if let Err(e) = self.send_to_stream(&stream_id, data.clone()).await {
385 warn!("Failed to send broadcast to stream {}: {}", stream_id, e);
386 }
387 }
388
389 if self.data_sender.send(data).is_err() {
391 warn!("No broadcast subscribers");
392 }
393
394 Ok(())
395 }
396
397 pub async fn remove_stream(&self, stream_id: &str) -> Result<()> {
399 info!("Removing stream: {}", stream_id);
400
401 self.active_streams.write().await.remove(stream_id);
403
404 let mut multiplexer = self.stream_multiplexer.lock().await;
406 multiplexer.unregister_stream(stream_id).await?;
407
408 let mut scheduler = self.priority_scheduler.lock().await;
410 scheduler.unregister_stream(stream_id).await?;
411
412 Ok(())
413 }
414
415 pub async fn get_stream_metrics(&self) -> Result<HashMap<String, StreamMetrics>> {
417 let mut metrics = HashMap::new();
418 let streams = self.active_streams.read().await;
419
420 for (stream_id, handle) in streams.iter() {
421 let metadata = handle.metadata.read().await;
422 let stream_metrics = StreamMetrics {
423 stream_id: stream_id.clone(),
424 priority: metadata.priority.clone(),
425 total_messages: metadata.total_messages,
426 total_bytes: metadata.total_bytes,
427 duration: metadata.created_at.elapsed(),
428 last_activity: metadata.last_activity.elapsed(),
429 compression_enabled: metadata.compression_enabled,
430 };
431 metrics.insert(stream_id.clone(), stream_metrics);
432 }
433
434 Ok(metrics)
435 }
436
437 pub async fn start_background_tasks(&self) -> Result<()> {
439 self.start_backpressure_monitoring().await?;
441
442 self.start_metrics_collection().await?;
444
445 self.start_stream_cleanup().await?;
447
448 Ok(())
449 }
450
451 async fn start_backpressure_monitoring(&self) -> Result<()> {
453 let controller = Arc::clone(&self.backpressure_controller);
454 let streams = Arc::clone(&self.active_streams);
455 let config = self.config.clone();
456
457 tokio::spawn(async move {
458 let mut interval = interval(Duration::from_millis(100));
459
460 loop {
461 interval.tick().await;
462
463 let mut controller = controller.lock().await;
464 let streams = streams.read().await;
465
466 for (stream_id, handle) in streams.iter() {
467 if let Err(e) = controller.monitor_stream(stream_id, handle, &config).await {
468 error!(
469 "Backpressure monitoring error for stream {}: {}",
470 stream_id, e
471 );
472 }
473 }
474 }
475 });
476
477 Ok(())
478 }
479
480 async fn start_metrics_collection(&self) -> Result<()> {
482 let collector = Arc::clone(&self.metrics_collector);
483 let streams = Arc::clone(&self.active_streams);
484
485 tokio::spawn(async move {
486 let mut interval = interval(Duration::from_secs(1));
487
488 loop {
489 interval.tick().await;
490
491 let mut collector = collector.lock().await;
492 let streams = streams.read().await;
493
494 if let Err(e) = collector.collect_metrics(&streams).await {
495 error!("Metrics collection error: {}", e);
496 }
497 }
498 });
499
500 Ok(())
501 }
502
503 async fn start_stream_cleanup(&self) -> Result<()> {
505 let streams = Arc::clone(&self.active_streams);
506 let timeout = self.config.stream_timeout;
507
508 tokio::spawn(async move {
509 let mut interval = interval(Duration::from_secs(30));
510
511 loop {
512 interval.tick().await;
513
514 let mut to_remove = Vec::new();
515 {
516 let streams = streams.read().await;
517 for (stream_id, handle) in streams.iter() {
518 let metadata = handle.metadata.read().await;
519 if metadata.last_activity.elapsed() > timeout {
520 to_remove.push(stream_id.clone());
521 }
522 }
523 }
524
525 if !to_remove.is_empty() {
526 let mut streams = streams.write().await;
527 for stream_id in to_remove {
528 info!("Cleaning up inactive stream: {}", stream_id);
529 streams.remove(&stream_id);
530 }
531 }
532 }
533 });
534
535 Ok(())
536 }
537}
538
539#[derive(Debug, Clone)]
541pub struct StreamHandle {
542 pub metadata: Arc<AsyncRwLock<StreamMetadata>>,
543 pub data_sender: mpsc::Sender<StreamData>,
544 pub control_sender: mpsc::Sender<StreamControl>,
545 pub backpressure_state: Arc<AsyncRwLock<BackpressureState>>,
546}
547
548#[derive(Debug, Clone)]
550pub enum StreamControl {
551 Pause,
552 Resume,
553 ChangeCompressionLevel(u8),
554 ChangePriority(StreamPriority),
555 Heartbeat,
556 Close,
557}
558
559#[derive(Debug, Clone)]
561pub enum BackpressureState {
562 Normal,
563 Warning,
564 Blocked,
565}
566
567pub struct AdaptiveStream {
569 stream_id: String,
570 data_receiver: mpsc::Receiver<StreamData>,
571 control_receiver: mpsc::Receiver<StreamControl>,
572 handle: StreamHandle,
573 compression_manager: Arc<AsyncMutex<CompressionManager>>,
574 #[allow(dead_code)]
575 metrics_collector: Arc<AsyncMutex<MetricsCollector>>,
576 #[allow(dead_code)]
577 config: StreamingConfig,
578 buffer: VecDeque<StreamData>,
579 paused: bool,
580}
581
582impl AdaptiveStream {
583 pub fn new(
584 stream_id: String,
585 data_receiver: mpsc::Receiver<StreamData>,
586 control_receiver: mpsc::Receiver<StreamControl>,
587 handle: StreamHandle,
588 compression_manager: Arc<AsyncMutex<CompressionManager>>,
589 metrics_collector: Arc<AsyncMutex<MetricsCollector>>,
590 config: StreamingConfig,
591 ) -> Self {
592 Self {
593 stream_id,
594 data_receiver,
595 control_receiver,
596 handle,
597 compression_manager,
598 metrics_collector,
599 config,
600 buffer: VecDeque::new(),
601 paused: false,
602 }
603 }
604
605 pub async fn next(&mut self) -> Option<Result<StreamData>> {
607 loop {
608 if !self.buffer.is_empty() && !self.paused {
609 return Some(Ok(self
610 .buffer
611 .pop_front()
612 .expect("buffer should not be empty after non-empty check")));
613 }
614
615 tokio::select! {
616 data = self.data_receiver.recv() => {
617 match data {
618 Some(data) => {
619 if self.paused {
620 self.buffer.push_back(data);
621 continue; } else {
623 return Some(Ok(data));
624 }
625 }
626 None => return None, }
628 }
629 control = self.control_receiver.recv() => {
630 match control {
631 Some(control) => {
632 self.handle_control_message(control).await;
633 }
635 None => return None, }
637 }
638 }
639 }
640 }
641
642 async fn handle_control_message(&mut self, control: StreamControl) {
644 match control {
645 StreamControl::Pause => {
646 debug!("Pausing stream {}", self.stream_id);
647 self.paused = true;
648 }
649 StreamControl::Resume => {
650 debug!("Resuming stream {}", self.stream_id);
651 self.paused = false;
652 }
653 StreamControl::ChangeCompressionLevel(level) => {
654 let mut compression_manager = self.compression_manager.lock().await;
655 compression_manager.set_compression_level(level).await;
656 }
657 StreamControl::ChangePriority(priority) => {
658 let mut metadata = self.handle.metadata.write().await;
659 metadata.priority = priority;
660 }
661 StreamControl::Heartbeat => {
662 let mut metadata = self.handle.metadata.write().await;
663 metadata.last_activity = Instant::now();
664 }
665 StreamControl::Close => {
666 info!("Closing stream {}", self.stream_id);
667 }
669 }
670 }
671}
672
673impl Stream for AdaptiveStream {
674 type Item = Result<StreamData>;
675
676 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
677 let future = self.next();
678 pin_mut!(future);
679 future.poll(cx)
680 }
681}
682
683#[derive(Debug)]
685pub struct StreamMultiplexer {
686 #[allow(dead_code)]
687 config: StreamingConfig,
688 registered_streams: HashMap<String, StreamPriority>,
689 #[allow(dead_code)]
690 multiplexing_enabled: bool,
691}
692
693impl StreamMultiplexer {
694 pub fn new(config: &StreamingConfig) -> Self {
695 Self {
696 config: config.clone(),
697 registered_streams: HashMap::new(),
698 multiplexing_enabled: config.enable_stream_multiplexing,
699 }
700 }
701
702 pub async fn register_stream(
703 &mut self,
704 stream_id: &str,
705 priority: &StreamPriority,
706 ) -> Result<()> {
707 self.registered_streams
708 .insert(stream_id.to_string(), priority.clone());
709 debug!(
710 "Registered stream {} with priority {:?}",
711 stream_id, priority
712 );
713 Ok(())
714 }
715
716 pub async fn unregister_stream(&mut self, stream_id: &str) -> Result<()> {
717 self.registered_streams.remove(stream_id);
718 debug!("Unregistered stream {}", stream_id);
719 Ok(())
720 }
721}
722
723#[derive(Debug)]
725pub struct BackpressureController {
726 #[allow(dead_code)]
727 config: StreamingConfig,
728 monitoring_enabled: bool,
729}
730
731impl BackpressureController {
732 pub fn new(config: &StreamingConfig) -> Self {
733 Self {
734 config: config.clone(),
735 monitoring_enabled: config.enable_backpressure_control,
736 }
737 }
738
739 pub async fn monitor_stream(
740 &mut self,
741 _stream_id: &str,
742 handle: &StreamHandle,
743 config: &StreamingConfig,
744 ) -> Result<()> {
745 if !self.monitoring_enabled {
746 return Ok(());
747 }
748
749 let buffer_utilization = self.calculate_buffer_utilization(handle).await?;
751
752 let mut backpressure_state = handle.backpressure_state.write().await;
753 *backpressure_state = if buffer_utilization > config.backpressure_threshold {
754 BackpressureState::Blocked
755 } else if buffer_utilization > config.backpressure_threshold * 0.7 {
756 BackpressureState::Warning
757 } else {
758 BackpressureState::Normal
759 };
760
761 Ok(())
762 }
763
764 async fn calculate_buffer_utilization(&self, handle: &StreamHandle) -> Result<f64> {
765 let metadata = handle.metadata.read().await;
767 let backpressure_state = handle.backpressure_state.read().await;
768
769 let activity_factor = if metadata.total_messages > 0 {
771 let recent_activity = metadata.last_activity.elapsed().as_secs() as f64;
772 (1.0 / (1.0 + recent_activity / 10.0)).min(0.8) } else {
774 0.1 };
776
777 let backpressure_factor = match *backpressure_state {
779 BackpressureState::Normal => 0.0,
780 BackpressureState::Warning => 0.2,
781 BackpressureState::Blocked => 0.4,
782 };
783
784 Ok((activity_factor + backpressure_factor).min(0.95))
785 }
786}
787
788#[derive(Debug)]
790pub struct CompressionManager {
791 #[allow(dead_code)]
792 config: CompressionConfig,
793 compression_level: u8,
794}
795
796impl CompressionManager {
797 pub fn new(config: &CompressionConfig) -> Self {
798 Self {
799 config: config.clone(),
800 compression_level: config.compression_level,
801 }
802 }
803
804 pub async fn compress_data(&self, data: &StreamData) -> Result<StreamData> {
805 Ok(data.clone())
807 }
808
809 pub async fn set_compression_level(&mut self, level: u8) {
810 self.compression_level = level;
811 }
812}
813
814#[derive(Debug)]
816pub struct PriorityScheduler {
817 #[allow(dead_code)]
818 config: PriorityConfig,
819 stream_priorities: HashMap<String, StreamPriority>,
820 #[allow(dead_code)]
821 priority_queues: Vec<VecDeque<String>>,
822}
823
824impl PriorityScheduler {
825 pub fn new(config: &PriorityConfig) -> Self {
826 Self {
827 config: config.clone(),
828 stream_priorities: HashMap::new(),
829 priority_queues: vec![VecDeque::new(); config.priority_queues],
830 }
831 }
832
833 pub async fn register_stream(
834 &mut self,
835 stream_id: &str,
836 priority: StreamPriority,
837 ) -> Result<()> {
838 self.stream_priorities
839 .insert(stream_id.to_string(), priority);
840 Ok(())
841 }
842
843 pub async fn unregister_stream(&mut self, stream_id: &str) -> Result<()> {
844 self.stream_priorities.remove(stream_id);
845 Ok(())
846 }
847
848 pub async fn schedule_broadcast(&mut self, _data: &StreamData) -> Result<Vec<String>> {
849 Ok(self.stream_priorities.keys().cloned().collect())
851 }
852}
853
854#[derive(Debug)]
856pub struct MetricsCollector {
857 collected_metrics: HashMap<String, StreamMetrics>,
858}
859
860impl Default for MetricsCollector {
861 fn default() -> Self {
862 Self::new()
863 }
864}
865
866impl MetricsCollector {
867 pub fn new() -> Self {
868 Self {
869 collected_metrics: HashMap::new(),
870 }
871 }
872
873 pub async fn collect_metrics(&mut self, streams: &HashMap<String, StreamHandle>) -> Result<()> {
874 for (stream_id, handle) in streams {
875 let metadata = handle.metadata.read().await;
876 let metrics = StreamMetrics {
877 stream_id: stream_id.clone(),
878 priority: metadata.priority.clone(),
879 total_messages: metadata.total_messages,
880 total_bytes: metadata.total_bytes,
881 duration: metadata.created_at.elapsed(),
882 last_activity: metadata.last_activity.elapsed(),
883 compression_enabled: metadata.compression_enabled,
884 };
885 self.collected_metrics.insert(stream_id.clone(), metrics);
886 }
887 Ok(())
888 }
889}
890
891#[derive(Debug, Clone)]
893pub struct StreamMetrics {
894 pub stream_id: String,
895 pub priority: StreamPriority,
896 pub total_messages: u64,
897 pub total_bytes: u64,
898 pub duration: Duration,
899 pub last_activity: Duration,
900 pub compression_enabled: bool,
901}