diff --git a/crates/adapters/src/controller.rs b/crates/adapters/src/controller.rs index e0a99ec77f5..f14cf32b51d 100644 --- a/crates/adapters/src/controller.rs +++ b/crates/adapters/src/controller.rs @@ -5470,6 +5470,7 @@ impl StatisticsThread { }; // Update time series.. + let latency = controller_status.latency_percentiles(); let sample = SampleStatistics { time: Utc::now(), total_processed_records: controller_status @@ -5477,6 +5478,10 @@ impl StatisticsThread { .num_total_processed_records(), memory_bytes: process_rss_bytes().unwrap_or_default(), storage_bytes, + processing_latency_p50_micros: latency.processing_p50, + processing_latency_p99_micros: latency.processing_p99, + completion_latency_p50_micros: latency.completion_p50, + completion_latency_p99_micros: latency.completion_p99, }; let mut time_series = controller_status.time_series.lock().unwrap(); if time_series.len() >= 60 { diff --git a/crates/adapters/src/controller/stats.rs b/crates/adapters/src/controller/stats.rs index a021c8b42aa..936b88399c9 100644 --- a/crates/adapters/src/controller/stats.rs +++ b/crates/adapters/src/controller/stats.rs @@ -751,6 +751,47 @@ impl ControllerStatus { self.inputs.read_recursive() } + /// Latency percentiles (microseconds) across input connectors, for the + /// time-series graph. Each connector contributes its own median latency, + /// we then take p50 and p99 across connectors, for both + /// processing (ingest to processed) and completion (ingest to all outputs). + /// `None` when no connector has samples. + pub fn latency_percentiles(&self) -> LatencyPercentiles { + let inputs = self.input_status(); + let mut processing = Vec::with_capacity(inputs.len()); + let mut completion = Vec::with_capacity(inputs.len()); + for ep in inputs.values() { + if let Some(median) = ep + .metrics + .processing_latency_micros_histogram + .lock() + .unwrap() + .snapshot() + .quantile(0.5) + { + processing.push(median); + } + if let Some(median) = ep + .metrics + .completion_latency_micros_histogram + .lock() + .unwrap() + .snapshot() + .quantile(0.5) + { + completion.push(median); + } + } + processing.sort_unstable(); + completion.sort_unstable(); + LatencyPercentiles { + processing_p50: percentile_of_sorted(&processing, 0.5), + processing_p99: percentile_of_sorted(&processing, 0.99), + completion_p50: percentile_of_sorted(&completion, 0.5), + completion_p99: percentile_of_sorted(&completion, 0.99), + } + } + /// Register connector-specific metrics for an input endpoint. pub fn set_input_custom_metrics( &self, @@ -1525,6 +1566,25 @@ impl InputEndpointMetrics { } } +/// Latency percentiles for the time series, in microseconds. See +/// [`ControllerStatus::latency_percentiles`]. +#[derive(Clone, Copy, Debug, Default)] +pub struct LatencyPercentiles { + pub processing_p50: Option, + pub processing_p99: Option, + pub completion_p50: Option, + pub completion_p99: Option, +} + +/// Returns `q`-quantile of an ascending-sorted slice (ceil-rank). `None` if empty. +fn percentile_of_sorted(sorted: &[u64], q: f64) -> Option { + if sorted.is_empty() { + return None; + } + let rank = ((q.clamp(0.0, 1.0) * sorted.len() as f64).ceil() as usize).clamp(1, sorted.len()); + Some(sorted[rank - 1]) +} + #[derive(Debug)] pub struct StepResults { pub amt: BufferSize, @@ -2800,3 +2860,63 @@ impl OutputEndpointStatus { self.metrics.total_processed_steps.load(Ordering::Acquire) } } + +#[cfg(test)] +mod latency_tests { + use super::percentile_of_sorted; + + #[test] + fn percentile_empty() { + assert_eq!(percentile_of_sorted(&[], 0.5), None); + assert_eq!(percentile_of_sorted(&[], 0.99), None); + } + + #[test] + fn percentile_single() { + assert_eq!(percentile_of_sorted(&[42], 0.5), Some(42)); + assert_eq!(percentile_of_sorted(&[42], 0.99), Some(42)); + } + + #[test] + fn percentile_uniform() { + let v: Vec = (1..=100).collect(); + // ceil(0.5*100)=50 -> 1-based rank 50 -> value 50. + assert_eq!(percentile_of_sorted(&v, 0.5), Some(50)); + // ceil(0.99*100)=99 -> value 99. + assert_eq!(percentile_of_sorted(&v, 0.99), Some(99)); + } + + #[test] + fn percentile_few_slow_connectors() { + // 97 fast connectors at 1ms, 3 slow at 500ms (values in micros). + let mut v = vec![1_000u64; 97]; + v.extend([500_000, 500_000, 500_000]); + v.sort_unstable(); + // p50 stays with the fast majority. + assert_eq!(percentile_of_sorted(&v, 0.5), Some(1_000)); + // p99 (rank 99) reaches the slow tail: surfaces the laggards. + assert_eq!(percentile_of_sorted(&v, 0.99), Some(500_000)); + } + + #[test] + fn percentile_broad_degradation() { + // 80 fast at 1ms, 20 slow at 100ms: ~20% degraded. + let mut v = vec![1_000u64; 80]; + v.extend(std::iter::repeat(100_000).take(20)); + v.sort_unstable(); + // Both p50 (typical) stays fast and p99 catches the slow band. + assert_eq!(percentile_of_sorted(&v, 0.5), Some(1_000)); + assert_eq!(percentile_of_sorted(&v, 0.99), Some(100_000)); + } + + #[test] + fn percentile_single_lone_outlier_escapes_p99() { + // Documents the known limit: one slow connector of 100 sits above p99. + let mut v = vec![1_000u64; 99]; + v.push(900_000); + v.sort_unstable(); + // p99 (rank 99) is still the fast majority; only max would catch it. + assert_eq!(percentile_of_sorted(&v, 0.99), Some(1_000)); + assert_eq!(percentile_of_sorted(&v, 1.0), Some(900_000)); + } +} diff --git a/crates/feldera-types/src/time_series.rs b/crates/feldera-types/src/time_series.rs index b3c9831aa24..143b5da0a63 100644 --- a/crates/feldera-types/src/time_series.rs +++ b/crates/feldera-types/src/time_series.rs @@ -34,4 +34,22 @@ pub struct SampleStatistics { /// Storage usage in bytes. #[serde(rename = "s")] pub storage_bytes: u64, + + /// Processing latency (ingest to circuit-processed), microseconds: + /// p50 across connectors of each connector's median. Absent without samples. + #[serde(rename = "pp50", skip_serializing_if = "Option::is_none", default)] + pub processing_latency_p50_micros: Option, + + /// Processing latency, microseconds: p99 across connectors. + #[serde(rename = "pp99", skip_serializing_if = "Option::is_none", default)] + pub processing_latency_p99_micros: Option, + + /// Completion latency (ingest to all outputs pushed), microseconds: + /// p50 across connectors. + #[serde(rename = "cp50", skip_serializing_if = "Option::is_none", default)] + pub completion_latency_p50_micros: Option, + + /// Completion latency, microseconds: p99 across connectors. + #[serde(rename = "cp99", skip_serializing_if = "Option::is_none", default)] + pub completion_latency_p99_micros: Option, } diff --git a/crates/storage/src/histogram.rs b/crates/storage/src/histogram.rs index f4de7018bd7..fc777592c9d 100644 --- a/crates/storage/src/histogram.rs +++ b/crates/storage/src/histogram.rs @@ -115,6 +115,25 @@ impl ExponentialHistogramSnapshot { pub fn sum(&self) -> u64 { self.sum } + + /// Approximate `q`-quantile (`q` in `0.0..=1.0`), as the lower bound of the + /// containing bucket. `None` if empty. + pub fn quantile(&self, q: f64) -> Option { + let total: u64 = self.buckets.iter().sum(); + if total == 0 { + return None; + } + let rank = ((q.clamp(0.0, 1.0) * total as f64).ceil() as u64).clamp(1, total); + let mut cumulative = 0u64; + for (index, count) in self.buckets.iter().enumerate() { + cumulative += *count; + if cumulative >= rank { + return Some(*bucket_to_range(index).start()); + } + } + // Unreachable: `cumulative` reaches `total >= rank` in the loop. + Some(*bucket_to_range(N_BUCKETS - 1).start()) + } } pub struct Bucket { @@ -278,7 +297,52 @@ impl SlidingHistogram { #[cfg(test)] mod test { - use crate::histogram::{N_BUCKETS, bucket_to_range, number_to_bucket}; + use crate::histogram::{ExponentialHistogram, N_BUCKETS, bucket_to_range, number_to_bucket}; + + #[test] + fn quantile_empty() { + let hist = ExponentialHistogram::new(); + assert_eq!(hist.snapshot().quantile(0.5), None); + assert_eq!(hist.snapshot().quantile(0.99), None); + } + + #[test] + fn quantile_single_sample() { + let hist = ExponentialHistogram::new(); + hist.record(42u64); + // 42 lands in the bucket for [40, 49]; the lower bound is 40. + assert_eq!(hist.snapshot().quantile(0.0), Some(40)); + assert_eq!(hist.snapshot().quantile(0.5), Some(40)); + assert_eq!(hist.snapshot().quantile(1.0), Some(40)); + } + + #[test] + fn quantile_all_same_bucket() { + let hist = ExponentialHistogram::new(); + for _ in 0..100 { + hist.record(5u64); + } + // Values 0..=9 each occupy their own bucket, so 5 is exact. + assert_eq!(hist.snapshot().quantile(0.5), Some(5)); + assert_eq!(hist.snapshot().quantile(0.99), Some(5)); + } + + #[test] + fn quantile_known_distribution() { + let hist = ExponentialHistogram::new(); + // 99 fast samples (bucket [0,0]) and 1 slow sample (bucket [900,999]). + for _ in 0..99 { + hist.record(0u64); + } + hist.record(950u64); + let snap = hist.snapshot(); + // p50 stays with the fast majority. + assert_eq!(snap.quantile(0.5), Some(0)); + // p99 (ceil(0.99*100)=99th value) is still the last fast sample. + assert_eq!(snap.quantile(0.99), Some(0)); + // p100 reaches the lone slow sample: bucket [900, 999], lower bound 900. + assert_eq!(snap.quantile(1.0), Some(900)); + } #[test] fn buckets() { diff --git a/js-packages/web-console/src/assets/fonts/feldera-material-icons.css b/js-packages/web-console/src/assets/fonts/feldera-material-icons.css index 22b32d51e98..9c019f34be1 100644 --- a/js-packages/web-console/src/assets/fonts/feldera-material-icons.css +++ b/js-packages/web-console/src/assets/fonts/feldera-material-icons.css @@ -2,8 +2,8 @@ font-family: "feldera-material-icons"; font-display: block; src: - url("feldera-material-icons.woff2?06f8372fd6db29f5f189dc16b2f9c8af") format("woff2"), - url("feldera-material-icons.woff?06f8372fd6db29f5f189dc16b2f9c8af") format("woff"); + url("feldera-material-icons.woff2?cf6c748bd7d9c0cae82a82a930cdf145") format("woff2"), + url("feldera-material-icons.woff?cf6c748bd7d9c0cae82a82a930cdf145") format("woff"); } .fd { @@ -17,204 +17,210 @@ vertical-align: top; } -.fd-chevron-down:before { +.fd-construction:before { content: "\f101"; } -.fd-square:before { +.fd-lightbulb:before { content: "\f102"; } -.fd-wine:before { +.fd-server-off:before { content: "\f103"; } -.fd-trash-2:before { +.fd-circle-check-big:before { content: "\f104"; } -.fd-book-open:before { +.fd-panel-right:before { content: "\f105"; } -.fd-moon:before { +.fd-wine:before { content: "\f106"; } -.fd-circle-x:before { +.fd-book-open:before { content: "\f107"; } -.fd-mic-off:before { +.fd-info:before { content: "\f108"; } -.fd-plus:before { +.fd-more_horiz:before { content: "\f109"; } -.fd-circle-play:before { +.fd-panel-left:before { content: "\f10a"; } -.fd-chevron-left:before { +.fd-heart-crack:before { content: "\f10b"; } -.fd-triangle-alert:before { +.fd-menu:before { content: "\f10c"; } -.fd-paintbrush:before { +.fd-circle-slash:before { content: "\f10d"; } -.fd-circle-stop:before { +.fd-layout-panel-left:before { content: "\f10e"; } -.fd-chevron-right:before { +.fd-server-cog:before { content: "\f10f"; } -.fd-mic:before { +.fd-server:before { content: "\f110"; } -.fd-square-power:before { +.fd-arrow-right:before { content: "\f111"; } -.fd-circle-check-big:before { +.fd-message-circle-question:before { content: "\f112"; } -.fd-circle-dot:before { +.fd-x:before { content: "\f113"; } -.fd-panel-right:before { +.fd-circle-pause:before { content: "\f114"; } -.fd-user:before { +.fd-file-text:before { content: "\f115"; } -.fd-circle-stop-locked:before { +.fd-chevron-left:before { content: "\f116"; } -.fd-sliders-horizontal:before { +.fd-copy-plus:before { content: "\f117"; } -.fd-panel-left:before { +.fd-pause:before { content: "\f118"; } -.fd-pencil-line:before { +.fd-book-marked:before { content: "\f119"; } -.fd-info:before { +.fd-moon:before { content: "\f11a"; } -.fd-database-off:before { +.fd-network:before { content: "\f11b"; } -.fd-sun:before { +.fd-plus:before { content: "\f11c"; } -.fd-file-text:before { +.fd-triangle-alert:before { content: "\f11d"; } -.fd-panel-bottom:before { +.fd-settings:before { content: "\f11e"; } -.fd-x:before { +.fd-chevron-down:before { content: "\f11f"; } -.fd-layout-panel-top:before { +.fd-circle-dot:before { content: "\f120"; } -.fd-settings:before { +.fd-trash-2:before { content: "\f121"; } -.fd-play:before { +.fd-mic:before { content: "\f122"; } -.fd-rocket:before { +.fd-circle-stop-locked:before { content: "\f123"; } -.fd-server:before { +.fd-user:before { content: "\f124"; } -.fd-circle-plus:before { +.fd-mic-off:before { content: "\f125"; } -.fd-more_horiz:before { +.fd-rocket:before { content: "\f126"; } -.fd-key:before { +.fd-paintbrush:before { content: "\f127"; } -.fd-copy:before { +.fd-circle-plus:before { content: "\f128"; } -.fd-arrow-down:before { +.fd-circle-divide:before { content: "\f129"; } -.fd-circle-pause:before { +.fd-copy:before { content: "\f12a"; } -.fd-menu:before { +.fd-circle-help:before { content: "\f12b"; } -.fd-lightbulb:before { +.fd-circle-play:before { content: "\f12c"; } -.fd-server-cog:before { +.fd-receipt-text:before { content: "\f12d"; } -.fd-save:before { +.fd-eraser:before { content: "\f12e"; } -.fd-database:before { +.fd-dot:before { content: "\f12f"; } -.fd-lock-open:before { +.fd-square-power:before { content: "\f130"; } -.fd-log-out:before { +.fd-panel-bottom:before { content: "\f131"; } -.fd-layout-panel-left:before { +.fd-chevron-right:before { content: "\f132"; } -.fd-server-off:before { +.fd-arrow-down:before { content: "\f133"; } -.fd-circle-slash:before { +.fd-circle-user:before { content: "\f134"; } -.fd-arrow-right:before { +.fd-check:before { content: "\f135"; } -.fd-construction:before { +.fd-database-off:before { content: "\f136"; } -.fd-file-down:before { +.fd-log-out:before { content: "\f137"; } -.fd-circle-divide:before { +.fd-key:before { content: "\f138"; } -.fd-network:before { +.fd-circle-stop:before { content: "\f139"; } -.fd-pause:before { +.fd-file-down:before { content: "\f13a"; } -.fd-copy-plus:before { +.fd-layout-panel-top:before { content: "\f13b"; } -.fd-book-marked:before { +.fd-save:before { content: "\f13c"; } -.fd-dot:before { +.fd-database:before { content: "\f13d"; } -.fd-circle-user:before { +.fd-sliders-horizontal:before { content: "\f13e"; } .fd-circle-alert:before { content: "\f13f"; } -.fd-message-circle-question:before { +.fd-pencil-line:before { content: "\f140"; } -.fd-receipt-text:before { +.fd-play:before { content: "\f141"; } -.fd-eraser:before { +.fd-lock-open:before { content: "\f142"; } -.fd-check:before { +.fd-square:before { content: "\f143"; } +.fd-circle-x:before { + content: "\f144"; +} +.fd-sun:before { + content: "\f145"; +} diff --git a/js-packages/web-console/src/assets/fonts/feldera-material-icons.svg b/js-packages/web-console/src/assets/fonts/feldera-material-icons.svg index 88d9035756f..126a221a4db 100644 --- a/js-packages/web-console/src/assets/fonts/feldera-material-icons.svg +++ b/js-packages/web-console/src/assets/fonts/feldera-material-icons.svg @@ -7,408 +7,420 @@ units-per-em="128" ascent="128" descent="0" /> - - - + + - - + + + + + + + + - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - + horiz-adv-x="128" d="M59.9466666666667 116.8213333333333C56.7733333333333 115.888 54.704 114.6293333333333 52.448 112.2613333333333C50.64 110.3626666666667 51.2373333333333 111.3706666666667 33.392 80.1066666666667C9.904 38.96 6.5013333333333 32.9066666666667 6.0586666666667 31.4666666666667C5.4506666666667 29.4773333333334 5.2266666666667 26.8426666666667 5.488 24.8C6.0053333333333 20.816 7.1733333333333 18.4586666666667 10.1333333333333 15.4666666666667C12.176 13.3973333333333 13.4666666666667 12.5813333333333 16.2666666666667 11.584C17.7973333333333 11.04 17.8986666666667 11.0293333333333 27.0933333333333 10.8373333333333C32.1973333333333 10.7253333333333 52.7893333333333 10.6826666666667 72.8533333333333 10.7413333333333L109.3333333333333 10.8426666666667L110.8266666666667 11.312C115.3493333333333 12.736 118.9226666666667 15.664 120.8373333333333 19.52C121.8826666666667 21.6266666666667 122.2666666666667 23.216 122.3946666666667 25.952C122.5493333333333 29.2906666666667 122.1546666666667 30.9546666666667 120.4213333333333 34.2773333333333C118.0693333333333 38.768 105.36 61.184 88.6826666666667 90.24C83.3973333333333 99.4506666666667 78.5813333333333 107.8346666666667 77.9893333333333 108.8746666666667C77.392 109.9093333333333 76.3946666666667 111.3226666666667 75.7706666666667 112.016C74.4 113.5306666666667 71.76 115.312 69.44 116.2773333333333C67.7813333333333 116.9653333333333 67.6373333333333 116.9866666666667 64.32 117.0453333333333C61.9466666666667 117.088 60.6133333333333 117.0186666666667 59.9466666666667 116.8213333333333M65.0613333333333 106.6453333333333C66.976 106.24 67.6586666666667 105.392 71.968 98.08C78.4906666666667 87.0133333333333 111.392 29.2053333333333 111.7493333333333 28.1813333333333C112.5546666666667 25.856 110.9386666666667 22.544 108.592 21.7066666666667C108 21.4986666666667 98.6186666666667 21.44 63.7866666666667 21.44L19.7333333333333 21.44L18.8586666666667 21.9093333333333C17.8453333333333 22.4533333333333 16.7093333333333 23.6693333333333 16.2933333333333 24.672C16.1333333333333 25.056 16 25.9946666666667 16 26.7466666666667C16 27.92 16.1173333333333 28.3413333333333 16.768 29.5733333333334C17.5253333333333 31.0026666666667 33.072 58.272 50.496 88.7466666666667C60.1066666666667 105.552 60.3253333333333 105.8826666666667 62.464 106.544C63.7173333333333 106.928 63.7493333333333 106.928 65.0613333333333 106.6453333333333M61.9573333333333 84.816C60.9866666666667 84.3946666666667 59.7706666666667 83.2533333333333 59.2426666666667 82.2613333333333C58.7733333333333 81.392 58.7733333333333 81.328 58.7733333333333 69.3333333333333C58.7733333333333 57.3813333333334 58.7786666666667 57.2746666666667 59.232 56.4266666666667C60.2666666666667 54.512 61.872 53.5466666666667 64 53.5466666666667C66.128 53.5466666666667 67.7333333333333 54.512 68.768 56.4266666666667C69.2213333333333 57.2746666666667 69.2266666666667 57.3813333333334 69.2266666666667 69.3333333333333C69.2266666666667 81.2746666666667 69.2213333333333 81.392 68.768 82.24C68.208 83.2746666666667 67.3813333333333 84.0693333333333 66.2453333333333 84.672C65.2426666666667 85.2 63.008 85.2746666666667 61.9573333333333 84.816M61.712 42.0053333333333C59.9253333333333 41.1253333333333 58.8373333333333 39.36 58.832 37.3386666666667C58.8213333333333 35.7653333333333 59.3706666666667 34.512 60.5013333333333 33.52C62.672 31.6053333333333 65.8506666666667 31.7066666666667 67.7653333333333 33.7386666666667C68.8533333333333 34.9013333333333 69.2106666666667 35.792 69.2106666666667 37.3333333333333C69.2106666666667 39.456 67.9893333333333 41.328 66.0853333333333 42.1226666666667C64.9226666666667 42.608 62.8106666666667 42.5493333333334 61.712 42.0053333333333" /> + - + + + + + + - - + + + + + + + + - - + + - - - - + + - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + - - - - - - - - - + + - - + + - - + + - + horiz-adv-x="128" d="M72.624 122.1493333333333C71.6533333333333 121.728 70.4373333333333 120.5866666666667 69.9093333333333 119.5946666666667C69.44 118.7253333333334 69.44 118.6613333333333 69.44 106.6666666666667C69.44 94.7146666666667 69.4453333333333 94.608 69.8986666666667 93.76C70.9333333333333 91.8453333333333 72.5386666666667 90.88 74.6666666666667 90.88C76.192 90.88 77.28 91.2853333333333 78.2613333333333 92.224C79.6426666666667 93.5466666666667 79.8453333333333 94.2453333333333 79.9573333333333 97.9573333333333L80.0533333333333 101.296L96.72 101.3706666666667C113.312 101.4453333333333 113.392 101.4453333333333 114.24 101.904C116.1706666666667 102.944 117.1253333333333 104.5386666666667 117.1146666666667 106.7146666666667C117.1093333333333 108.3626666666667 116.624 109.5093333333333 115.5146666666667 110.48C113.9146666666667 111.888 114.8266666666667 111.8133333333333 96.624 111.9466666666667L80.0533333333333 112.064L79.9573333333333 115.392C79.872 118.2186666666667 79.792 118.848 79.4133333333333 119.5733333333334C78.8853333333333 120.5973333333334 78.0586666666667 121.3973333333333 76.912 122.0053333333334C75.9093333333333 122.5333333333333 73.6746666666667 122.608 72.624 122.1493333333333M19.6266666666667 111.808C13.52 111.664 13.4453333333333 111.648 12.0746666666667 110.048C11.1893333333333 109.0133333333333 10.8853333333333 108.096 10.88 106.5013333333333C10.88 104.4693333333333 11.9306666666667 102.864 13.9146666666667 101.8453333333333L14.9173333333333 101.3333333333333L34.6666666666667 101.3333333333333L54.416 101.3333333333333L55.4186666666667 101.8453333333333C57.4613333333333 102.8906666666667 58.4533333333333 104.464 58.4533333333333 106.6666666666667C58.4533333333333 108.7733333333333 57.632 110.1866666666667 55.7866666666667 111.248L54.9333333333333 111.744L40 111.84C31.7866666666667 111.888 22.6186666666667 111.8773333333333 19.6266666666667 111.808M40.624 79.4826666666667C39.6373333333333 79.056 38.432 77.9093333333333 37.9253333333333 76.928C37.5413333333333 76.1813333333333 37.4613333333333 75.568 37.376 72.7253333333333L37.28 69.3973333333333L26.0426666666667 69.28C13.7173333333333 69.152 14.048 69.1893333333333 12.4853333333333 67.8133333333333C11.376 66.8426666666667 10.8906666666667 65.696 10.8853333333333 64.048C10.8746666666667 61.872 11.8346666666667 60.272 13.76 59.2373333333333C14.5973333333333 58.7893333333333 14.832 58.7786666666667 25.9466666666667 58.704L37.28 58.6293333333333L37.376 55.2853333333333C37.488 51.5786666666667 37.6906666666667 50.88 39.072 49.5573333333333C40.0533333333333 48.6186666666667 41.1413333333333 48.2133333333333 42.6666666666667 48.2133333333333C44.7946666666667 48.2133333333333 46.4 49.1786666666667 47.4346666666667 51.0933333333333C47.888 51.9413333333334 47.8933333333333 52.048 47.8933333333333 64C47.8933333333333 75.9413333333334 47.888 76.0586666666667 47.4346666666667 76.9066666666667C46.8746666666667 77.9413333333334 46.048 78.736 44.912 79.3386666666667C43.9093333333333 79.8666666666667 41.6746666666667 79.9413333333334 40.624 79.4826666666667M69.0133333333333 69.136C61.36 68.9973333333333 61.4826666666667 69.024 60.0746666666667 67.3813333333334C59.1893333333333 66.3466666666667 58.8853333333333 65.4293333333334 58.88 63.8346666666667C58.88 61.8026666666667 59.9306666666667 60.1973333333334 61.9146666666667 59.1786666666667L62.9173333333333 58.6666666666667L88 58.6666666666667L113.0826666666667 58.6666666666667L114.0853333333333 59.1786666666667C116.128 60.224 117.12 61.7973333333333 117.12 64C117.12 66.1066666666667 116.2986666666667 67.52 114.4533333333333 68.5813333333333L113.6 69.0773333333333L94.72 69.168C84.336 69.2213333333333 72.768 69.2053333333333 69.0133333333333 69.136M83.2906666666667 36.816C82.32 36.3946666666667 81.104 35.2533333333333 80.576 34.2613333333333C80.1066666666667 33.392 80.1066666666667 33.328 80.1066666666667 21.3333333333333C80.1066666666667 9.3813333333333 80.112 9.2746666666667 80.5653333333333 8.4266666666667C81.6 6.512 83.2053333333333 5.5466666666667 85.3333333333333 5.5466666666667C86.8586666666667 5.5466666666667 87.9466666666667 5.952 88.928 6.8906666666667C90.3093333333333 8.2133333333333 90.512 8.912 90.624 12.6186666666667L90.72 15.9626666666667L102.0533333333333 16.0373333333334C113.168 16.112 113.4026666666667 16.1226666666667 114.24 16.5706666666667C116.1653333333333 17.6053333333333 117.1253333333333 19.2053333333333 117.1146666666667 21.3813333333333C117.1093333333333 23.0293333333333 116.624 24.176 115.5146666666667 25.1466666666667C113.952 26.5226666666667 114.2826666666667 26.4853333333333 101.9573333333333 26.6133333333333L90.72 26.7306666666667L90.624 30.0586666666667C90.5386666666667 32.8853333333333 90.4586666666667 33.5146666666667 90.08 34.24C89.552 35.264 88.7253333333333 36.064 87.5786666666667 36.672C86.576 37.2 84.3413333333333 37.2746666666667 83.2906666666667 36.816M21.0133333333333 26.4693333333333C13.36 26.3306666666667 13.4826666666667 26.3573333333333 12.0746666666667 24.7146666666667C11.1893333333333 23.68 10.8853333333333 22.7626666666667 10.88 21.168C10.88 19.136 11.9306666666667 17.5306666666667 13.9146666666667 16.512L14.9173333333333 16L40 16L65.0826666666667 16L66.0853333333333 16.512C68.128 17.5573333333333 69.12 19.1306666666667 69.12 21.3333333333333C69.12 23.44 68.2986666666667 24.8533333333333 66.4533333333333 25.9146666666667L65.6 26.4106666666667L46.72 26.5013333333334C36.336 26.5546666666667 24.768 26.5386666666667 21.0133333333333 26.4693333333333" /> + - - - + + - - + + - - + + - + horiz-adv-x="128" d="M22.848 116.7893333333333C19.0346666666667 115.9626666666667 14.5706666666667 112.4853333333333 12.6133333333333 108.8213333333333C12.2613333333333 108.1546666666667 11.6746666666667 106.704 11.3173333333333 105.6L10.6666666666667 103.5893333333333L10.6666666666667 64L10.6666666666667 24.4106666666667L11.3173333333333 22.4C12.448 18.9066666666667 13.9253333333333 16.6933333333333 16.6666666666667 14.3626666666667C18.3146666666667 12.9653333333333 19.872 12.1386666666667 22.4 11.3173333333333L24.4106666666667 10.6666666666667L64 10.6666666666667L103.5893333333333 10.6666666666667L105.6 11.3173333333333C108.128 12.1386666666667 109.6213333333333 12.9333333333333 111.28 14.352C114.096 16.7573333333333 115.5573333333333 18.928 116.6826666666667 22.4L117.3333333333333 24.4106666666667L117.3333333333333 64L117.3333333333333 103.5893333333333L116.6826666666667 105.6C115.5413333333334 109.12 114.1493333333333 111.1946666666667 111.28 113.648C109.7706666666667 114.9386666666667 108.2666666666667 115.776 106.0693333333333 116.5653333333333L104.5066666666667 117.12L64.3626666666667 117.104C30.9653333333333 117.088 23.984 117.0346666666667 22.848 116.7893333333333M103.36 106.2666666666667C104.6186666666667 105.696 105.504 104.864 106.064 103.728L106.56 102.72L106.6186666666667 66.7733333333333C106.656 47.0026666666667 106.6026666666667 29.4133333333333 106.5013333333333 27.68C106.3306666666667 24.672 106.2933333333333 24.496 105.6853333333333 23.6266666666667C105.3386666666667 23.1306666666667 104.528 22.4053333333333 103.8933333333333 22.0266666666667L102.7413333333333 21.3333333333333L65.0773333333333 21.3386666666667C44.3573333333333 21.344 26.752 21.4346666666667 25.9413333333333 21.536C24.1386666666667 21.7706666666667 23.0133333333333 22.4853333333333 22.0853333333333 23.9893333333334L21.44 25.0346666666667L21.44 63.8773333333333L21.44 102.72L21.936 103.728C22.496 104.864 23.3813333333333 105.696 24.64 106.2666666666667C25.456 106.64 27.1893333333333 106.656 64 106.656C100.8106666666667 106.656 102.544 106.64 103.36 106.2666666666667" /> + + + + + diff --git a/js-packages/web-console/src/assets/fonts/feldera-material-icons.ttf b/js-packages/web-console/src/assets/fonts/feldera-material-icons.ttf index 8b1d7fa9769..75b5dbd4162 100644 Binary files a/js-packages/web-console/src/assets/fonts/feldera-material-icons.ttf and b/js-packages/web-console/src/assets/fonts/feldera-material-icons.ttf differ diff --git a/js-packages/web-console/src/assets/fonts/feldera-material-icons.woff b/js-packages/web-console/src/assets/fonts/feldera-material-icons.woff index 3e552c9e818..292bfb8bfd6 100644 Binary files a/js-packages/web-console/src/assets/fonts/feldera-material-icons.woff and b/js-packages/web-console/src/assets/fonts/feldera-material-icons.woff differ diff --git a/js-packages/web-console/src/assets/fonts/feldera-material-icons.woff2 b/js-packages/web-console/src/assets/fonts/feldera-material-icons.woff2 index 199c0aa8dd6..95c5dec71d7 100644 Binary files a/js-packages/web-console/src/assets/fonts/feldera-material-icons.woff2 and b/js-packages/web-console/src/assets/fonts/feldera-material-icons.woff2 differ diff --git a/js-packages/web-console/src/assets/fonts/generic-icons.css b/js-packages/web-console/src/assets/fonts/generic-icons.css index 34ae10699cb..40d8aab5a38 100644 --- a/js-packages/web-console/src/assets/fonts/generic-icons.css +++ b/js-packages/web-console/src/assets/fonts/generic-icons.css @@ -2,8 +2,8 @@ font-family: "generic-icons"; font-display: block; src: - url("generic-icons.woff2?93adf303867ea39c2b4e5835c473116a") format("woff2"), - url("generic-icons.woff?93adf303867ea39c2b4e5835c473116a") format("woff"); + url("generic-icons.woff2?033f330cef2f06323d21562ae6e6b4d9") format("woff2"), + url("generic-icons.woff?033f330cef2f06323d21562ae6e6b4d9") format("woff"); } .gc { @@ -17,18 +17,18 @@ vertical-align: top; } -.gc-layout-panel-right:before { +.gc-http-get:before { content: "\f101"; } .gc-circle-solid:before { content: "\f102"; } -.gc-loader-alt:before { +.gc-layout-panel-right:before { content: "\f103"; } .gc-boiling-flask:before { content: "\f104"; } -.gc-http-get:before { +.gc-loader-alt:before { content: "\f105"; } diff --git a/js-packages/web-console/src/assets/fonts/generic-icons.svg b/js-packages/web-console/src/assets/fonts/generic-icons.svg index b3269604155..c994c560a5e 100644 --- a/js-packages/web-console/src/assets/fonts/generic-icons.svg +++ b/js-packages/web-console/src/assets/fonts/generic-icons.svg @@ -7,36 +7,36 @@ units-per-em="128" ascent="128" descent="0" /> - - + horiz-adv-x="128" d="" /> + - - + horiz-adv-x="128" d="M91.818 92H36.1818C32.7818 92 30 89.2 30 85.7776V42.2224C30 38.8 32.7818 36 36.1818 36H91.818C95.218 36 98 38.8 98 42.2224V85.7776C98 89.2 95.218 92 91.818 92zM91.818 42.2224H36.1818V85.7776H91.818V42.2224zM74 82H88V45.3332H74V82z" /> + - - + horiz-adv-x="128" d="M64 10.6666666666667C92.912 10.6666666666667 117.3333333333333 35.088 117.3333333333333 64H106.6666666666667C106.6666666666667 40.8693333333333 87.1306666666667 21.3333333333333 64 21.3333333333333S21.3333333333333 40.8693333333333 21.3333333333333 64C21.3333333333333 87.1253333333334 40.8693333333333 106.6666666666667 64 106.6666666666667V117.3333333333333C35.088 117.3333333333333 10.6666666666667 92.9066666666667 10.6666666666667 64C10.6666666666667 35.088 35.088 10.6666666666667 64 10.6666666666667z" /> + diff --git a/js-packages/web-console/src/assets/fonts/generic-icons.ttf b/js-packages/web-console/src/assets/fonts/generic-icons.ttf index e9db6e59e2d..1cfb76f2a02 100644 Binary files a/js-packages/web-console/src/assets/fonts/generic-icons.ttf and b/js-packages/web-console/src/assets/fonts/generic-icons.ttf differ diff --git a/js-packages/web-console/src/assets/fonts/generic-icons.woff b/js-packages/web-console/src/assets/fonts/generic-icons.woff index 102d845addb..9a8488a591c 100644 Binary files a/js-packages/web-console/src/assets/fonts/generic-icons.woff and b/js-packages/web-console/src/assets/fonts/generic-icons.woff differ diff --git a/js-packages/web-console/src/assets/fonts/generic-icons.woff2 b/js-packages/web-console/src/assets/fonts/generic-icons.woff2 index 0a75081b4e8..665bca71460 100644 Binary files a/js-packages/web-console/src/assets/fonts/generic-icons.woff2 and b/js-packages/web-console/src/assets/fonts/generic-icons.woff2 differ diff --git a/js-packages/web-console/src/assets/icons/feldera-material-icons/circle-help.svg b/js-packages/web-console/src/assets/icons/feldera-material-icons/circle-help.svg new file mode 100755 index 00000000000..4d290c0da59 --- /dev/null +++ b/js-packages/web-console/src/assets/icons/feldera-material-icons/circle-help.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/js-packages/web-console/src/lib/components/layout/pipelines/PipelineLatencyGraph.svelte b/js-packages/web-console/src/lib/components/layout/pipelines/PipelineLatencyGraph.svelte new file mode 100644 index 00000000000..626e58e38ff --- /dev/null +++ b/js-packages/web-console/src/lib/components/layout/pipelines/PipelineLatencyGraph.svelte @@ -0,0 +1,262 @@ + + +
+ +
+
+ {#if compactTitle} + Latency: + {formatDuration(currentProcessing)} + | + {formatDuration(currentCompletion)} + {:else} + Latency: + {formatDuration(currentProcessing)} + step | + {formatDuration(currentCompletion)} + end-to-end + {/if} +
+
+ + +

+ Time from record ingest, median across input connectors. +
Step: until the record has been processed by the circuit. +
End-to-end: until the related output changes have been written to all + outputs. +

+ Documentation +
+
+
+ +
+ +
+ + + {#key pipelineName} + (ref = init(dom, theme, opts))} {options} /> + {/key} +
diff --git a/js-packages/web-console/src/lib/components/pipelines/editor/TabPerformance.svelte b/js-packages/web-console/src/lib/components/pipelines/editor/TabPerformance.svelte index 2dd2a0694b6..32229b18d00 100644 --- a/js-packages/web-console/src/lib/components/pipelines/editor/TabPerformance.svelte +++ b/js-packages/web-console/src/lib/components/pipelines/editor/TabPerformance.svelte @@ -7,6 +7,7 @@