diff --git a/dc/s2n-quic-dc/events/map.rs b/dc/s2n-quic-dc/events/map.rs index 24c3ae227..f1cadf08b 100644 --- a/dc/s2n-quic-dc/events/map.rs +++ b/dc/s2n-quic-dc/events/map.rs @@ -307,3 +307,50 @@ struct PathSecretMapIdCacheAccessed<'a> { #[bool_counter("hit")] hit: bool, } + +#[event("path_secret_map:cleaner_cycled")] +#[subject(endpoint)] +/// Emitted when the cleaner task performed a single cycle +/// +/// This can be used to track cache utilization +struct PathSecretMapCleanerCycled { + /// The number of Path Secret ID entries left after the cleaning cycle + #[measure("entries")] + entries: usize, + + /// The number of Path Secret ID entries that were retired in the cycle + #[measure("entries.retired")] + retired_entries: usize, + + /// The utilization percentage of the available number of entries after the cycle + #[measure("entries.utilization", Percent)] + entries_utilization: f32, + + /// The utilization percentage of the available number of entries before the cycle + #[measure("entries.utilization.initial", Percent)] + entries_initial_utilization: f32, + + /// The number of SocketAddress entries left after the cleaning cycle + #[measure("addresses")] + addresses: usize, + + /// The number of SocketAddress entries that were retired in the cycle + #[measure("addresses.retired")] + retired_addresses: usize, + + /// The utilization percentage of the available number of address entries after the cycle + #[measure("addresses.utilization", Percent)] + addresses_utilization: f32, + + /// The utilization percentage of the available number of address entries before the cycle + #[measure("addresses.utilization.initial", Percent)] + addresses_initial_utilization: f32, + + /// The number of handshake requests that are pending after the cleaning cycle + #[measure("handshake_requests")] + handshake_requests: usize, + + /// The number of handshake requests that were retired in the cycle + #[measure("retired_handshake_requests")] + retired_handshake_requests: usize, +} diff --git a/dc/s2n-quic-dc/src/event/generated.rs b/dc/s2n-quic-dc/src/event/generated.rs index 8c4993d41..7a58ed916 100644 --- a/dc/s2n-quic-dc/src/event/generated.rs +++ b/dc/s2n-quic-dc/src/event/generated.rs @@ -1190,6 +1190,62 @@ pub mod api { impl<'a> Event for PathSecretMapIdCacheAccessed<'a> { const NAME: &'static str = "path_secret_map:id_cache_accessed"; } + #[derive(Clone, Debug)] + #[non_exhaustive] + #[doc = " Emitted when the cleaner task performed a single cycle"] + #[doc = ""] + #[doc = " This can be used to track cache utilization"] + pub struct PathSecretMapCleanerCycled { + #[doc = " The number of Path Secret ID entries left after the cleaning cycle"] + pub entries: usize, + #[doc = " The number of Path Secret ID entries that were retired in the cycle"] + pub retired_entries: usize, + #[doc = " The utilization percentage of the available number of entries after the cycle"] + pub entries_utilization: f32, + #[doc = " The utilization percentage of the available number of entries before the cycle"] + pub entries_initial_utilization: f32, + #[doc = " The number of SocketAddress entries left after the cleaning cycle"] + pub addresses: usize, + #[doc = " The number of SocketAddress entries that were retired in the cycle"] + pub retired_addresses: usize, + #[doc = " The utilization percentage of the available number of address entries after the cycle"] + pub addresses_utilization: f32, + #[doc = " The utilization percentage of the available number of address entries before the cycle"] + pub addresses_initial_utilization: f32, + #[doc = " The number of handshake requests that are pending after the cleaning cycle"] + pub handshake_requests: usize, + #[doc = " The number of handshake requests that were retired in the cycle"] + pub retired_handshake_requests: usize, + } + #[cfg(any(test, feature = "testing"))] + impl crate::event::snapshot::Fmt for PathSecretMapCleanerCycled { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + let mut fmt = fmt.debug_struct("PathSecretMapCleanerCycled"); + fmt.field("entries", &self.entries); + fmt.field("retired_entries", &self.retired_entries); + fmt.field("entries_utilization", &self.entries_utilization); + fmt.field( + "entries_initial_utilization", + &self.entries_initial_utilization, + ); + fmt.field("addresses", &self.addresses); + fmt.field("retired_addresses", &self.retired_addresses); + fmt.field("addresses_utilization", &self.addresses_utilization); + fmt.field( + "addresses_initial_utilization", + &self.addresses_initial_utilization, + ); + fmt.field("handshake_requests", &self.handshake_requests); + fmt.field( + "retired_handshake_requests", + &self.retired_handshake_requests, + ); + fmt.finish() + } + } + impl Event for PathSecretMapCleanerCycled { + const NAME: &'static str = "path_secret_map:cleaner_cycled"; + } impl IntoEvent for s2n_codec::DecoderError { fn into_event(self) -> builder::AcceptorPacketDropReason { use builder::AcceptorPacketDropReason as Reason; @@ -1849,6 +1905,27 @@ pub mod tracing { let api::PathSecretMapIdCacheAccessed { credential_id, hit } = event; tracing :: event ! (target : "path_secret_map_id_cache_accessed" , parent : parent , tracing :: Level :: DEBUG , credential_id = tracing :: field :: debug (credential_id) , hit = tracing :: field :: debug (hit)); } + #[inline] + fn on_path_secret_map_cleaner_cycled( + &self, + meta: &api::EndpointMeta, + event: &api::PathSecretMapCleanerCycled, + ) { + let parent = self.parent(meta); + let api::PathSecretMapCleanerCycled { + entries, + retired_entries, + entries_utilization, + entries_initial_utilization, + addresses, + retired_addresses, + addresses_utilization, + addresses_initial_utilization, + handshake_requests, + retired_handshake_requests, + } = event; + tracing :: event ! (target : "path_secret_map_cleaner_cycled" , parent : parent , tracing :: Level :: DEBUG , entries = tracing :: field :: debug (entries) , retired_entries = tracing :: field :: debug (retired_entries) , entries_utilization = tracing :: field :: debug (entries_utilization) , entries_initial_utilization = tracing :: field :: debug (entries_initial_utilization) , addresses = tracing :: field :: debug (addresses) , retired_addresses = tracing :: field :: debug (retired_addresses) , addresses_utilization = tracing :: field :: debug (addresses_utilization) , addresses_initial_utilization = tracing :: field :: debug (addresses_initial_utilization) , handshake_requests = tracing :: field :: debug (handshake_requests) , retired_handshake_requests = tracing :: field :: debug (retired_handshake_requests)); + } } } pub mod builder { @@ -2995,6 +3072,61 @@ pub mod builder { } } } + #[derive(Clone, Debug)] + #[doc = " Emitted when the cleaner task performed a single cycle"] + #[doc = ""] + #[doc = " This can be used to track cache utilization"] + pub struct PathSecretMapCleanerCycled { + #[doc = " The number of Path Secret ID entries left after the cleaning cycle"] + pub entries: usize, + #[doc = " The number of Path Secret ID entries that were retired in the cycle"] + pub retired_entries: usize, + #[doc = " The utilization percentage of the available number of entries after the cycle"] + pub entries_utilization: f32, + #[doc = " The utilization percentage of the available number of entries before the cycle"] + pub entries_initial_utilization: f32, + #[doc = " The number of SocketAddress entries left after the cleaning cycle"] + pub addresses: usize, + #[doc = " The number of SocketAddress entries that were retired in the cycle"] + pub retired_addresses: usize, + #[doc = " The utilization percentage of the available number of address entries after the cycle"] + pub addresses_utilization: f32, + #[doc = " The utilization percentage of the available number of address entries before the cycle"] + pub addresses_initial_utilization: f32, + #[doc = " The number of handshake requests that are pending after the cleaning cycle"] + pub handshake_requests: usize, + #[doc = " The number of handshake requests that were retired in the cycle"] + pub retired_handshake_requests: usize, + } + impl IntoEvent for PathSecretMapCleanerCycled { + #[inline] + fn into_event(self) -> api::PathSecretMapCleanerCycled { + let PathSecretMapCleanerCycled { + entries, + retired_entries, + entries_utilization, + entries_initial_utilization, + addresses, + retired_addresses, + addresses_utilization, + addresses_initial_utilization, + handshake_requests, + retired_handshake_requests, + } = self; + api::PathSecretMapCleanerCycled { + entries: entries.into_event(), + retired_entries: retired_entries.into_event(), + entries_utilization: entries_utilization.into_event(), + entries_initial_utilization: entries_initial_utilization.into_event(), + addresses: addresses.into_event(), + retired_addresses: retired_addresses.into_event(), + addresses_utilization: addresses_utilization.into_event(), + addresses_initial_utilization: addresses_initial_utilization.into_event(), + handshake_requests: handshake_requests.into_event(), + retired_handshake_requests: retired_handshake_requests.into_event(), + } + } + } } pub use traits::*; mod traits { @@ -3523,6 +3655,16 @@ mod traits { let _ = meta; let _ = event; } + #[doc = "Called when the `PathSecretMapCleanerCycled` event is triggered"] + #[inline] + fn on_path_secret_map_cleaner_cycled( + &self, + meta: &api::EndpointMeta, + event: &api::PathSecretMapCleanerCycled, + ) { + let _ = meta; + let _ = event; + } #[doc = r" Called for each event that relates to the endpoint and all connections"] #[inline] fn on_event(&self, meta: &M, event: &E) { @@ -3948,6 +4090,14 @@ mod traits { .on_path_secret_map_id_cache_accessed(meta, event); } #[inline] + fn on_path_secret_map_cleaner_cycled( + &self, + meta: &api::EndpointMeta, + event: &api::PathSecretMapCleanerCycled, + ) { + self.as_ref().on_path_secret_map_cleaner_cycled(meta, event); + } + #[inline] fn on_event(&self, meta: &M, event: &E) { self.as_ref().on_event(meta, event); } @@ -4402,6 +4552,15 @@ mod traits { (self.1).on_path_secret_map_id_cache_accessed(meta, event); } #[inline] + fn on_path_secret_map_cleaner_cycled( + &self, + meta: &api::EndpointMeta, + event: &api::PathSecretMapCleanerCycled, + ) { + (self.0).on_path_secret_map_cleaner_cycled(meta, event); + (self.1).on_path_secret_map_cleaner_cycled(meta, event); + } + #[inline] fn on_event(&self, meta: &M, event: &E) { self.0.on_event(meta, event); self.1.on_event(meta, event); @@ -4545,6 +4704,8 @@ mod traits { &self, event: builder::PathSecretMapIdCacheAccessed, ); + #[doc = "Publishes a `PathSecretMapCleanerCycled` event to the publisher's subscriber"] + fn on_path_secret_map_cleaner_cycled(&self, event: builder::PathSecretMapCleanerCycled); #[doc = r" Returns the QUIC version, if any"] fn quic_version(&self) -> Option; } @@ -4912,6 +5073,13 @@ mod traits { self.subscriber.on_event(&self.meta, &event); } #[inline] + fn on_path_secret_map_cleaner_cycled(&self, event: builder::PathSecretMapCleanerCycled) { + let event = event.into_event(); + self.subscriber + .on_path_secret_map_cleaner_cycled(&self.meta, &event); + self.subscriber.on_event(&self.meta, &event); + } + #[inline] fn quic_version(&self) -> Option { self.quic_version } @@ -5041,6 +5209,7 @@ pub mod testing { pub stale_key_packet_dropped: AtomicU32, pub path_secret_map_address_cache_accessed: AtomicU32, pub path_secret_map_id_cache_accessed: AtomicU32, + pub path_secret_map_cleaner_cycled: AtomicU32, } impl Drop for Subscriber { fn drop(&mut self) { @@ -5117,6 +5286,7 @@ pub mod testing { stale_key_packet_dropped: AtomicU32::new(0), path_secret_map_address_cache_accessed: AtomicU32::new(0), path_secret_map_id_cache_accessed: AtomicU32::new(0), + path_secret_map_cleaner_cycled: AtomicU32::new(0), } } } @@ -5656,6 +5826,18 @@ pub mod testing { let out = format!("{meta:?} {event:?}"); self.output.lock().unwrap().push(out); } + fn on_path_secret_map_cleaner_cycled( + &self, + meta: &api::EndpointMeta, + event: &api::PathSecretMapCleanerCycled, + ) { + self.path_secret_map_cleaner_cycled + .fetch_add(1, Ordering::Relaxed); + let meta = crate::event::snapshot::Fmt::to_snapshot(meta); + let event = crate::event::snapshot::Fmt::to_snapshot(event); + let out = format!("{meta:?} {event:?}"); + self.output.lock().unwrap().push(out); + } } } #[derive(Debug)] @@ -5709,6 +5891,7 @@ pub mod testing { pub stale_key_packet_dropped: AtomicU32, pub path_secret_map_address_cache_accessed: AtomicU32, pub path_secret_map_id_cache_accessed: AtomicU32, + pub path_secret_map_cleaner_cycled: AtomicU32, } impl Drop for Subscriber { fn drop(&mut self) { @@ -5787,6 +5970,7 @@ pub mod testing { stale_key_packet_dropped: AtomicU32::new(0), path_secret_map_address_cache_accessed: AtomicU32::new(0), path_secret_map_id_cache_accessed: AtomicU32::new(0), + path_secret_map_cleaner_cycled: AtomicU32::new(0), } } } @@ -6354,6 +6538,18 @@ pub mod testing { let out = format!("{meta:?} {event:?}"); self.output.lock().unwrap().push(out); } + fn on_path_secret_map_cleaner_cycled( + &self, + meta: &api::EndpointMeta, + event: &api::PathSecretMapCleanerCycled, + ) { + self.path_secret_map_cleaner_cycled + .fetch_add(1, Ordering::Relaxed); + let meta = crate::event::snapshot::Fmt::to_snapshot(meta); + let event = crate::event::snapshot::Fmt::to_snapshot(event); + let out = format!("{meta:?} {event:?}"); + self.output.lock().unwrap().push(out); + } } #[derive(Debug)] pub struct Publisher { @@ -6406,6 +6602,7 @@ pub mod testing { pub stale_key_packet_dropped: AtomicU32, pub path_secret_map_address_cache_accessed: AtomicU32, pub path_secret_map_id_cache_accessed: AtomicU32, + pub path_secret_map_cleaner_cycled: AtomicU32, } impl Publisher { #[doc = r" Creates a publisher with snapshot assertions enabled"] @@ -6474,6 +6671,7 @@ pub mod testing { stale_key_packet_dropped: AtomicU32::new(0), path_secret_map_address_cache_accessed: AtomicU32::new(0), path_secret_map_id_cache_accessed: AtomicU32::new(0), + path_secret_map_cleaner_cycled: AtomicU32::new(0), } } } @@ -6857,6 +7055,14 @@ pub mod testing { let out = format!("{event:?}"); self.output.lock().unwrap().push(out); } + fn on_path_secret_map_cleaner_cycled(&self, event: builder::PathSecretMapCleanerCycled) { + self.path_secret_map_cleaner_cycled + .fetch_add(1, Ordering::Relaxed); + let event = event.into_event(); + let event = crate::event::snapshot::Fmt::to_snapshot(&event); + let out = format!("{event:?}"); + self.output.lock().unwrap().push(out); + } fn quic_version(&self) -> Option { Some(1) } diff --git a/dc/s2n-quic-dc/src/event/generated/metrics/aggregate.rs b/dc/s2n-quic-dc/src/event/generated/metrics/aggregate.rs index d81ffdba8..f09451799 100644 --- a/dc/s2n-quic-dc/src/event/generated/metrics/aggregate.rs +++ b/dc/s2n-quic-dc/src/event/generated/metrics/aggregate.rs @@ -12,7 +12,7 @@ use crate::event::{ AsVariant, BoolRecorder, Info, Metric, NominalRecorder, Recorder, Registry, Units, }, }; -static INFO: &[Info; 115usize] = &[ +static INFO: &[Info; 126usize] = &[ info::Builder { id: 0usize, name: Str::new("acceptor_tcp_started\0"), @@ -703,6 +703,72 @@ static INFO: &[Info; 115usize] = &[ units: Units::None, } .build(), + info::Builder { + id: 115usize, + name: Str::new("path_secret_map_cleaner_cycled\0"), + units: Units::None, + } + .build(), + info::Builder { + id: 116usize, + name: Str::new("path_secret_map_cleaner_cycled.entries\0"), + units: Units::None, + } + .build(), + info::Builder { + id: 117usize, + name: Str::new("path_secret_map_cleaner_cycled.entries.retired\0"), + units: Units::None, + } + .build(), + info::Builder { + id: 118usize, + name: Str::new("path_secret_map_cleaner_cycled.entries.utilization\0"), + units: Units::Percent, + } + .build(), + info::Builder { + id: 119usize, + name: Str::new("path_secret_map_cleaner_cycled.entries.utilization.initial\0"), + units: Units::Percent, + } + .build(), + info::Builder { + id: 120usize, + name: Str::new("path_secret_map_cleaner_cycled.addresses\0"), + units: Units::None, + } + .build(), + info::Builder { + id: 121usize, + name: Str::new("path_secret_map_cleaner_cycled.addresses.retired\0"), + units: Units::None, + } + .build(), + info::Builder { + id: 122usize, + name: Str::new("path_secret_map_cleaner_cycled.addresses.utilization\0"), + units: Units::Percent, + } + .build(), + info::Builder { + id: 123usize, + name: Str::new("path_secret_map_cleaner_cycled.addresses.utilization.initial\0"), + units: Units::Percent, + } + .build(), + info::Builder { + id: 124usize, + name: Str::new("path_secret_map_cleaner_cycled.handshake_requests\0"), + units: Units::None, + } + .build(), + info::Builder { + id: 125usize, + name: Str::new("path_secret_map_cleaner_cycled.retired_handshake_requests\0"), + units: Units::None, + } + .build(), ]; #[derive(Clone, Copy, Debug)] #[allow(dead_code)] @@ -711,7 +777,7 @@ pub struct ConnectionContext { } pub struct Subscriber { #[allow(dead_code)] - counters: Box<[R::Counter; 49usize]>, + counters: Box<[R::Counter; 50usize]>, #[allow(dead_code)] bool_counters: Box<[R::BoolCounter; 10usize]>, #[allow(dead_code)] @@ -719,7 +785,7 @@ pub struct Subscriber { #[allow(dead_code)] nominal_counter_offsets: Box<[usize; 26usize]>, #[allow(dead_code)] - measures: Box<[R::Measure; 23usize]>, + measures: Box<[R::Measure; 33usize]>, #[allow(dead_code)] gauges: Box<[R::Gauge; 0usize]>, #[allow(dead_code)] @@ -746,11 +812,11 @@ impl Subscriber { #[allow(unused_mut)] #[inline] pub fn new(registry: R) -> Self { - let mut counters = Vec::with_capacity(49usize); + let mut counters = Vec::with_capacity(50usize); let mut bool_counters = Vec::with_capacity(10usize); let mut nominal_counters = Vec::with_capacity(26usize); let mut nominal_counter_offsets = Vec::with_capacity(26usize); - let mut measures = Vec::with_capacity(23usize); + let mut measures = Vec::with_capacity(33usize); let mut gauges = Vec::with_capacity(0usize); let mut timers = Vec::with_capacity(7usize); let mut nominal_timers = Vec::with_capacity(0usize); @@ -804,6 +870,7 @@ impl Subscriber { counters.push(registry.register_counter(&INFO[108usize])); counters.push(registry.register_counter(&INFO[110usize])); counters.push(registry.register_counter(&INFO[113usize])); + counters.push(registry.register_counter(&INFO[115usize])); bool_counters.push(registry.register_bool_counter(&INFO[19usize])); bool_counters.push(registry.register_bool_counter(&INFO[20usize])); bool_counters.push(registry.register_bool_counter(&INFO[34usize])); @@ -1127,6 +1194,16 @@ impl Subscriber { measures.push(registry.register_measure(&INFO[85usize])); measures.push(registry.register_measure(&INFO[86usize])); measures.push(registry.register_measure(&INFO[89usize])); + measures.push(registry.register_measure(&INFO[116usize])); + measures.push(registry.register_measure(&INFO[117usize])); + measures.push(registry.register_measure(&INFO[118usize])); + measures.push(registry.register_measure(&INFO[119usize])); + measures.push(registry.register_measure(&INFO[120usize])); + measures.push(registry.register_measure(&INFO[121usize])); + measures.push(registry.register_measure(&INFO[122usize])); + measures.push(registry.register_measure(&INFO[123usize])); + measures.push(registry.register_measure(&INFO[124usize])); + measures.push(registry.register_measure(&INFO[125usize])); timers.push(registry.register_timer(&INFO[5usize])); timers.push(registry.register_timer(&INFO[15usize])); timers.push(registry.register_timer(&INFO[21usize])); @@ -1217,6 +1294,7 @@ impl Subscriber { 46usize => (&INFO[108usize], entry), 47usize => (&INFO[110usize], entry), 48usize => (&INFO[113usize], entry), + 49usize => (&INFO[115usize], entry), _ => unsafe { core::hint::unreachable_unchecked() }, }) } @@ -1461,6 +1539,16 @@ impl Subscriber { 20usize => (&INFO[85usize], entry), 21usize => (&INFO[86usize], entry), 22usize => (&INFO[89usize], entry), + 23usize => (&INFO[116usize], entry), + 24usize => (&INFO[117usize], entry), + 25usize => (&INFO[118usize], entry), + 26usize => (&INFO[119usize], entry), + 27usize => (&INFO[120usize], entry), + 28usize => (&INFO[121usize], entry), + 29usize => (&INFO[122usize], entry), + 30usize => (&INFO[123usize], entry), + 31usize => (&INFO[124usize], entry), + 32usize => (&INFO[125usize], entry), _ => unsafe { core::hint::unreachable_unchecked() }, }) } @@ -2141,4 +2229,26 @@ impl event::Subscriber for Subscriber { let _ = event; let _ = meta; } + #[inline] + fn on_path_secret_map_cleaner_cycled( + &self, + meta: &api::EndpointMeta, + event: &api::PathSecretMapCleanerCycled, + ) { + #[allow(unused_imports)] + use api::*; + self.count(115usize, 49usize, 1usize); + self.measure(116usize, 23usize, event.entries); + self.measure(117usize, 24usize, event.retired_entries); + self.measure(118usize, 25usize, event.entries_utilization); + self.measure(119usize, 26usize, event.entries_initial_utilization); + self.measure(120usize, 27usize, event.addresses); + self.measure(121usize, 28usize, event.retired_addresses); + self.measure(122usize, 29usize, event.addresses_utilization); + self.measure(123usize, 30usize, event.addresses_initial_utilization); + self.measure(124usize, 31usize, event.handshake_requests); + self.measure(125usize, 32usize, event.retired_handshake_requests); + let _ = event; + let _ = meta; + } } diff --git a/dc/s2n-quic-dc/src/event/generated/metrics/probe.rs b/dc/s2n-quic-dc/src/event/generated/metrics/probe.rs index 4a2ad236e..77cd2d493 100644 --- a/dc/s2n-quic-dc/src/event/generated/metrics/probe.rs +++ b/dc/s2n-quic-dc/src/event/generated/metrics/probe.rs @@ -66,6 +66,7 @@ mod counter { 108usize => Self(stale_key_packet_dropped), 110usize => Self(path_secret_map_address_cache_accessed), 113usize => Self(path_secret_map_id_cache_accessed), + 115usize => Self(path_secret_map_cleaner_cycled), _ => unreachable!("invalid info: {info:?}"), } } @@ -175,6 +176,8 @@ mod counter { fn path_secret_map_address_cache_accessed(value: u64); # [link_name = s2n_quic_dc__event__counter__path_secret_map_id_cache_accessed] fn path_secret_map_id_cache_accessed(value: u64); + # [link_name = s2n_quic_dc__event__counter__path_secret_map_cleaner_cycled] + fn path_secret_map_cleaner_cycled(value: u64); } ); pub mod bool { @@ -473,6 +476,16 @@ mod measure { 85usize => Self(key_accepted__gap), 86usize => Self(key_accepted__forward_shift), 89usize => Self(replay_potentially_detected__gap), + 116usize => Self(path_secret_map_cleaner_cycled__entries), + 117usize => Self(path_secret_map_cleaner_cycled__entries__retired), + 118usize => Self(path_secret_map_cleaner_cycled__entries__utilization), + 119usize => Self(path_secret_map_cleaner_cycled__entries__utilization__initial), + 120usize => Self(path_secret_map_cleaner_cycled__addresses), + 121usize => Self(path_secret_map_cleaner_cycled__addresses__retired), + 122usize => Self(path_secret_map_cleaner_cycled__addresses__utilization), + 123usize => Self(path_secret_map_cleaner_cycled__addresses__utilization__initial), + 124usize => Self(path_secret_map_cleaner_cycled__handshake_requests), + 125usize => Self(path_secret_map_cleaner_cycled__retired_handshake_requests), _ => unreachable!("invalid info: {info:?}"), } } @@ -530,6 +543,26 @@ mod measure { fn key_accepted__forward_shift(value: u64); # [link_name = s2n_quic_dc__event__measure__replay_potentially_detected__gap] fn replay_potentially_detected__gap(value: u64); + # [link_name = s2n_quic_dc__event__measure__path_secret_map_cleaner_cycled__entries] + fn path_secret_map_cleaner_cycled__entries(value: u64); + # [link_name = s2n_quic_dc__event__measure__path_secret_map_cleaner_cycled__entries__retired] + fn path_secret_map_cleaner_cycled__entries__retired(value: u64); + # [link_name = s2n_quic_dc__event__measure__path_secret_map_cleaner_cycled__entries__utilization] + fn path_secret_map_cleaner_cycled__entries__utilization(value: u64); + # [link_name = s2n_quic_dc__event__measure__path_secret_map_cleaner_cycled__entries__utilization__initial] + fn path_secret_map_cleaner_cycled__entries__utilization__initial(value: u64); + # [link_name = s2n_quic_dc__event__measure__path_secret_map_cleaner_cycled__addresses] + fn path_secret_map_cleaner_cycled__addresses(value: u64); + # [link_name = s2n_quic_dc__event__measure__path_secret_map_cleaner_cycled__addresses__retired] + fn path_secret_map_cleaner_cycled__addresses__retired(value: u64); + # [link_name = s2n_quic_dc__event__measure__path_secret_map_cleaner_cycled__addresses__utilization] + fn path_secret_map_cleaner_cycled__addresses__utilization(value: u64); + # [link_name = s2n_quic_dc__event__measure__path_secret_map_cleaner_cycled__addresses__utilization__initial] + fn path_secret_map_cleaner_cycled__addresses__utilization__initial(value: u64); + # [link_name = s2n_quic_dc__event__measure__path_secret_map_cleaner_cycled__handshake_requests] + fn path_secret_map_cleaner_cycled__handshake_requests(value: u64); + # [link_name = s2n_quic_dc__event__measure__path_secret_map_cleaner_cycled__retired_handshake_requests] + fn path_secret_map_cleaner_cycled__retired_handshake_requests(value: u64); } ); } diff --git a/dc/s2n-quic-dc/src/fixed_map.rs b/dc/s2n-quic-dc/src/fixed_map.rs index 845b94b26..b986303e7 100644 --- a/dc/s2n-quic-dc/src/fixed_map.rs +++ b/dc/s2n-quic-dc/src/fixed_map.rs @@ -47,7 +47,7 @@ where } } - pub fn len(&self) -> usize { + pub fn count(&self) -> usize { self.slots.iter().map(|s| s.len()).sum() } diff --git a/dc/s2n-quic-dc/src/fixed_map/test.rs b/dc/s2n-quic-dc/src/fixed_map/test.rs index 726869137..771fa61ac 100644 --- a/dc/s2n-quic-dc/src/fixed_map/test.rs +++ b/dc/s2n-quic-dc/src/fixed_map/test.rs @@ -38,5 +38,5 @@ fn capacity_size() { for idx in 0..500_000 { map.insert(idx, ()); } - assert!(map.len() >= 400_000, "{}", map.len()); + assert!(map.count() >= 400_000, "{}", map.count()); } diff --git a/dc/s2n-quic-dc/src/path/secret/map/cleaner.rs b/dc/s2n-quic-dc/src/path/secret/map/cleaner.rs index c23218fdd..015b9c04a 100644 --- a/dc/s2n-quic-dc/src/path/secret/map/cleaner.rs +++ b/dc/s2n-quic-dc/src/path/secret/map/cleaner.rs @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use super::state::State; -use crate::event; +use crate::{ + event::{self, EndpointPublisher as _}, + path::secret::map::store::Store, +}; use rand::Rng as _; use s2n_quic_core::time; use std::{ @@ -68,7 +71,12 @@ impl Cleaner { break; } state.cleaner().clean(&state, EVICTION_CYCLES); - let pause = rand::thread_rng().gen_range(5..60); + // in tests, we should try and be deterministic + let pause = if cfg!(test) { + 60 + } else { + rand::thread_rng().gen_range(5..60) + }; drop(state); std::thread::park_timeout(Duration::from_secs(pause)); }) @@ -85,11 +93,20 @@ impl Cleaner { let current_epoch = self.epoch.fetch_add(1, Ordering::Relaxed); let now = Instant::now(); + let utilization = |count: usize| (count as f32 / state.secrets_capacity() as f32) * 100.0; + + let mut initial_ids_count = 0usize; + let mut initial_addresses_count = 0usize; + let mut retired_ids_count = 0usize; + let mut retired_addresses_count = 0usize; + // For non-retired entries, if it's time for them to handshake again, request a // handshake to happen. This handshake will currently happen on the next request for this // particular peer. state.ids.retain(|_, entry| { - if let Some(retired_at) = entry.retired_at() { + initial_ids_count += 1; + + let retained = if let Some(retired_at) = entry.retired_at() { // retain if we aren't yet ready to evict. current_epoch.saturating_sub(retired_at) < eviction_cycles } else { @@ -99,15 +116,29 @@ impl Cleaner { // always retain true + }; + + if !retained { + retired_ids_count += 1; } + + retained }); // Drop IP entries if we no longer have the path secret ID entry. // FIXME: Don't require a loop to do this. This is likely somewhat slow since it takes a // write lock + read lock essentially per-entry, but should be near-constant-time. - state - .peers - .retain(|_, entry| state.ids.contains_key(entry.id())); + state.peers.retain(|_, entry| { + initial_addresses_count += 1; + + let retained = state.ids.contains_key(entry.id()); + + if !retained { + retired_addresses_count += 1; + } + + retained + }); // Iteration order should be effectively random, so this effectively just prunes the list // periodically. 5000 is chosen arbitrarily to make sure this isn't a memory leak. Note @@ -116,11 +147,38 @@ impl Cleaner { // // FIXME: Long or mid-term it likely makes sense to replace this data structure with a // fuzzy set of some kind and/or just moving to immediate background handshake attempts. - let mut count = 0; + const MAX_REQUESTED_HANDSHAKES: usize = 5000; + + let mut handshake_requests_count = 0usize; + let mut retired_handshake_requests_count = 0usize; state.requested_handshakes.pin().retain(|_| { - count += 1; - count < 5000 + handshake_requests_count += 1; + let retain = handshake_requests_count < MAX_REQUESTED_HANDSHAKES; + + if !retain { + retired_handshake_requests_count += 1; + } + + retain }); + + let entries = initial_ids_count - retired_ids_count; + let addresses = initial_addresses_count - retired_addresses_count; + + state.subscriber().on_path_secret_map_cleaner_cycled( + event::builder::PathSecretMapCleanerCycled { + entries, + retired_entries: retired_ids_count, + entries_utilization: utilization(entries), + entries_initial_utilization: utilization(initial_ids_count), + addresses, + addresses_utilization: utilization(addresses), + addresses_initial_utilization: utilization(initial_addresses_count), + retired_addresses: retired_addresses_count, + handshake_requests: handshake_requests_count, + retired_handshake_requests: retired_handshake_requests_count, + }, + ); } pub fn epoch(&self) -> u64 { diff --git a/dc/s2n-quic-dc/src/path/secret/map/event_tests.rs b/dc/s2n-quic-dc/src/path/secret/map/event_tests.rs index 1f0f76f03..378486278 100644 --- a/dc/s2n-quic-dc/src/path/secret/map/event_tests.rs +++ b/dc/s2n-quic-dc/src/path/secret/map/event_tests.rs @@ -33,7 +33,12 @@ fn map(capacity: usize) -> Map { fn map_sub(capacity: usize, sub: Arc) -> Map { let signer = stateless_reset::Signer::random(); - Map::new(signer, capacity, NoopClock, sub) + let map = Map::new(signer, capacity, NoopClock, sub); + + // sleep so the cleaner has time to emit events + std::thread::sleep(core::time::Duration::from_millis(100)); + + map } #[test] diff --git a/dc/s2n-quic-dc/src/path/secret/map/snapshots/path__secret__map__event_tests__control_packets__events.snap b/dc/s2n-quic-dc/src/path/secret/map/snapshots/path__secret__map__event_tests__control_packets__events.snap index 047490ebf..5e5698d0e 100644 --- a/dc/s2n-quic-dc/src/path/secret/map/snapshots/path__secret__map__event_tests__control_packets__events.snap +++ b/dc/s2n-quic-dc/src/path/secret/map/snapshots/path__secret__map__event_tests__control_packets__events.snap @@ -3,7 +3,9 @@ source: quic/s2n-quic-core/src/event/snapshot.rs input_file: dc/s2n-quic-dc/src/path/secret/map/event_tests.rs --- EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapInitialized { capacity: 10 } +EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapCleanerCycled { entries: 0, retired_entries: 0, entries_utilization: 0.0, entries_initial_utilization: 0.0, addresses: 0, retired_addresses: 0, addresses_utilization: 0.0, addresses_initial_utilization: 0.0, handshake_requests: 0, retired_handshake_requests: 0 } EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapInitialized { capacity: 10 } +EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapCleanerCycled { entries: 0, retired_entries: 0, entries_utilization: 0.0, entries_initial_utilization: 0.0, addresses: 0, retired_addresses: 0, addresses_utilization: 0.0, addresses_initial_utilization: 0.0, handshake_requests: 0, retired_handshake_requests: 0 } EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapEntryInserted { peer_address: 127.0.0.1:5678, credential_id: "[HIDDEN]" } EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapEntryReady { peer_address: 127.0.0.1:5678, credential_id: "[HIDDEN]" } EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapEntryInserted { peer_address: 127.0.0.1:1234, credential_id: "[HIDDEN]" } diff --git a/dc/s2n-quic-dc/src/path/secret/map/snapshots/path__secret__map__event_tests__init_uninit__events.snap b/dc/s2n-quic-dc/src/path/secret/map/snapshots/path__secret__map__event_tests__init_uninit__events.snap index 6e18dcd39..44d558746 100644 --- a/dc/s2n-quic-dc/src/path/secret/map/snapshots/path__secret__map__event_tests__init_uninit__events.snap +++ b/dc/s2n-quic-dc/src/path/secret/map/snapshots/path__secret__map__event_tests__init_uninit__events.snap @@ -3,4 +3,5 @@ source: quic/s2n-quic-core/src/event/snapshot.rs input_file: dc/s2n-quic-dc/src/path/secret/map/event_tests.rs --- EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapInitialized { capacity: 10 } +EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapCleanerCycled { entries: 0, retired_entries: 0, entries_utilization: 0.0, entries_initial_utilization: 0.0, addresses: 0, retired_addresses: 0, addresses_utilization: 0.0, addresses_initial_utilization: 0.0, handshake_requests: 0, retired_handshake_requests: 0 } EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapUninitialized { capacity: 10, entries: 0, lifetime: 1µs } diff --git a/dc/s2n-quic-dc/src/path/secret/map/snapshots/path__secret__map__event_tests__insert_one__events.snap b/dc/s2n-quic-dc/src/path/secret/map/snapshots/path__secret__map__event_tests__insert_one__events.snap index a1fb28a3c..08ac44a6c 100644 --- a/dc/s2n-quic-dc/src/path/secret/map/snapshots/path__secret__map__event_tests__insert_one__events.snap +++ b/dc/s2n-quic-dc/src/path/secret/map/snapshots/path__secret__map__event_tests__insert_one__events.snap @@ -3,6 +3,7 @@ source: quic/s2n-quic-core/src/event/snapshot.rs input_file: dc/s2n-quic-dc/src/path/secret/map/event_tests.rs --- EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapInitialized { capacity: 10 } +EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapCleanerCycled { entries: 0, retired_entries: 0, entries_utilization: 0.0, entries_initial_utilization: 0.0, addresses: 0, retired_addresses: 0, addresses_utilization: 0.0, addresses_initial_utilization: 0.0, handshake_requests: 0, retired_handshake_requests: 0 } EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapEntryInserted { peer_address: 127.0.0.1:4567, credential_id: "[HIDDEN]" } EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapEntryReady { peer_address: 127.0.0.1:4567, credential_id: "[HIDDEN]" } EndpointMeta { timestamp: Timestamp(0:00:00.000001) } PathSecretMapUninitialized { capacity: 10, entries: 1, lifetime: 1µs } diff --git a/dc/s2n-quic-dc/src/path/secret/map/state.rs b/dc/s2n-quic-dc/src/path/secret/map/state.rs index 92b30091a..9041a16a1 100644 --- a/dc/s2n-quic-dc/src/path/secret/map/state.rs +++ b/dc/s2n-quic-dc/src/path/secret/map/state.rs @@ -320,7 +320,7 @@ where self.ids = fixed_map::Map::with_capacity(new, Default::default()); } - fn subscriber(&self) -> event::EndpointPublisherSubscriber { + pub(super) fn subscriber(&self) -> event::EndpointPublisherSubscriber { use event::IntoEvent as _; let timestamp = self.clock.get_time().into_event(); @@ -339,11 +339,11 @@ where S: event::Subscriber, { fn secrets_len(&self) -> usize { - self.ids.len() + self.ids.count() } fn peers_len(&self) -> usize { - self.peers.len() + self.peers.count() } fn secrets_capacity(&self) -> usize {