1use super::*;
2
3#[derive(Debug)]
5pub struct ExternalPacketIo {
6 pub outbound_tx: crate::upper::tun::TunOutboundTx,
8 pub inbound_rx: tokio::sync::mpsc::Receiver<NodeDeliveredPacket>,
10}
11
12#[derive(Debug)]
14pub(crate) struct EndpointDataIo {
15 pub(crate) priority_command_tx: tokio::sync::mpsc::Sender<NodeEndpointCommand>,
18 pub(crate) command_tx: tokio::sync::mpsc::Sender<NodeEndpointCommand>,
27 pub(crate) event_rx: EndpointEventReceiver,
36 pub(crate) event_tx: EndpointEventSender,
42}
43
44#[derive(Debug, Clone)]
46pub(crate) struct EndpointEventSender {
47 priority: tokio::sync::mpsc::UnboundedSender<NodeEndpointEvent>,
48 bulk: tokio::sync::mpsc::Sender<NodeEndpointEvent>,
49 queued_messages: Arc<AtomicUsize>,
50 bulk_queued_messages: Arc<AtomicUsize>,
51 ready: Arc<EndpointEventReady>,
52 bulk_message_cap: usize,
53}
54
55#[derive(Debug)]
56pub(crate) struct EndpointEventReceiver {
57 priority: tokio::sync::mpsc::UnboundedReceiver<NodeEndpointEvent>,
58 bulk: tokio::sync::mpsc::Receiver<NodeEndpointEvent>,
59 queued_messages: Arc<AtomicUsize>,
60 bulk_queued_messages: Arc<AtomicUsize>,
61 ready: Arc<EndpointEventReady>,
62 priority_closed: bool,
63 bulk_closed: bool,
64}
65
66#[derive(Debug, Default)]
67struct EndpointEventReady {
68 sequence: StdMutex<u64>,
69 changed: Condvar,
70}
71
72impl EndpointEventReady {
73 fn notify(&self) {
74 if let Ok(mut sequence) = self.sequence.lock() {
75 *sequence = sequence.wrapping_add(1);
76 self.changed.notify_one();
77 }
78 }
79
80 fn snapshot(&self) -> u64 {
81 self.sequence.lock().map(|sequence| *sequence).unwrap_or(0)
82 }
83
84 fn wait_for_change(&self, observed: &mut u64) {
85 let Ok(mut sequence) = self.sequence.lock() else {
86 return;
87 };
88 while *sequence == *observed {
89 match self.changed.wait(sequence) {
90 Ok(next) => sequence = next,
91 Err(_) => return,
92 }
93 }
94 *observed = *sequence;
95 }
96}
97
98#[derive(Clone, Copy)]
99enum EndpointEventLane {
100 Priority,
101 Bulk,
102}
103
104fn endpoint_event_lane_for_len(len: usize) -> EndpointEventLane {
105 if len <= ENDPOINT_EVENT_PRIORITY_MAX_LEN {
106 EndpointEventLane::Priority
107 } else {
108 EndpointEventLane::Bulk
109 }
110}
111
112fn endpoint_event_bulk_capacity(requested: usize) -> usize {
113 requested.max(1)
114}
115
116fn try_reserve_endpoint_event_bulk_messages(
117 counter: &AtomicUsize,
118 capacity: usize,
119 count: usize,
120) -> bool {
121 if count == 0 {
122 return true;
123 }
124
125 counter
126 .fetch_update(Relaxed, Relaxed, |current| {
127 current.checked_add(count).filter(|next| *next <= capacity)
128 })
129 .is_ok()
130}
131
132#[derive(Debug, Default)]
139pub(in crate::node) struct EndpointEventRuntime {
140 sender: Option<EndpointEventSender>,
141 batch_depth: usize,
142 batch: Vec<EndpointDataDelivery>,
143}
144
145impl EndpointEventSender {
146 pub(in crate::node) fn channel(capacity: usize) -> (Self, EndpointEventReceiver) {
147 let (priority_tx, priority_rx) = tokio::sync::mpsc::unbounded_channel();
148 let bulk_message_cap = endpoint_event_bulk_capacity(capacity);
149 let (bulk_tx, bulk_rx) = tokio::sync::mpsc::channel(bulk_message_cap);
150 let queued_messages = Arc::new(AtomicUsize::new(0));
151 let bulk_queued_messages = Arc::new(AtomicUsize::new(0));
152 let ready = Arc::new(EndpointEventReady::default());
153 (
154 Self {
155 priority: priority_tx,
156 bulk: bulk_tx,
157 queued_messages: Arc::clone(&queued_messages),
158 bulk_queued_messages: Arc::clone(&bulk_queued_messages),
159 ready: Arc::clone(&ready),
160 bulk_message_cap,
161 },
162 EndpointEventReceiver {
163 priority: priority_rx,
164 bulk: bulk_rx,
165 queued_messages,
166 bulk_queued_messages,
167 ready,
168 priority_closed: false,
169 bulk_closed: false,
170 },
171 )
172 }
173
174 pub(in crate::node) fn same_channels(&self, other: &Self) -> bool {
175 self.priority.same_channel(&other.priority)
176 && self.bulk.same_channel(&other.bulk)
177 && Arc::ptr_eq(&self.queued_messages, &other.queued_messages)
178 && Arc::ptr_eq(&self.bulk_queued_messages, &other.bulk_queued_messages)
179 && Arc::ptr_eq(&self.ready, &other.ready)
180 && self.bulk_message_cap == other.bulk_message_cap
181 }
182
183 #[allow(clippy::result_large_err)]
184 pub(crate) fn send(
185 &self,
186 event: NodeEndpointEvent,
187 ) -> Result<(), tokio::sync::mpsc::error::SendError<NodeEndpointEvent>> {
188 match event {
189 NodeEndpointEvent::Data {
190 source_peer,
191 payload,
192 queued_at,
193 } => {
194 let lane = endpoint_event_lane_for_len(payload.len());
195 self.send_to_lane(
196 NodeEndpointEvent::Data {
197 source_peer,
198 payload,
199 queued_at,
200 },
201 lane,
202 )
203 }
204 NodeEndpointEvent::DataBatch {
205 messages,
206 queued_at,
207 } => self.send_data_batch(messages, queued_at),
208 }
209 }
210
211 #[allow(clippy::result_large_err)]
212 fn send_data_batch(
213 &self,
214 messages: Vec<EndpointDataDelivery>,
215 queued_at: Option<crate::perf_profile::TraceStamp>,
216 ) -> Result<(), tokio::sync::mpsc::error::SendError<NodeEndpointEvent>> {
217 if messages.is_empty() {
218 return Ok(());
219 }
220
221 let message_count = messages.len();
222 let priority_count = messages
223 .iter()
224 .filter(|message| message.is_priority_sized())
225 .count();
226 if priority_count == 0 || priority_count == message_count {
227 let lane = if priority_count == 0 {
228 EndpointEventLane::Bulk
229 } else {
230 EndpointEventLane::Priority
231 };
232 let event = NodeEndpointEvent::from_delivery_messages(messages, queued_at)
233 .expect("non-empty endpoint event batch should produce event");
234 return self.send_to_lane(event, lane);
235 }
236
237 let mut priority_messages = Vec::with_capacity(priority_count);
238 let mut bulk_messages = Vec::with_capacity(message_count - priority_count);
239 for message in messages {
240 if message.is_priority_sized() {
241 priority_messages.push(message);
242 } else {
243 bulk_messages.push(message);
244 }
245 }
246
247 if let Some(event) = NodeEndpointEvent::from_delivery_messages(priority_messages, queued_at)
248 {
249 self.send_to_lane(event, EndpointEventLane::Priority)?;
250 }
251 if let Some(event) = NodeEndpointEvent::from_delivery_messages(bulk_messages, queued_at) {
252 self.send_to_lane(event, EndpointEventLane::Bulk)?;
253 }
254 Ok(())
255 }
256
257 #[allow(clippy::result_large_err)]
258 fn send_to_lane(
259 &self,
260 event: NodeEndpointEvent,
261 lane: EndpointEventLane,
262 ) -> Result<(), tokio::sync::mpsc::error::SendError<NodeEndpointEvent>> {
263 let count = event.message_count();
264 let bulk_reserved = if matches!(lane, EndpointEventLane::Bulk) {
265 try_reserve_endpoint_event_bulk_messages(
266 &self.bulk_queued_messages,
267 self.bulk_message_cap,
268 count,
269 )
270 } else {
271 false
272 };
273 if matches!(lane, EndpointEventLane::Bulk) && !bulk_reserved {
274 crate::perf_profile::record_event_count(
275 crate::perf_profile::Event::EndpointEventBulkDropped,
276 count as u64,
277 );
278 return Ok(());
279 }
280
281 let previous = self.queued_messages.fetch_add(count, Relaxed);
282 let queued = previous.saturating_add(count);
283 match lane {
284 EndpointEventLane::Priority => match self.priority.send(event) {
285 Ok(()) => {
286 self.note_send_success(previous, queued);
287 Ok(())
288 }
289 Err(error) => {
290 self.note_send_rejected(count);
291 Err(error)
292 }
293 },
294 EndpointEventLane::Bulk => match self.bulk.try_send(event) {
295 Ok(()) => {
296 self.note_send_success(previous, queued);
297 Ok(())
298 }
299 Err(tokio::sync::mpsc::error::TrySendError::Full(_event)) => {
300 self.note_bulk_send_rejected(count);
301 crate::perf_profile::record_event_count(
302 crate::perf_profile::Event::EndpointEventBulkDropped,
303 count as u64,
304 );
305 Ok(())
306 }
307 Err(tokio::sync::mpsc::error::TrySendError::Closed(event)) => {
308 self.note_bulk_send_rejected(count);
309 Err(tokio::sync::mpsc::error::SendError(event))
310 }
311 },
312 }
313 }
314
315 fn note_send_success(&self, previous: usize, queued: usize) {
316 if previous < ENDPOINT_EVENT_BACKLOG_HIGH_WATER
317 && queued >= ENDPOINT_EVENT_BACKLOG_HIGH_WATER
318 {
319 crate::perf_profile::record_event(crate::perf_profile::Event::EndpointEventBacklogHigh);
320 }
321 self.ready.notify();
322 }
323
324 fn note_send_rejected(&self, count: usize) {
325 release_endpoint_event_messages(&self.queued_messages, count);
326 self.ready.notify();
327 }
328
329 fn note_bulk_send_rejected(&self, count: usize) {
330 release_endpoint_event_messages(&self.queued_messages, count);
331 release_endpoint_event_messages(&self.bulk_queued_messages, count);
332 self.ready.notify();
333 }
334
335 #[cfg(test)]
336 pub(crate) fn queued_messages(&self) -> usize {
337 self.queued_messages.load(Relaxed)
338 }
339
340 #[cfg(test)]
341 pub(crate) fn bulk_queued_messages(&self) -> usize {
342 self.bulk_queued_messages.load(Relaxed)
343 }
344}
345
346impl Drop for EndpointEventSender {
347 fn drop(&mut self) {
348 self.ready.notify();
349 }
350}
351
352impl EndpointEventRuntime {
353 pub(in crate::node) fn attach(&mut self, sender: EndpointEventSender) {
354 self.sender = Some(sender);
355 self.batch_depth = 0;
356 self.batch.clear();
357 }
358
359 pub(in crate::node) fn is_attached(&self) -> bool {
360 self.sender.is_some()
361 }
362
363 pub(in crate::node) fn sender(&self) -> Option<EndpointEventSender> {
364 self.sender.clone()
365 }
366
367 pub(in crate::node) fn begin_batch(&mut self) {
368 if self.is_attached() {
369 self.batch_depth = self.batch_depth.saturating_add(1);
370 }
371 }
372
373 pub(in crate::node) fn finish_batch(&mut self) {
374 if self.batch_depth == 0 {
375 return;
376 }
377 self.batch_depth -= 1;
378 if self.batch_depth == 0 {
379 self.flush_batch();
380 }
381 }
382
383 #[allow(clippy::result_large_err)]
384 pub(in crate::node) fn deliver_endpoint_data(
385 &mut self,
386 message: EndpointDataDelivery,
387 ) -> Result<(), tokio::sync::mpsc::error::SendError<NodeEndpointEvent>> {
388 if self.batch_depth > 0 {
389 self.batch.push(message);
390 return Ok(());
391 }
392
393 self.send(NodeEndpointEvent::Data {
394 source_peer: message.source_peer,
395 payload: message.payload,
396 queued_at: crate::perf_profile::stamp(),
397 })
398 }
399
400 fn flush_batch(&mut self) {
401 let count = self.batch.len();
402 if count == 0 {
403 return;
404 }
405
406 let queued_at = crate::perf_profile::stamp();
407 let event = if count == 1 {
408 let message = self.batch.pop().expect("batch should contain message");
409 NodeEndpointEvent::Data {
410 source_peer: message.source_peer,
411 payload: message.payload,
412 queued_at,
413 }
414 } else {
415 NodeEndpointEvent::DataBatch {
416 messages: std::mem::take(&mut self.batch),
417 queued_at,
418 }
419 };
420
421 if let Err(error) = self.send(event) {
422 debug!(
423 error = %error,
424 messages = count,
425 "Failed to deliver endpoint data event batch"
426 );
427 }
428 }
429
430 #[allow(clippy::result_large_err)]
431 fn send(
432 &self,
433 event: NodeEndpointEvent,
434 ) -> Result<(), tokio::sync::mpsc::error::SendError<NodeEndpointEvent>> {
435 let Some(sender) = &self.sender else {
436 return Ok(());
437 };
438 let _t_deliver =
439 crate::perf_profile::Timer::start(crate::perf_profile::Stage::EndpointDeliver);
440 sender.send(event)
441 }
442}
443
444impl EndpointEventReceiver {
445 pub(crate) async fn recv(&mut self) -> Option<NodeEndpointEvent> {
446 loop {
447 match self.try_recv() {
448 Ok(event) => return Some(event),
449 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => return None,
450 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {}
451 }
452
453 tokio::select! {
454 biased;
455 event = self.priority.recv(), if !self.priority_closed => {
456 match event {
457 Some(event) => {
458 self.note_dequeued(&event);
459 return Some(event);
460 }
461 None => self.priority_closed = true,
462 }
463 }
464 event = self.bulk.recv(), if !self.bulk_closed => {
465 match event {
466 Some(event) => {
467 self.note_dequeued(&event);
468 return Some(event);
469 }
470 None => self.bulk_closed = true,
471 }
472 }
473 }
474 }
475 }
476
477 pub(crate) fn blocking_recv(&mut self) -> Option<NodeEndpointEvent> {
478 let mut observed = self.ready.snapshot();
479 loop {
480 match self.try_recv() {
481 Ok(event) => return Some(event),
482 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => return None,
483 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
484 self.ready.wait_for_change(&mut observed);
485 }
486 }
487 }
488 }
489
490 pub(crate) fn try_recv(
491 &mut self,
492 ) -> Result<NodeEndpointEvent, tokio::sync::mpsc::error::TryRecvError> {
493 match self.try_recv_priority() {
494 Ok(event) => return Ok(event),
495 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {}
496 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {}
497 }
498
499 match self.bulk.try_recv() {
500 Ok(event) => {
501 self.note_dequeued(&event);
502 Ok(event)
503 }
504 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
505 if self.priority_closed && self.bulk_closed {
506 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected)
507 } else {
508 Err(tokio::sync::mpsc::error::TryRecvError::Empty)
509 }
510 }
511 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
512 self.bulk_closed = true;
513 if self.priority_closed {
514 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected)
515 } else {
516 Err(tokio::sync::mpsc::error::TryRecvError::Empty)
517 }
518 }
519 }
520 }
521
522 pub(crate) fn try_recv_priority(
523 &mut self,
524 ) -> Result<NodeEndpointEvent, tokio::sync::mpsc::error::TryRecvError> {
525 let event = match self.priority.try_recv() {
526 Ok(event) => event,
527 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
528 return Err(tokio::sync::mpsc::error::TryRecvError::Empty);
529 }
530 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
531 self.priority_closed = true;
532 return Err(tokio::sync::mpsc::error::TryRecvError::Disconnected);
533 }
534 };
535 self.note_dequeued(&event);
536 Ok(event)
537 }
538
539 fn note_dequeued(&self, event: &NodeEndpointEvent) {
540 event.record_dequeue_wait();
541 let counts = event.dequeue_counts();
542 release_endpoint_event_messages(&self.queued_messages, counts.total);
543 release_endpoint_event_messages(&self.bulk_queued_messages, counts.bulk);
544 }
545}
546
547pub(in crate::node) fn release_endpoint_event_messages(counter: &AtomicUsize, count: usize) {
548 if count == 0 {
549 return;
550 }
551
552 let previous = counter.fetch_sub(count, Relaxed);
553 debug_assert!(
554 previous >= count,
555 "endpoint event queued message accounting underflow"
556 );
557}
558
559pub(crate) fn endpoint_data_command_capacity(requested: usize) -> usize {
560 if let Ok(raw) = std::env::var("FIPS_ENDPOINT_DATA_QUEUE_CAP")
561 && let Ok(value) = raw.trim().parse::<usize>()
562 && value > 0
563 {
564 return value;
565 }
566
567 requested.max(1).max(32_768)
568}
569
570#[derive(Debug)]
572pub(crate) enum NodeEndpointCommand {
573 Send {
577 command: EndpointSendCommand,
578 response_tx: tokio::sync::oneshot::Sender<Result<(), NodeError>>,
579 },
580 SendOneway { command: EndpointSendCommand },
586 SendBatchOneway {
591 command: EndpointSendBatchCommand,
592 lane: EndpointCommandLane,
593 },
594 PeerSnapshot {
595 response_tx: tokio::sync::oneshot::Sender<Vec<NodeEndpointPeer>>,
596 },
597 RelaySnapshot {
598 response_tx: tokio::sync::oneshot::Sender<Vec<NodeEndpointRelayStatus>>,
599 },
600 UpdateRelays {
601 advert_relays: Vec<String>,
602 dm_relays: Vec<String>,
603 response_tx: tokio::sync::oneshot::Sender<Result<(), NodeError>>,
604 },
605 UpdatePeers {
611 peers: Vec<crate::config::PeerConfig>,
612 response_tx: tokio::sync::oneshot::Sender<Result<UpdatePeersOutcome, NodeError>>,
613 },
614}
615
616#[derive(Debug)]
619pub(crate) struct EndpointSendCommand {
620 send: EndpointDataSend,
621 queued_at: Option<crate::perf_profile::TraceStamp>,
622}
623
624impl EndpointSendCommand {
625 pub(crate) fn new(
626 remote: PeerIdentity,
627 payload: Vec<u8>,
628 queued_at: Option<crate::perf_profile::TraceStamp>,
629 ) -> Self {
630 Self {
631 send: EndpointDataSend::new(remote, EndpointDataPayload::new(payload)),
632 queued_at,
633 }
634 }
635
636 pub(crate) fn lane(&self) -> EndpointCommandLane {
637 self.send.payload().lane()
638 }
639
640 pub(crate) fn drop_on_backpressure(&self) -> bool {
641 self.send.payload().drop_on_backpressure()
642 }
643
644 pub(crate) fn set_queued_at(&mut self, queued_at: Option<crate::perf_profile::TraceStamp>) {
645 self.queued_at = queued_at;
646 }
647
648 pub(crate) fn into_parts(self) -> (EndpointDataSend, Option<crate::perf_profile::TraceStamp>) {
649 (self.send, self.queued_at)
650 }
651}
652
653#[derive(Debug)]
655pub(crate) struct EndpointSendBatchCommand {
656 remote: PeerIdentity,
657 payloads: Vec<EndpointDataPayload>,
658 queued_at: Option<crate::perf_profile::TraceStamp>,
659}
660
661impl EndpointSendBatchCommand {
662 pub(crate) fn new(
663 remote: PeerIdentity,
664 payloads: Vec<EndpointDataPayload>,
665 queued_at: Option<crate::perf_profile::TraceStamp>,
666 ) -> Option<Self> {
667 if payloads.is_empty() {
668 return None;
669 }
670 Some(Self {
671 remote,
672 payloads,
673 queued_at,
674 })
675 }
676
677 pub(crate) fn lane(&self) -> EndpointCommandLane {
678 self.payloads[0].lane()
679 }
680
681 pub(crate) fn len(&self) -> usize {
682 self.payloads.len()
683 }
684
685 pub(crate) fn drop_on_backpressure(&self) -> bool {
686 self.payloads
687 .iter()
688 .all(EndpointDataPayload::drop_on_backpressure)
689 }
690
691 pub(crate) fn set_queued_at(&mut self, queued_at: Option<crate::perf_profile::TraceStamp>) {
692 self.queued_at = queued_at;
693 }
694
695 pub(crate) fn into_parts(
696 self,
697 ) -> (
698 PeerIdentity,
699 Vec<EndpointDataPayload>,
700 Option<crate::perf_profile::TraceStamp>,
701 ) {
702 (self.remote, self.payloads, self.queued_at)
703 }
704}
705
706impl NodeEndpointCommand {
707 pub(crate) fn send(
708 remote: PeerIdentity,
709 payload: Vec<u8>,
710 queued_at: Option<crate::perf_profile::TraceStamp>,
711 response_tx: tokio::sync::oneshot::Sender<Result<(), NodeError>>,
712 ) -> Self {
713 Self::Send {
714 command: EndpointSendCommand::new(remote, payload, queued_at),
715 response_tx,
716 }
717 }
718
719 pub(crate) fn send_oneway(
720 remote: PeerIdentity,
721 payload: Vec<u8>,
722 queued_at: Option<crate::perf_profile::TraceStamp>,
723 ) -> Self {
724 Self::SendOneway {
725 command: EndpointSendCommand::new(remote, payload, queued_at),
726 }
727 }
728
729 pub(crate) fn send_batch_oneway(
730 remote: PeerIdentity,
731 payloads: Vec<EndpointDataPayload>,
732 queued_at: Option<crate::perf_profile::TraceStamp>,
733 lane: EndpointCommandLane,
734 ) -> Option<Self> {
735 debug_assert!(payloads.iter().all(|payload| payload.lane() == lane));
736 let command = EndpointSendBatchCommand::new(remote, payloads, queued_at)?;
737 debug_assert_eq!(command.lane(), lane);
738 Some(Self::SendBatchOneway { command, lane })
739 }
740
741 pub(crate) fn lane(&self) -> EndpointCommandLane {
742 match self {
743 Self::Send { command, .. } | Self::SendOneway { command } => command.lane(),
744 Self::SendBatchOneway { lane, .. } => *lane,
745 Self::PeerSnapshot { .. }
746 | Self::RelaySnapshot { .. }
747 | Self::UpdateRelays { .. }
748 | Self::UpdatePeers { .. } => EndpointCommandLane::Priority,
749 }
750 }
751
752 pub(crate) fn drop_on_backpressure(&self) -> bool {
753 match self {
754 Self::SendOneway { command } => {
755 command.lane() == EndpointCommandLane::Bulk && command.drop_on_backpressure()
756 }
757 Self::SendBatchOneway { command, lane } => {
758 *lane == EndpointCommandLane::Bulk && command.drop_on_backpressure()
759 }
760 Self::Send { .. }
761 | Self::PeerSnapshot { .. }
762 | Self::RelaySnapshot { .. }
763 | Self::UpdateRelays { .. }
764 | Self::UpdatePeers { .. } => false,
765 }
766 }
767
768 pub(crate) fn drain_cost(&self) -> usize {
769 match self {
770 Self::SendBatchOneway { command, .. } => command.len().max(1),
771 Self::Send { .. }
772 | Self::SendOneway { .. }
773 | Self::PeerSnapshot { .. }
774 | Self::RelaySnapshot { .. }
775 | Self::UpdateRelays { .. }
776 | Self::UpdatePeers { .. } => 1,
777 }
778 }
779
780 pub(crate) fn set_queued_at(&mut self, queued_at: Option<crate::perf_profile::TraceStamp>) {
781 match self {
782 Self::Send { command, .. } | Self::SendOneway { command } => {
783 command.set_queued_at(queued_at);
784 }
785 Self::SendBatchOneway { command, .. } => {
786 command.set_queued_at(queued_at);
787 }
788 Self::PeerSnapshot { .. }
789 | Self::RelaySnapshot { .. }
790 | Self::UpdateRelays { .. }
791 | Self::UpdatePeers { .. } => {}
792 }
793 }
794}
795
796#[derive(Debug, Clone, Default, PartialEq, Eq)]
798pub(crate) struct UpdatePeersOutcome {
799 pub(crate) added: usize,
800 pub(crate) removed: usize,
801 pub(crate) updated: usize,
802 pub(crate) unchanged: usize,
803}
804
805#[derive(Debug)]
811pub(crate) struct EndpointDataDelivery {
812 pub(crate) source_peer: PeerIdentity,
813 pub(crate) payload: Vec<u8>,
814}
815
816impl EndpointDataDelivery {
817 pub(crate) fn new(source_peer: PeerIdentity, payload: Vec<u8>) -> Self {
818 Self {
819 source_peer,
820 payload,
821 }
822 }
823
824 fn is_priority_sized(&self) -> bool {
825 matches!(
826 endpoint_event_lane_for_len(self.payload.len()),
827 EndpointEventLane::Priority
828 )
829 }
830}
831
832#[derive(Debug)]
834pub(crate) enum NodeEndpointEvent {
835 Data {
836 source_peer: PeerIdentity,
837 payload: Vec<u8>,
838 queued_at: Option<crate::perf_profile::TraceStamp>,
839 },
840 DataBatch {
841 messages: Vec<EndpointDataDelivery>,
842 queued_at: Option<crate::perf_profile::TraceStamp>,
843 },
844}
845
846#[derive(Debug, Clone, Copy, PartialEq, Eq)]
847pub(in crate::node) struct EndpointEventDequeueCounts {
848 pub(in crate::node) total: usize,
849 pub(in crate::node) priority: usize,
850 pub(in crate::node) bulk: usize,
851}
852
853impl NodeEndpointEvent {
854 fn message_count(&self) -> usize {
855 match self {
856 NodeEndpointEvent::Data { .. } => 1,
857 NodeEndpointEvent::DataBatch { messages, .. } => messages.len(),
858 }
859 }
860
861 pub(in crate::node) fn dequeue_counts(&self) -> EndpointEventDequeueCounts {
862 match self {
863 NodeEndpointEvent::Data { payload, .. } => {
864 let priority = usize::from(payload.len() <= ENDPOINT_EVENT_PRIORITY_MAX_LEN);
865 EndpointEventDequeueCounts {
866 total: 1,
867 priority,
868 bulk: 1 - priority,
869 }
870 }
871 NodeEndpointEvent::DataBatch { messages, .. } => {
872 let priority = messages
873 .iter()
874 .filter(|message| message.is_priority_sized())
875 .count();
876 EndpointEventDequeueCounts {
877 total: messages.len(),
878 priority,
879 bulk: messages.len().saturating_sub(priority),
880 }
881 }
882 }
883 }
884
885 fn queued_at(&self) -> Option<crate::perf_profile::TraceStamp> {
886 match self {
887 NodeEndpointEvent::Data { queued_at, .. }
888 | NodeEndpointEvent::DataBatch { queued_at, .. } => *queued_at,
889 }
890 }
891
892 fn record_dequeue_wait(&self) {
893 let queued_at = self.queued_at();
894 if queued_at.is_none() {
895 return;
896 }
897 let counts = self.dequeue_counts();
898 crate::perf_profile::record_since_split_count(
899 crate::perf_profile::Stage::EndpointEventWait,
900 crate::perf_profile::Stage::EndpointPriorityEventWait,
901 crate::perf_profile::Stage::EndpointBulkEventWait,
902 queued_at,
903 counts.total as u64,
904 counts.priority as u64,
905 counts.bulk as u64,
906 );
907 }
908
909 fn from_delivery_messages(
910 mut messages: Vec<EndpointDataDelivery>,
911 queued_at: Option<crate::perf_profile::TraceStamp>,
912 ) -> Option<Self> {
913 match messages.len() {
914 0 => None,
915 1 => {
916 let message = messages.pop().expect("one endpoint message should exist");
917 Some(NodeEndpointEvent::Data {
918 source_peer: message.source_peer,
919 payload: message.payload,
920 queued_at,
921 })
922 }
923 _ => Some(NodeEndpointEvent::DataBatch {
924 messages,
925 queued_at,
926 }),
927 }
928 }
929}
930
931#[derive(Debug, Clone, PartialEq, Eq)]
933pub(crate) struct NodeEndpointPeer {
934 pub(crate) npub: String,
935 pub(crate) node_addr: NodeAddr,
936 pub(crate) connected: bool,
937 pub(crate) transport_addr: Option<String>,
938 pub(crate) transport_type: Option<String>,
939 pub(crate) link_id: u64,
940 pub(crate) srtt_ms: Option<u64>,
941 pub(crate) srtt_age_ms: Option<u64>,
942 pub(crate) packets_sent: u64,
943 pub(crate) packets_recv: u64,
944 pub(crate) bytes_sent: u64,
945 pub(crate) bytes_recv: u64,
946 pub(crate) rekey_in_progress: bool,
947 pub(crate) rekey_draining: bool,
948 pub(crate) current_k_bit: Option<bool>,
949 pub(crate) direct_probe_pending: bool,
950 pub(crate) direct_probe_after_ms: Option<u64>,
951 pub(crate) direct_probe_retry_count: u32,
952 pub(crate) direct_probe_auto_reconnect: bool,
953 pub(crate) direct_probe_expires_at_ms: Option<u64>,
954 pub(crate) nostr_traversal_consecutive_failures: u32,
955 pub(crate) nostr_traversal_in_cooldown: bool,
956 pub(crate) nostr_traversal_cooldown_until_ms: Option<u64>,
957 pub(crate) nostr_traversal_last_observed_skew_ms: Option<i64>,
958}
959
960#[derive(Debug, Clone, PartialEq, Eq)]
962pub(crate) struct NodeEndpointRelayStatus {
963 pub(crate) url: String,
964 pub(crate) status: String,
965}