1use crate::{Error, Result, Timescale, Timestamp, coding};
17use crate::{broadcast, cache, frame, group, stats};
18
19use super::{Datagram, Requests};
20
21pub use super::subscription::Subscription;
22
23use std::{
24 collections::{HashSet, VecDeque},
25 sync::Arc,
26 task::{Poll, ready},
27 time::Duration,
28};
29
30pub const DEFAULT_LATENCY_MAX: Duration = Duration::from_secs(5);
32
33const MAX_DATAGRAM_AGE: Duration = Duration::from_millis(50);
39
40#[derive(Clone, Debug)]
49#[non_exhaustive]
50pub struct Info {
51 pub timescale: Timescale,
58 pub latency_max: Duration,
67 pub priority: u8,
70 pub ordered: bool,
74
75 pub(crate) broadcast: Arc<broadcast::Info>,
82}
83
84fn default_broadcast() -> Arc<broadcast::Info> {
88 static DEFAULT: std::sync::LazyLock<Arc<broadcast::Info>> =
89 std::sync::LazyLock::new(|| Arc::new(broadcast::Info::default()));
90 DEFAULT.clone()
91}
92
93impl Default for Info {
94 fn default() -> Self {
95 Self {
96 timescale: Timescale::default(),
97 latency_max: DEFAULT_LATENCY_MAX,
98 priority: 0,
99 ordered: false,
100 broadcast: default_broadcast(),
101 }
102 }
103}
104
105impl Info {
106 pub fn with_timescale(mut self, timescale: Timescale) -> Self {
111 self.timescale = timescale;
112 self
113 }
114
115 pub fn with_latency_max(mut self, latency_max: Duration) -> Self {
117 self.latency_max = latency_max;
118 self
119 }
120
121 pub fn with_priority(mut self, priority: u8) -> Self {
123 self.priority = priority;
124 self
125 }
126
127 pub fn with_ordered(mut self, ordered: bool) -> Self {
131 self.ordered = ordered;
132 self
133 }
134
135 pub(crate) fn bind(&mut self, broadcast: Arc<broadcast::Info>) {
142 self.latency_max = self.latency_max.min(broadcast.origin.cache_duration);
143 self.broadcast = broadcast;
144 }
145}
146
147#[derive(Default)]
148struct TrackState {
149 info: Option<Info>,
152
153 broadcast: Arc<broadcast::Info>,
157
158 latest_entry: Option<Arc<cache::Entry>>,
162
163 groups: VecDeque<Option<(group::Producer, web_async::time::Instant)>>,
165
166 datagrams: VecDeque<(Datagram, web_async::time::Instant)>,
170
171 datagram_offset: usize,
174
175 duplicates: HashSet<u64>,
179
180 offset: usize,
182
183 max_sequence: Option<u64>,
185
186 final_sequence: Option<u64>,
188
189 abort: Option<Error>,
191
192 subscriptions: kio::Shared<Subscriptions>,
196
197 fetch: kio::Shared<FetchState>,
200}
201
202type Subscriptions = Vec<kio::Consumer<Subscription>>;
204
205type FetchState = Requests<u64, PendingFetch>;
210
211struct PendingFetch {
213 priority: u8,
215
216 result: kio::Producer<FetchOutcome>,
221}
222
223#[derive(Default)]
226struct FetchOutcome {
227 rejected: Option<Error>,
228}
229
230impl TrackState {
231 fn poll_info(&self) -> Poll<Result<Info>> {
232 if let Some(info) = &self.info {
233 Poll::Ready(Ok(info.clone()))
234 } else {
235 Poll::Pending
236 }
237 }
238
239 fn poll_recv_group(&self, index: usize, min_sequence: u64) -> Poll<Result<Option<(group::Consumer, usize)>>> {
243 let start = index.saturating_sub(self.offset);
244 for (i, slot) in self.groups.iter().enumerate().skip(start) {
245 if let Some((group, _)) = slot
246 && group.sequence >= min_sequence
247 && !group.is_aborted()
248 {
249 return Poll::Ready(Ok(Some((group.consume(), self.offset + i))));
250 }
251 }
252
253 if self.is_complete() {
255 Poll::Ready(Ok(None))
256 } else if let Some(err) = &self.abort {
257 Poll::Ready(Err(err.clone()))
258 } else {
259 Poll::Pending
260 }
261 }
262
263 fn poll_recv_datagram(&self, index: usize) -> Poll<Result<Option<(Datagram, usize)>>> {
269 let start = index.saturating_sub(self.datagram_offset);
270 if let Some((datagram, _)) = self.datagrams.get(start) {
271 return Poll::Ready(Ok(Some((datagram.clone(), self.datagram_offset + start))));
272 }
273
274 if self.is_complete() {
276 Poll::Ready(Ok(None))
277 } else if let Some(err) = &self.abort {
278 Poll::Ready(Err(err.clone()))
279 } else {
280 Poll::Pending
281 }
282 }
283
284 fn push_datagram(&mut self, datagram: Datagram) {
286 let now = web_async::time::Instant::now();
287 self.datagrams.push_back((datagram, now));
288 while let Some((_, at)) = self.datagrams.front() {
289 if now.duration_since(*at) <= MAX_DATAGRAM_AGE {
290 break;
291 }
292 self.datagrams.pop_front();
293 self.datagram_offset += 1;
294 }
295 }
296
297 fn poll_read_frame(
301 &self,
302 index: usize,
303 next_sequence: u64,
304 waiter: &kio::Waiter,
305 ) -> Poll<Result<Option<(frame::Frame, usize, u64)>>> {
306 let start = index.saturating_sub(self.offset);
307 let mut pending_seen = false;
308 for (i, slot) in self.groups.iter().enumerate().skip(start) {
309 let Some((group, _)) = slot else { continue };
310 if group.sequence < next_sequence {
311 continue;
312 }
313
314 let mut consumer = group.consume();
315 match consumer.poll_read_frame(waiter) {
316 Poll::Ready(Ok(Some(frame))) => {
317 return Poll::Ready(Ok(Some((frame, self.offset + i, group.sequence))));
318 }
319 Poll::Ready(Ok(None)) => continue,
320 Poll::Ready(Err(_)) => continue,
323 Poll::Pending => {
324 pending_seen = true;
325 continue;
326 }
327 }
328 }
329
330 if pending_seen {
333 Poll::Pending
334 } else if self.is_complete() {
335 Poll::Ready(Ok(None))
336 } else if let Some(err) = &self.abort {
337 Poll::Ready(Err(err.clone()))
338 } else {
339 Poll::Pending
340 }
341 }
342
343 fn poll_next_in_range(
353 &self,
354 next_sequence: u64,
355 end_sequence: Option<u64>,
356 ) -> Poll<Result<Option<group::Consumer>>> {
357 if let Some(end) = end_sequence
361 && end < next_sequence
362 {
363 if let Some(err) = &self.abort {
364 return Poll::Ready(Err(err.clone()));
365 }
366 return Poll::Pending;
367 }
368
369 let mut best: Option<&group::Producer> = None;
370 for (group, _) in self.groups.iter().flatten() {
371 if group.sequence < next_sequence {
372 continue;
373 }
374 if let Some(end) = end_sequence
375 && group.sequence > end
376 {
377 continue;
378 }
379 if group.is_aborted() {
380 continue;
381 }
382 if best.is_none_or(|b| group.sequence < b.sequence) {
383 best = Some(group);
384 }
385 }
386
387 if let Some(group) = best {
388 return Poll::Ready(Ok(Some(group.consume())));
389 }
390
391 if let Some(err) = &self.abort {
393 return Poll::Ready(Err(err.clone()));
394 }
395 if let Some(fin) = self.final_sequence
398 && next_sequence >= fin
399 {
400 return Poll::Ready(Ok(None));
401 }
402 Poll::Pending
403 }
404
405 fn cached_group(&self, sequence: u64) -> Option<group::Consumer> {
409 self.groups
410 .iter()
411 .flatten()
412 .find(|(group, _)| group.sequence == sequence && !group.is_aborted())
413 .map(|(group, _)| group.consume())
414 }
415
416 fn latency_bound(&self) -> Option<Duration> {
419 self.info.as_ref().map(|info| info.latency_max)
420 }
421
422 fn poll_fetch_cached(&self, sequence: u64) -> Poll<Result<group::Consumer>> {
427 if let Some(group) = self.cached_group(sequence) {
428 return Poll::Ready(Ok(group));
429 }
430
431 if let Some(err) = &self.abort {
432 return Poll::Ready(Err(err.clone()));
433 }
434
435 if self.final_sequence.is_some_and(|fin| sequence >= fin) {
437 return Poll::Ready(Err(Error::NotFound));
438 }
439
440 Poll::Pending
441 }
442
443 fn evict_expired(&mut self, now: web_async::time::Instant, max_age: Duration) {
454 for slot in self.groups.iter_mut() {
455 let Some((group, created_at)) = slot else { continue };
456
457 if group.is_aborted() {
460 self.duplicates.remove(&group.sequence);
461 *slot = None;
462 continue;
463 }
464
465 if Some(group.sequence) == self.max_sequence {
466 continue;
467 }
468
469 if now.duration_since(*created_at) <= max_age {
470 break;
471 }
472
473 self.duplicates.remove(&group.sequence);
474 if let Some((group, _)) = slot.take() {
479 let _ = group.abort(Error::Old);
480 }
481 }
482
483 while let Some(None) = self.groups.front() {
485 self.groups.pop_front();
486 self.offset += 1;
487 }
488 }
489
490 fn pin_latest(&mut self, group: &group::Producer) {
493 if Some(group.sequence) != self.max_sequence {
494 return;
495 }
496 if let Some(prev) = self.latest_entry.take() {
497 prev.set_pinned(false);
498 }
499 if let Some(entry) = group.cache_entry() {
500 entry.set_pinned(true);
501 self.latest_entry = Some(entry);
502 }
503 }
504
505 fn set_final(&mut self, final_sequence: u64) -> Result<()> {
508 if self.final_sequence.is_some() {
509 return Err(Error::Closed);
510 }
511 if let Some(max) = self.max_sequence
512 && final_sequence <= max
513 {
514 return Err(Error::ProtocolViolation);
515 }
516 self.final_sequence = Some(final_sequence);
517 Ok(())
518 }
519
520 fn is_complete(&self) -> bool {
526 self.final_sequence
527 .is_some_and(|fin| self.max_sequence.map_or(0, |max| max.saturating_add(1)) >= fin)
528 }
529
530 fn poll_finished(&self) -> Poll<Result<u64>> {
531 if let Some(fin) = self.final_sequence {
532 Poll::Ready(Ok(fin))
533 } else if let Some(err) = &self.abort {
534 Poll::Ready(Err(err.clone()))
535 } else {
536 Poll::Pending
537 }
538 }
539
540 fn modify(producer: &kio::Producer<Self>) -> Result<kio::Mut<'_, Self>> {
541 producer.write().map_err(|r| r.abort.clone().unwrap_or(Error::Dropped))
542 }
543
544 fn replace_evicted(
548 &mut self,
549 sequence: u64,
550 track: Info,
551 now: web_async::time::Instant,
552 ) -> Option<Result<group::Producer>> {
553 let slot = self
554 .groups
555 .iter_mut()
556 .find(|slot| matches!(slot, Some((group, _)) if group.sequence == sequence))?;
557 let (existing, _) = slot.as_ref().unwrap();
558 if !existing.is_aborted() {
559 return Some(Err(Error::Duplicate));
560 }
561 let group = group::Producer::new(group::Info { sequence }, track);
562 *slot = Some((group.clone(), now));
563 self.pin_latest(&group);
566 Some(Ok(group))
567 }
568
569 fn insert_group_request(&mut self, sequence: u64, info: Option<Info>) -> Result<group::Producer> {
575 if let Some(err) = &self.abort {
576 return Err(err.clone());
577 }
578 if let Some(fin) = self.final_sequence
579 && sequence >= fin
580 {
581 return Err(Error::Closed);
582 }
583
584 let now = web_async::time::Instant::now();
587 let broadcast = self.broadcast.clone();
588 let info = self
589 .info
590 .get_or_insert_with(|| {
591 let mut info = info.unwrap_or_default();
592 info.bind(broadcast);
593 info
594 })
595 .clone();
596
597 if !self.duplicates.insert(sequence) {
598 return self
600 .replace_evicted(sequence, info, now)
601 .unwrap_or(Err(Error::Duplicate));
602 }
603
604 let latency_max = info.latency_max;
605 let group = group::Producer::new(group::Info { sequence }, info);
606 self.max_sequence = Some(self.max_sequence.unwrap_or(0).max(sequence));
607 self.groups.push_back(Some((group.clone(), now)));
608 self.pin_latest(&group);
609 self.evict_expired(now, latency_max);
610 Ok(group)
611 }
612}
613
614#[derive(Clone)]
616pub struct Producer {
617 name: Arc<str>,
618 broadcast: Arc<broadcast::Info>,
621 state: kio::Producer<TrackState>,
622 prev_subscription: Option<Subscription>,
623 stats: stats::Scope,
627}
628
629impl Producer {
630 pub(crate) fn new(
638 broadcast: Arc<broadcast::Info>,
639 name: impl Into<Arc<str>>,
640 info: impl Into<Option<Info>>,
641 ) -> Self {
642 let mut info = info.into().unwrap_or_default();
643 info.bind(broadcast.clone());
644 Self {
645 name: name.into(),
646 state: kio::Producer::new(TrackState {
647 info: Some(info),
648 broadcast: broadcast.clone(),
649 ..Default::default()
650 }),
651 broadcast,
652 prev_subscription: None,
653 stats: stats::Scope::default(),
654 }
655 }
656
657 pub(crate) fn with_stats(mut self, scope: stats::Scope) -> Self {
661 scope.open_subscription();
662 self.stats = scope;
663 self
664 }
665
666 pub fn name(&self) -> &str {
668 &self.name
669 }
670
671 pub fn broadcast(&self) -> &broadcast::Info {
673 &self.broadcast
674 }
675
676 pub fn create_group(&mut self, group: group::Info) -> Result<group::Producer> {
678 let mut state = self.modify()?;
679 if let Some(fin) = state.final_sequence
680 && group.sequence >= fin
681 {
682 return Err(Error::Closed);
683 }
684 let info = state.info.as_ref().unwrap();
685 let track = info.clone();
686 let latency_max = info.latency_max;
687 let now = web_async::time::Instant::now();
688
689 if !state.duplicates.insert(group.sequence) {
690 return state
692 .replace_evicted(group.sequence, track, now)
693 .unwrap_or(Err(Error::Duplicate));
694 }
695
696 let group = group::Producer::new(group, track).with_meter(self.stats.meter());
697 state.max_sequence = Some(state.max_sequence.unwrap_or(0).max(group.sequence));
698 state.groups.push_back(Some((group.clone(), now)));
699 state.pin_latest(&group);
700 state.evict_expired(now, latency_max);
701
702 Ok(group)
703 }
704
705 pub fn append_group(&mut self) -> Result<group::Producer> {
707 let mut state = self.modify()?;
708 let sequence = match state.max_sequence {
709 Some(s) => s.checked_add(1).ok_or(coding::BoundsExceeded)?,
710 None => 0,
711 };
712 if let Some(fin) = state.final_sequence
713 && sequence >= fin
714 {
715 return Err(Error::Closed);
716 }
717
718 let info = state.info.as_ref().unwrap();
719 let track = info.clone();
720 let latency_max = info.latency_max;
721
722 let group = group::Producer::new(group::Info { sequence }, track).with_meter(self.stats.meter());
723
724 let now = web_async::time::Instant::now();
725 state.duplicates.insert(sequence);
726 state.max_sequence = Some(sequence);
727 state.groups.push_back(Some((group.clone(), now)));
728 state.pin_latest(&group);
729 state.evict_expired(now, latency_max);
730
731 Ok(group)
732 }
733
734 pub fn append_datagram<B: crate::IntoBytes>(&mut self, timestamp: Timestamp, payload: B) -> Result<u64> {
746 let payload = payload.into_bytes();
747 if payload.len() > super::datagram::MAX_DATAGRAM_PAYLOAD {
748 return Err(Error::FrameTooLarge);
749 }
750 let meter = self.stats.meter();
752 let mut state = self.modify()?;
753 let timescale = state.info.as_ref().unwrap().timescale;
755 let timestamp = timestamp.convert(timescale).map_err(|_| Error::TimestampMismatch)?;
756 let sequence = match state.max_sequence {
757 Some(s) => s.checked_add(1).ok_or(coding::BoundsExceeded)?,
758 None => 0,
759 };
760 if let Some(fin) = state.final_sequence
761 && sequence >= fin
762 {
763 return Err(Error::Closed);
764 }
765 state.max_sequence = Some(sequence);
766 meter.datagram(payload.len() as u64);
767 state.push_datagram(Datagram {
768 sequence,
769 timestamp,
770 payload,
771 });
772 Ok(sequence)
773 }
774
775 pub fn write_datagram(&mut self, mut datagram: Datagram) -> Result<()> {
781 if datagram.payload.len() > super::datagram::MAX_DATAGRAM_PAYLOAD {
782 return Err(Error::FrameTooLarge);
783 }
784 let meter = self.stats.meter();
786 let mut state = self.modify()?;
787 let timescale = state.info.as_ref().unwrap().timescale;
789 datagram.timestamp = datagram
790 .timestamp
791 .convert(timescale)
792 .map_err(|_| Error::TimestampMismatch)?;
793 if let Some(fin) = state.final_sequence
794 && datagram.sequence >= fin
795 {
796 return Err(Error::Closed);
797 }
798 state.max_sequence = Some(state.max_sequence.unwrap_or(0).max(datagram.sequence));
799 meter.datagram(datagram.payload.len() as u64);
800 state.push_datagram(datagram);
801 Ok(())
802 }
803
804 pub fn write_frame<B: crate::IntoBytes>(&mut self, timestamp: Timestamp, frame: B) -> Result<()> {
809 let mut group = self.append_group()?;
810 group.write_frame(timestamp, frame)?;
811 group.finish()?;
812 Ok(())
813 }
814
815 pub fn finish(&mut self) -> Result<()> {
821 let mut state = self.modify()?;
822 let final_sequence = match state.max_sequence {
823 Some(max) => max.checked_add(1).ok_or(coding::BoundsExceeded)?,
824 None => 0,
825 };
826 state.set_final(final_sequence)
827 }
828
829 pub fn finish_at(&mut self, final_sequence: u64) -> Result<()> {
842 self.modify()?.set_final(final_sequence)
843 }
844
845 pub fn final_sequence(&self) -> Option<u64> {
850 self.state.read().final_sequence
851 }
852
853 pub fn abort(self, err: Error) -> Result<()> {
864 let mut guard = self.modify()?;
865 guard.abort = Some(err);
866 guard.groups.clear();
867 guard.datagrams.clear();
868 guard.duplicates.clear();
869 guard.latest_entry = None;
870 guard.close();
871 Ok(())
872 }
873
874 pub async fn unused(&self) -> Result<()> {
876 self.state.unused().await.map_err(|_| self.abort_reason())
877 }
878
879 pub async fn used(&self) -> Result<()> {
881 self.state.used().await.map_err(|_| self.abort_reason())
882 }
883
884 pub async fn closed(&self) -> Error {
886 kio::wait(|waiter| self.poll_closed(waiter)).await
887 }
888
889 pub fn poll_closed(&self, waiter: &kio::Waiter) -> Poll<Error> {
891 self.state.poll_closed(waiter).map(|()| self.abort_reason())
892 }
893
894 fn abort_reason(&self) -> Error {
896 self.state.read().abort.clone().unwrap_or(Error::Dropped)
897 }
898
899 pub fn is_closed(&self) -> bool {
901 self.state.read().is_closed()
902 }
903
904 pub fn latest(&self) -> Option<u64> {
906 self.state.read().max_sequence
907 }
908
909 pub fn is_clone(&self, other: &Self) -> bool {
911 self.state.same_channel(&other.state)
912 }
913
914 pub(crate) fn weak(&self) -> TrackWeak {
916 TrackWeak {
917 name: self.name.clone(),
918 state: self.state.weak(),
919 }
920 }
921
922 pub fn demand(&self) -> Demand {
930 Demand {
931 name: self.name.clone(),
932 state: self.state.weak(),
933 }
934 }
935
936 pub fn consume(&self) -> Consumer {
941 Consumer::plain(self.name.clone(), self.state.consume())
942 }
943
944 pub fn subscribe(&self, subscription: impl Into<Option<Subscription>>) -> Subscriber {
949 let preferences = subscription.into().unwrap_or_default();
950
951 let info = self
956 .state
957 .read()
958 .info
959 .as_ref()
960 .expect("producer always has info")
961 .clone();
962 let subscription = kio::Producer::new(preferences);
963 register_subscription(self.state.read(), &subscription);
964
965 Subscriber {
966 name: self.name.clone(),
967 info,
968 inner: SubscriberKind::Plain(PlainSubscriber {
969 state: self.state.consume(),
970 subscription,
971 index: 0,
972 datagram_index: 0,
973 min_sequence: 0,
974 next_sequence: 0,
975 end_sequence: None,
976 }),
977 stats: stats::Scope::default(),
979 _stats_sub: stats::Subscription::default(),
980 }
981 }
982
983 pub async fn subscription_changed(&mut self) -> Result<Option<Subscription>> {
989 kio::wait(|waiter| self.poll_subscription_changed(waiter)).await
990 }
991
992 pub fn subscription(&self) -> Option<Subscription> {
1000 let state = self.state.read();
1001 let (subs, bound) = (state.subscriptions.clone(), state.latency_bound());
1002 drop(state);
1003 snapshot_subscription(&subs, bound)
1004 }
1005
1006 pub fn poll_subscription_changed(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<Subscription>>> {
1010 if self.state.poll_closed(waiter).is_ready() {
1013 let abort = self.state.read().abort.clone();
1014 return Poll::Ready(Err(abort.unwrap_or(Error::Dropped)));
1015 }
1016
1017 let state = self.state.read();
1019 let (subs, bound) = (state.subscriptions.clone(), state.latency_bound());
1020 drop(state);
1021
1022 let prev = &self.prev_subscription;
1023 let mut combined = None;
1024 let mut guard = match subs.poll(waiter, |subs| {
1025 let next = combined_subscription(subs, bound, waiter);
1026 if &next == prev {
1027 Poll::Pending
1028 } else {
1029 combined = next;
1030 Poll::Ready(())
1031 }
1032 }) {
1033 Poll::Ready(guard) => guard,
1034 Poll::Pending => return Poll::Pending,
1035 };
1036 guard.retain(|sub| !sub.is_closed());
1038 drop(guard);
1039 self.prev_subscription = combined.clone();
1040 Poll::Ready(Ok(combined))
1041 }
1042
1043 pub fn poll_unused(&self, waiter: &kio::Waiter) -> Poll<()> {
1045 self.state.poll_unused(waiter).map(|_| ())
1046 }
1047
1048 pub fn dynamic(&self) -> Dynamic {
1052 Dynamic::new(self.name.clone(), self.state.clone())
1053 }
1054
1055 fn modify(&self) -> Result<kio::Mut<'_, TrackState>> {
1056 TrackState::modify(&self.state)
1057 }
1058}
1059
1060fn poll_requested_group(
1064 state: &kio::Producer<TrackState>,
1065 fetch: &kio::Shared<FetchState>,
1066 waiter: &kio::Waiter,
1067) -> Poll<Result<GroupRequest>> {
1068 if let Poll::Ready(mut guard) = fetch.poll(waiter, |fetch| {
1070 if fetch.has_queued() {
1071 Poll::Ready(())
1072 } else {
1073 Poll::Pending
1074 }
1075 }) {
1076 let sequence = guard.pop().expect("predicate guaranteed a request");
1077 let pending = guard.get(&sequence).expect("popped key must be pending");
1081 let priority = pending.priority;
1082 let result = pending.result.clone();
1083 drop(guard);
1084 return Poll::Ready(Ok(GroupRequest {
1085 state: state.clone(),
1086 fetch: fetch.clone(),
1087 sequence,
1088 priority,
1089 result,
1090 done: false,
1091 }));
1092 }
1093
1094 match state.poll_ref(waiter, |state| match &state.abort {
1096 Some(err) => Poll::Ready(err.clone()),
1097 None => Poll::Pending,
1098 }) {
1099 Poll::Ready(Ok(err)) => Poll::Ready(Err(err)),
1100 Poll::Ready(Err(closed)) => Poll::Ready(Err(closed.abort.clone().unwrap_or(Error::Dropped))),
1101 Poll::Pending => Poll::Pending,
1102 }
1103}
1104
1105pub struct Dynamic {
1115 name: Arc<str>,
1116 state: kio::Producer<TrackState>,
1118 fetch: kio::Shared<FetchState>,
1120}
1121
1122impl Dynamic {
1123 fn new(name: Arc<str>, state: kio::Producer<TrackState>) -> Self {
1124 let fetch = state.read().fetch.clone();
1125 fetch.lock().add_handler();
1126 Self { name, state, fetch }
1127 }
1128
1129 pub fn name(&self) -> &str {
1131 &self.name
1132 }
1133
1134 pub async fn requested_group(&self) -> Result<GroupRequest> {
1140 kio::wait(|waiter| self.poll_requested_group(waiter)).await
1141 }
1142
1143 pub fn poll_requested_group(&self, waiter: &kio::Waiter) -> Poll<Result<GroupRequest>> {
1145 poll_requested_group(&self.state, &self.fetch, waiter)
1146 }
1147
1148 pub fn poll_unused(&self, waiter: &kio::Waiter) -> Poll<()> {
1150 self.state.poll_unused(waiter).map(|_| ())
1151 }
1152}
1153
1154impl Clone for Dynamic {
1155 fn clone(&self) -> Self {
1156 self.fetch.lock().add_handler();
1158 Self {
1159 name: self.name.clone(),
1160 state: self.state.clone(),
1161 fetch: self.fetch.clone(),
1162 }
1163 }
1164}
1165
1166impl Drop for Dynamic {
1167 fn drop(&mut self) {
1168 let mut fetch = self.fetch.lock();
1174 if fetch.remove_handler() {
1175 fetch.drain_queued();
1176 }
1177 }
1178}
1179
1180impl Drop for Producer {
1181 fn drop(&mut self) {
1182 if !self.state.is_last() {
1187 return;
1188 }
1189 self.stats.close_subscription();
1191 if let Ok(mut state) = self.state.write()
1192 && state.final_sequence.is_none()
1193 {
1194 tracing::warn!(
1198 track = %self.name(),
1199 "track::Producer dropped without finish() or abort()"
1200 );
1201 state.groups.clear();
1202 state.datagrams.clear();
1203 state.duplicates.clear();
1204 state.latest_entry = None;
1205 }
1206 }
1207}
1208
1209fn combined_subscription(subs: &Subscriptions, bound: Option<Duration>, waiter: &kio::Waiter) -> Option<Subscription> {
1215 let mut combined = None;
1216 for sub in subs.iter() {
1217 if sub.is_closed() {
1222 continue;
1223 }
1224 let _ = sub.poll_closed(waiter);
1229 if let Poll::Ready(Ok(sub)) = sub.poll(waiter, |sub| sub.poll_combined(&combined)) {
1230 combined = Some(sub);
1231 }
1232 }
1233 clamp_combined(combined, bound)
1234}
1235
1236fn snapshot_subscription(subs: &kio::Shared<Subscriptions>, bound: Option<Duration>) -> Option<Subscription> {
1238 let mut combined: Option<Subscription> = None;
1239 for sub in subs.read().iter() {
1240 if sub.is_closed() {
1242 continue;
1243 }
1244 if let Poll::Ready(merged) = sub.read().poll_combined(&combined) {
1245 combined = Some(merged);
1246 }
1247 }
1248 clamp_combined(combined, bound)
1249}
1250
1251fn clamp_combined(combined: Option<Subscription>, bound: Option<Duration>) -> Option<Subscription> {
1259 let mut combined = combined?;
1260 if let Some(bound) = bound {
1261 combined.latency_max = combined.latency_max.min(bound);
1262 }
1263 Some(combined)
1264}
1265
1266fn register_subscription(state: kio::Ref<'_, TrackState>, subscription: &kio::Producer<Subscription>) {
1270 if state.is_closed() {
1271 return;
1272 }
1273 let subs = state.subscriptions.clone();
1274 drop(state);
1275 subs.lock().push(subscription.consume());
1276}
1277
1278#[derive(Clone)]
1280pub(crate) struct TrackWeak {
1281 name: Arc<str>,
1282 state: kio::ProducerWeak<TrackState>,
1283}
1284
1285impl TrackWeak {
1286 pub fn consume(&self) -> Consumer {
1287 Consumer::plain(self.name.clone(), self.state.consume())
1288 }
1289
1290 pub(crate) fn name(&self) -> &Arc<str> {
1293 &self.name
1294 }
1295
1296 pub(crate) fn is_used(&self) -> bool {
1299 !self.state.is_closed() && self.state.is_used()
1300 }
1301
1302 pub(crate) fn poll_used(&self, waiter: &kio::Waiter) {
1305 let _ = self.state.poll_used(waiter);
1306 }
1307
1308 pub(crate) fn poll_unused(&self, waiter: &kio::Waiter) {
1311 let _ = self.state.poll_unused(waiter);
1312 }
1313}
1314
1315impl super::WeakEntry for TrackWeak {
1316 fn is_closed(&self) -> bool {
1317 self.state.is_closed()
1318 }
1319
1320 fn same_channel(&self, other: &Self) -> bool {
1321 self.state.same_channel(&other.state)
1322 }
1323}
1324
1325#[derive(Clone)]
1334pub struct Demand {
1335 name: Arc<str>,
1336 state: kio::ProducerWeak<TrackState>,
1337}
1338
1339impl Demand {
1340 pub fn name(&self) -> &str {
1342 &self.name
1343 }
1344
1345 pub async fn used(&self) -> Result<()> {
1347 self.state.used().await.map_err(|_| self.abort_reason())
1348 }
1349
1350 pub async fn unused(&self) -> Result<()> {
1352 self.state.unused().await.map_err(|_| self.abort_reason())
1353 }
1354
1355 pub async fn closed(&self) -> Error {
1357 self.state.closed().await;
1358 self.abort_reason()
1359 }
1360
1361 fn abort_reason(&self) -> Error {
1363 self.state.read().abort.clone().unwrap_or(Error::Dropped)
1364 }
1365}
1366
1367#[derive(Clone)]
1378pub struct Consumer {
1379 name: Arc<str>,
1380 inner: ConsumerKind,
1381 stats: stats::Scope,
1384}
1385
1386#[derive(Clone)]
1387enum ConsumerKind {
1388 Plain(kio::Consumer<TrackState>),
1389 Spliced(super::resume::Consumer),
1390}
1391
1392impl Consumer {
1393 fn plain(name: Arc<str>, state: kio::Consumer<TrackState>) -> Self {
1394 Self {
1395 name,
1396 inner: ConsumerKind::Plain(state),
1397 stats: stats::Scope::default(),
1398 }
1399 }
1400
1401 pub(crate) fn spliced(name: Arc<str>, resume: super::resume::Consumer) -> Self {
1403 Self {
1404 name,
1405 inner: ConsumerKind::Spliced(resume),
1406 stats: stats::Scope::default(),
1407 }
1408 }
1409
1410 pub(crate) fn with_stats(mut self, scope: stats::Scope) -> Self {
1413 self.stats = scope;
1414 self
1415 }
1416
1417 pub fn name(&self) -> &str {
1419 &self.name
1420 }
1421
1422 pub fn subscribe(&self, subscription: impl Into<Option<Subscription>>) -> kio::Pending<Subscribing> {
1428 let subscription = kio::Producer::new(subscription.into().unwrap_or_default());
1429
1430 let inner = match &self.inner {
1431 ConsumerKind::Plain(state) => {
1432 register_subscription(state.read(), &subscription);
1435 SubscribingKind::Plain(state.clone())
1436 }
1437 ConsumerKind::Spliced(resume) => SubscribingKind::Spliced(resume.clone()),
1439 };
1440
1441 kio::Pending::new(Subscribing {
1442 name: self.name.clone(),
1443 inner,
1444 subscription,
1445 stats: self.stats.clone(),
1446 })
1447 }
1448
1449 #[cfg(test)]
1453 pub(crate) fn peek_group(&self, sequence: u64) -> Option<group::Consumer> {
1454 match &self.inner {
1455 ConsumerKind::Plain(state) => state.read().cached_group(sequence),
1456 ConsumerKind::Spliced(_) => None,
1459 }
1460 }
1461
1462 pub fn fetch_group(&self, sequence: u64, options: impl Into<Option<group::Fetch>>) -> kio::Pending<Fetching> {
1474 let options = options.into().unwrap_or_default();
1475
1476 self.stats.fetch();
1480
1481 let state = match &self.inner {
1482 ConsumerKind::Plain(state) => state,
1483 ConsumerKind::Spliced(resume) => {
1486 return kio::Pending::new(Fetching {
1487 inner: FetchingKind::Spliced(resume.fetch_group(sequence, options)),
1488 stats: self.stats.clone(),
1489 });
1490 }
1491 };
1492
1493 let mut result = None;
1494
1495 let (fetch, unresolved) = {
1499 let state = state.read();
1500 (state.fetch.clone(), state.poll_fetch_cached(sequence).is_pending())
1501 };
1502
1503 if unresolved {
1504 let mut fetch = fetch.lock();
1505 if let Some(pending) = fetch.join(&sequence) {
1506 pending.priority = pending.priority.max(options.priority);
1509 result = Some(pending.result.consume());
1510 } else {
1511 let producer = kio::Producer::<FetchOutcome>::default();
1515 let consumer = producer.consume();
1516 let attempt = PendingFetch {
1517 priority: options.priority,
1518 result: producer,
1519 };
1520 if fetch.insert(sequence, attempt).is_ok() {
1521 result = Some(consumer);
1522 }
1523 }
1524 }
1525
1526 kio::Pending::new(Fetching {
1527 inner: FetchingKind::Plain {
1528 state: state.clone(),
1529 fetch,
1530 sequence,
1531 result,
1532 },
1533 stats: self.stats.clone(),
1534 })
1535 }
1536
1537 pub fn info(&self) -> kio::Pending<Querying> {
1544 kio::Pending::new(Querying {
1545 inner: match &self.inner {
1546 ConsumerKind::Plain(state) => QueryingKind::Plain(state.clone()),
1547 ConsumerKind::Spliced(resume) => QueryingKind::Spliced(resume.clone()),
1548 },
1549 })
1550 }
1551
1552 pub fn latest(&self) -> Option<u64> {
1554 match &self.inner {
1555 ConsumerKind::Plain(state) => state.read().max_sequence,
1556 ConsumerKind::Spliced(resume) => resume.latest(),
1557 }
1558 }
1559
1560 pub(crate) fn poll_complete(&self, waiter: &kio::Waiter) -> Poll<Result<()>> {
1565 let ConsumerKind::Plain(state) = &self.inner else {
1566 return Poll::Pending;
1568 };
1569 match ready!(state.poll(waiter, |state| {
1570 if state.is_complete() {
1571 Poll::Ready(())
1572 } else {
1573 Poll::Pending
1574 }
1575 })) {
1576 Ok(_) => Poll::Ready(Ok(())),
1577 Err(closed) => Poll::Ready(Err(closed.abort.clone().unwrap_or(Error::Dropped))),
1580 }
1581 }
1582}
1583
1584pub struct Subscribing {
1587 name: Arc<str>,
1588 inner: SubscribingKind,
1589 subscription: kio::Producer<Subscription>,
1590 stats: stats::Scope,
1591}
1592
1593enum SubscribingKind {
1594 Plain(kio::Consumer<TrackState>),
1595 Spliced(super::resume::Consumer),
1596}
1597
1598impl Subscribing {
1599 pub fn poll_ok(&self, waiter: &kio::Waiter) -> Poll<Result<Subscriber>> {
1602 match &self.inner {
1603 SubscribingKind::Plain(state) => {
1604 let info = ready!(state.poll(waiter, |state| state.poll_info()))
1606 .map_err(|e| e.abort.clone().unwrap_or(Error::Dropped))??;
1607
1608 Poll::Ready(Ok(Subscriber {
1609 name: self.name.clone(),
1610 info,
1611 inner: SubscriberKind::Plain(PlainSubscriber {
1612 state: state.clone(),
1613 subscription: self.subscription.clone(),
1614 index: 0,
1615 datagram_index: 0,
1616 min_sequence: 0,
1617 next_sequence: 0,
1618 end_sequence: None,
1619 }),
1620 stats: self.stats.clone(),
1621 _stats_sub: self.stats.subscribe(),
1622 }))
1623 }
1624 SubscribingKind::Spliced(resume) => {
1625 let info = ready!(resume.poll_info(waiter))?;
1628
1629 Poll::Ready(Ok(Subscriber {
1630 name: self.name.clone(),
1631 info,
1632 inner: SubscriberKind::Spliced(Box::new(resume.subscribe_shared(self.subscription.clone()))),
1633 stats: self.stats.clone(),
1634 _stats_sub: self.stats.subscribe(),
1635 }))
1636 }
1637 }
1638 }
1639
1640 pub fn update(&mut self, subscription: Subscription) -> Result<()> {
1645 let mut state = self.subscription.write().map_err(|_| Error::Closed)?;
1646 *state = subscription;
1647 Ok(())
1648 }
1649}
1650
1651impl kio::Pollable for Subscribing {
1652 type Output = Result<Subscriber>;
1653
1654 fn poll(&self, waiter: &kio::Waiter) -> Poll<Self::Output> {
1655 self.poll_ok(waiter)
1656 }
1657}
1658
1659pub struct Querying {
1662 inner: QueryingKind,
1663}
1664
1665enum QueryingKind {
1666 Plain(kio::Consumer<TrackState>),
1667 Spliced(super::resume::Consumer),
1668}
1669
1670impl Querying {
1671 pub fn poll_ok(&self, waiter: &kio::Waiter) -> Poll<Result<Info>> {
1673 match &self.inner {
1674 QueryingKind::Plain(state) => {
1675 let info = ready!(state.poll(waiter, |state| state.poll_info()))
1677 .map_err(|e| e.abort.clone().unwrap_or(Error::Dropped))??;
1678 Poll::Ready(Ok(info))
1679 }
1680 QueryingKind::Spliced(resume) => resume.poll_info(waiter),
1681 }
1682 }
1683}
1684
1685impl kio::Pollable for Querying {
1686 type Output = Result<Info>;
1687
1688 fn poll(&self, waiter: &kio::Waiter) -> Poll<Self::Output> {
1689 self.poll_ok(waiter)
1690 }
1691}
1692
1693pub struct GroupRequest {
1702 state: kio::Producer<TrackState>,
1703 fetch: kio::Shared<FetchState>,
1705 sequence: u64,
1706 priority: u8,
1707 result: kio::Producer<FetchOutcome>,
1709 done: bool,
1710}
1711
1712impl GroupRequest {
1713 pub fn sequence(&self) -> u64 {
1715 self.sequence
1716 }
1717
1718 pub fn priority(&self) -> u8 {
1720 self.priority
1721 }
1722
1723 pub fn accept(mut self, info: impl Into<Option<Info>>) -> Result<group::Producer> {
1731 self.done = true;
1732 let res = TrackState::modify(&self.state)
1736 .and_then(|mut state| state.insert_group_request(self.sequence, info.into()));
1737 self.remove();
1738 res
1739 }
1740
1741 pub fn reject(mut self, err: Error) {
1743 self.done = true;
1744 self.remove();
1747 if let Ok(mut outcome) = self.result.write() {
1748 outcome.rejected = Some(err);
1749 }
1750 }
1751
1752 fn remove(&self) {
1755 self.fetch
1756 .lock()
1757 .remove_if(&self.sequence, |pending| pending.result.same_channel(&self.result));
1758 }
1759}
1760
1761impl Drop for GroupRequest {
1762 fn drop(&mut self) {
1763 if self.done {
1764 return;
1765 }
1766 self.remove();
1767 if let Ok(mut outcome) = self.result.write() {
1768 outcome.rejected = Some(Error::Dropped);
1769 }
1770 }
1771}
1772
1773pub struct Fetching {
1779 inner: FetchingKind,
1780 stats: stats::Scope,
1783}
1784
1785enum FetchingKind {
1786 Plain {
1787 state: kio::Consumer<TrackState>,
1788 fetch: kio::Shared<FetchState>,
1789 sequence: u64,
1790 result: Option<kio::Consumer<FetchOutcome>>,
1792 },
1793 Spliced(kio::Pending<super::resume::Fetching>),
1795}
1796
1797impl kio::Pollable for Fetching {
1798 type Output = Result<group::Consumer>;
1799
1800 fn poll(&self, waiter: &kio::Waiter) -> Poll<Self::Output> {
1801 let (state, fetch, sequence, result) = match &self.inner {
1802 FetchingKind::Plain {
1803 state,
1804 fetch,
1805 sequence,
1806 result,
1807 } => (state, fetch, *sequence, result.as_ref()),
1808 FetchingKind::Spliced(spliced) => {
1809 return kio::Pollable::poll(&**spliced, waiter)
1812 .map(|res| res.map(|group| group.with_meter(self.stats.meter())));
1813 }
1814 };
1815
1816 match state.poll(waiter, |state| state.poll_fetch_cached(sequence)) {
1819 Poll::Ready(Ok(res)) => return Poll::Ready(res.map(|group| group.with_meter(self.stats.meter()))),
1820 Poll::Ready(Err(closed)) => {
1821 return Poll::Ready(Err(closed.abort.clone().unwrap_or(Error::Dropped)));
1822 }
1823 Poll::Pending => {}
1824 }
1825
1826 let Some(result) = result else {
1828 return match fetch.poll(waiter, |fetch| match fetch.has_handlers() {
1831 false => Poll::Ready(()),
1832 true => Poll::Pending,
1833 }) {
1834 Poll::Ready(_guard) => Poll::Ready(Err(Error::NotFound)),
1835 Poll::Pending => Poll::Pending,
1836 };
1837 };
1838
1839 match result.poll(waiter, |outcome| match &outcome.rejected {
1842 Some(err) => Poll::Ready(err.clone()),
1843 None => Poll::Pending,
1844 }) {
1845 Poll::Ready(Ok(err)) => Poll::Ready(Err(err)),
1846 Poll::Ready(Err(_closed)) => Poll::Ready(Err(Error::NotFound)),
1847 Poll::Pending => Poll::Pending,
1848 }
1849 }
1850}
1851
1852pub struct Subscriber {
1875 name: Arc<str>,
1876 info: Info,
1877 inner: SubscriberKind,
1878 stats: stats::Scope,
1881 _stats_sub: stats::Subscription,
1884}
1885
1886enum SubscriberKind {
1887 Plain(PlainSubscriber),
1888 Spliced(Box<super::resume::Subscriber>),
1890}
1891
1892struct PlainSubscriber {
1894 state: kio::Consumer<TrackState>,
1895
1896 subscription: kio::Producer<Subscription>,
1897 index: usize,
1899 datagram_index: usize,
1901 min_sequence: u64,
1903 next_sequence: u64,
1906 end_sequence: Option<u64>,
1911}
1912
1913impl PlainSubscriber {
1914 fn poll<F, R>(&self, waiter: &kio::Waiter, f: F) -> Poll<Result<R>>
1916 where
1917 F: Fn(&kio::Ref<'_, TrackState>) -> Poll<Result<R>>,
1918 {
1919 Poll::Ready(match ready!(self.state.poll(waiter, f)) {
1920 Ok(res) => res,
1921 Err(state) => Err(state.abort.clone().unwrap_or(Error::Dropped)),
1923 })
1924 }
1925
1926 fn poll_recv_group(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<group::Consumer>>> {
1927 let Some((consumer, found_index)) =
1928 ready!(self.poll(waiter, |state| state.poll_recv_group(self.index, self.min_sequence))?)
1929 else {
1930 return Poll::Ready(Ok(None));
1931 };
1932
1933 self.index = found_index + 1;
1934 Poll::Ready(Ok(Some(consumer)))
1935 }
1936
1937 fn poll_recv_datagram(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<Datagram>>> {
1938 let Some((datagram, found_index)) =
1939 ready!(self.poll(waiter, |state| state.poll_recv_datagram(self.datagram_index))?)
1940 else {
1941 return Poll::Ready(Ok(None));
1942 };
1943
1944 self.datagram_index = found_index + 1;
1945 self.next_sequence = self.next_sequence.max(datagram.sequence.saturating_add(1));
1946 Poll::Ready(Ok(Some(datagram)))
1947 }
1948
1949 fn poll_next_group(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<group::Consumer>>> {
1950 let floor = self.next_sequence.max(self.min_sequence);
1951 let Some(group) = ready!(self.poll(waiter, |state| state.poll_next_in_range(floor, self.end_sequence))?) else {
1952 return Poll::Ready(Ok(None));
1953 };
1954 self.next_sequence = group.sequence.saturating_add(1);
1955 Poll::Ready(Ok(Some(group)))
1956 }
1957
1958 fn poll_read_frame(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<frame::Frame>>> {
1959 let lower = self.min_sequence.max(self.next_sequence);
1960 let Some((frame, found_index, sequence)) =
1961 ready!(self.poll(waiter, |state| { state.poll_read_frame(self.index, lower, waiter) })?)
1962 else {
1963 return Poll::Ready(Ok(None));
1964 };
1965
1966 self.index = found_index + 1;
1967 self.next_sequence = sequence.saturating_add(1);
1968 Poll::Ready(Ok(Some(frame)))
1969 }
1970}
1971
1972#[derive(Clone)]
1978pub struct SubscriberControl {
1979 subscription: kio::Producer<Subscription>,
1980}
1981
1982impl SubscriberControl {
1983 pub fn subscription(&self) -> Subscription {
1985 self.subscription.read().clone()
1986 }
1987
1988 pub fn update(&self, subscription: Subscription) -> Result<()> {
1993 let mut state = self.subscription.write().map_err(|_| Error::Closed)?;
1994 *state = subscription;
1995 Ok(())
1996 }
1997}
1998
1999impl Subscriber {
2000 pub fn info(&self) -> &Info {
2005 &self.info
2006 }
2007
2008 pub fn name(&self) -> &str {
2010 &self.name
2011 }
2012
2013 pub fn control(&self) -> SubscriberControl {
2015 SubscriberControl {
2016 subscription: match &self.inner {
2017 SubscriberKind::Plain(plain) => plain.subscription.clone(),
2018 SubscriberKind::Spliced(spliced) => spliced.prefs(),
2019 },
2020 }
2021 }
2022
2023 pub fn poll_recv_group(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<group::Consumer>>> {
2034 let meter = self.stats.meter();
2035 let res = match &mut self.inner {
2036 SubscriberKind::Plain(plain) => plain.poll_recv_group(waiter),
2037 SubscriberKind::Spliced(spliced) => spliced.poll_recv_group(waiter),
2038 };
2039 res.map(|res| res.map(|group| group.map(|group| group.with_meter(meter))))
2040 }
2041
2042 pub async fn recv_group(&mut self) -> Result<Option<group::Consumer>> {
2048 kio::wait(|waiter| self.poll_recv_group(waiter)).await
2049 }
2050
2051 pub fn poll_recv_datagram(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<Datagram>>> {
2062 let meter = self.stats.meter();
2063 let res = match &mut self.inner {
2064 SubscriberKind::Plain(plain) => plain.poll_recv_datagram(waiter),
2065 SubscriberKind::Spliced(spliced) => spliced.poll_recv_datagram(waiter),
2066 };
2067 if let Poll::Ready(Ok(Some(datagram))) = &res {
2070 meter.datagram(datagram.payload.len() as u64);
2071 }
2072 res
2073 }
2074
2075 pub async fn recv_datagram(&mut self) -> Result<Option<Datagram>> {
2082 kio::wait(|waiter| self.poll_recv_datagram(waiter)).await
2083 }
2084
2085 pub fn poll_next_group(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<group::Consumer>>> {
2094 let meter = self.stats.meter();
2095 let res = match &mut self.inner {
2096 SubscriberKind::Plain(plain) => plain.poll_next_group(waiter),
2097 SubscriberKind::Spliced(spliced) => spliced.poll_next_group(waiter),
2098 };
2099 res.map(|res| res.map(|group| group.map(|group| group.with_meter(meter))))
2100 }
2101
2102 pub async fn next_group(&mut self) -> Result<Option<group::Consumer>> {
2108 kio::wait(|waiter| self.poll_next_group(waiter)).await
2109 }
2110
2111 pub fn poll_read_frame(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<frame::Frame>>> {
2115 let meter = self.stats.meter();
2116 let res = match &mut self.inner {
2117 SubscriberKind::Plain(plain) => plain.poll_read_frame(waiter),
2118 SubscriberKind::Spliced(spliced) => spliced.poll_read_frame(waiter),
2119 };
2120 if let Poll::Ready(Ok(Some(frame))) = &res {
2123 meter.group();
2124 meter.frames(1);
2125 meter.bytes(frame.payload.len() as u64);
2126 }
2127 res
2128 }
2129
2130 pub async fn read_frame(&mut self) -> Result<Option<frame::Frame>> {
2135 kio::wait(|waiter| self.poll_read_frame(waiter)).await
2136 }
2137
2138 pub fn is_clone(&self, other: &Self) -> bool {
2140 match (&self.inner, &other.inner) {
2141 (SubscriberKind::Plain(a), SubscriberKind::Plain(b)) => a.state.same_channel(&b.state),
2142 (SubscriberKind::Spliced(a), SubscriberKind::Spliced(b)) => a.is_clone(b),
2143 _ => false,
2144 }
2145 }
2146
2147 pub fn poll_finished(&mut self, waiter: &kio::Waiter) -> Poll<Result<u64>> {
2149 match &mut self.inner {
2150 SubscriberKind::Plain(plain) => plain.poll(waiter, |state| state.poll_finished()),
2151 SubscriberKind::Spliced(spliced) => spliced.poll_finished(waiter),
2152 }
2153 }
2154
2155 pub async fn finished(&mut self) -> Result<u64> {
2163 kio::wait(|waiter| self.poll_finished(waiter)).await
2164 }
2165
2166 pub fn start_at(&mut self, sequence: u64) {
2173 match &mut self.inner {
2174 SubscriberKind::Plain(plain) => plain.min_sequence = sequence,
2175 SubscriberKind::Spliced(spliced) => spliced.start_at(sequence),
2176 }
2177 }
2178
2179 pub fn end_at(&mut self, sequence: impl Into<Option<u64>>) {
2192 match &mut self.inner {
2193 SubscriberKind::Plain(plain) => plain.end_sequence = sequence.into(),
2194 SubscriberKind::Spliced(spliced) => spliced.end_at(sequence),
2195 }
2196 }
2197
2198 pub fn subscription(&self) -> Subscription {
2200 self.control().subscription()
2201 }
2202
2203 pub fn update(&mut self, subscription: Subscription) -> Result<()> {
2209 match &mut self.inner {
2210 SubscriberKind::Plain(plain) => {
2211 let mut state = plain.subscription.write().map_err(|_| Error::Closed)?;
2212 *state = subscription;
2213 }
2214 SubscriberKind::Spliced(spliced) => spliced.update(subscription),
2215 }
2216 Ok(())
2217 }
2218
2219 pub fn latest(&self) -> Option<u64> {
2221 match &self.inner {
2222 SubscriberKind::Plain(plain) => plain.state.read().max_sequence,
2223 SubscriberKind::Spliced(spliced) => spliced.latest(),
2224 }
2225 }
2226}
2227
2228pub struct Request {
2240 name: Arc<str>,
2241 broadcast: Arc<broadcast::Info>,
2243 state: kio::Producer<TrackState>,
2244
2245 prev_subscription: Option<Subscription>,
2247
2248 _dynamic: Dynamic,
2253
2254 stats: stats::Scope,
2257}
2258
2259impl Request {
2260 pub(crate) fn new(broadcast: Arc<broadcast::Info>, name: impl Into<Arc<str>>) -> Self {
2261 let name = name.into();
2262 let state = kio::Producer::new(TrackState {
2263 broadcast: broadcast.clone(),
2264 ..Default::default()
2265 });
2266 let dynamic = Dynamic::new(name.clone(), state.clone());
2267 Self {
2268 name,
2269 broadcast,
2270 state,
2271 prev_subscription: None,
2272 _dynamic: dynamic,
2273 stats: stats::Scope::default(),
2274 }
2275 }
2276
2277 pub(crate) fn with_stats(mut self, scope: stats::Scope) -> Self {
2280 self.stats = scope;
2281 self
2282 }
2283
2284 pub fn name(&self) -> &str {
2286 &self.name
2287 }
2288
2289 pub fn consume(&self) -> Consumer {
2291 Consumer::plain(self.name.clone(), self.state.consume())
2292 }
2293
2294 pub fn dynamic(&self) -> Dynamic {
2298 Dynamic::new(self.name.clone(), self.state.clone())
2299 }
2300
2301 pub fn poll_unused(&self, waiter: &kio::Waiter) -> Poll<()> {
2304 self.state.poll_unused(waiter).map(|_| ())
2305 }
2306
2307 pub fn accept(self, info: impl Into<Option<Info>>) -> Producer {
2314 let mut info = info.into().unwrap_or_default();
2315 info.bind(self.broadcast.clone());
2316 if let Ok(mut state) = self.state.write() {
2319 state.info = Some(info);
2320 }
2321 self.stats.open_subscription();
2324 Producer {
2325 name: self.name,
2326 broadcast: self.broadcast,
2327 state: self.state,
2328 prev_subscription: None,
2329 stats: self.stats,
2330 }
2331 }
2332
2333 pub fn reject(self, err: Error) {
2335 if let Ok(mut state) = self.state.write() {
2336 state.abort = Some(err);
2337 }
2338 }
2339
2340 pub fn subscription(&self) -> Option<Subscription> {
2343 let state = self.state.read();
2344 let (subs, bound) = (state.subscriptions.clone(), state.latency_bound());
2345 drop(state);
2346 snapshot_subscription(&subs, bound)
2347 }
2348
2349 pub async fn subscription_changed(&mut self) -> Option<Subscription> {
2352 kio::wait(|waiter| self.poll_subscription_changed(waiter)).await
2353 }
2354
2355 pub fn poll_subscription_changed(&mut self, waiter: &kio::Waiter) -> Poll<Option<Subscription>> {
2357 let state = self.state.read();
2358 let (subs, bound) = (state.subscriptions.clone(), state.latency_bound());
2359 drop(state);
2360
2361 let prev = &self.prev_subscription;
2362 let mut combined = None;
2363 let mut guard = ready!(subs.poll(waiter, |subs| {
2364 let next = combined_subscription(subs, bound, waiter);
2365 if &next == prev {
2366 Poll::Pending
2367 } else {
2368 combined = next;
2369 Poll::Ready(())
2370 }
2371 }));
2372 guard.retain(|sub| !sub.is_closed());
2374 drop(guard);
2375 self.prev_subscription = combined.clone();
2376 Poll::Ready(combined)
2377 }
2378
2379 pub(super) fn weak(&self) -> TrackWeak {
2380 TrackWeak {
2381 name: self.name.clone(),
2382 state: self.state.weak(),
2383 }
2384 }
2385}
2386
2387#[cfg(test)]
2388use futures::FutureExt;
2389
2390#[cfg(test)]
2391#[allow(missing_docs)] impl Subscriber {
2393 pub fn assert_group(&mut self) -> group::Consumer {
2394 self.recv_group()
2395 .now_or_never()
2396 .expect("group would have blocked")
2397 .expect("would have errored")
2398 .expect("track was closed")
2399 }
2400
2401 pub fn assert_no_group(&mut self) {
2402 assert!(
2403 self.recv_group().now_or_never().is_none(),
2404 "recv_group would not have blocked"
2405 );
2406 }
2407
2408 pub fn assert_not_closed(&mut self) {
2409 assert!(self.finished().now_or_never().is_none(), "should not be closed");
2410 }
2411
2412 pub fn assert_closed(&mut self) {
2413 assert!(self.finished().now_or_never().is_some(), "should be closed");
2414 }
2415
2416 pub fn assert_error(&mut self) {
2418 assert!(
2419 self.finished().now_or_never().expect("should not block").is_err(),
2420 "should be error"
2421 );
2422 }
2423
2424 pub fn assert_is_clone(&self, other: &Self) {
2425 assert!(self.is_clone(other), "should be clone");
2426 }
2427
2428 pub fn assert_not_clone(&self, other: &Self) {
2429 assert!(!self.is_clone(other), "should not be clone");
2430 }
2431}
2432
2433#[cfg(test)]
2434mod test {
2435 use super::*;
2436
2437 fn track_producer(name: impl Into<Arc<str>>, info: impl Into<Option<Info>>) -> Producer {
2440 Producer::new(Arc::new(broadcast::Info::default()), name, info)
2441 }
2442
2443 fn live_groups(state: &TrackState) -> usize {
2445 state.groups.iter().flatten().count()
2446 }
2447
2448 fn first_live_sequence(state: &TrackState) -> u64 {
2450 state.groups.iter().flatten().next().unwrap().0.sequence
2451 }
2452
2453 fn recv_datagram(dg: &mut Subscriber) -> Datagram {
2455 dg.recv_datagram()
2456 .now_or_never()
2457 .expect("datagram would have blocked")
2458 .expect("would have errored")
2459 .expect("track was closed")
2460 }
2461
2462 #[tokio::test]
2463 async fn append_datagram_shares_group_sequence() {
2464 let mut producer = track_producer("test", None);
2465 let ts = Timestamp::from_millis(10).unwrap();
2466
2467 assert_eq!(producer.append_group().unwrap().sequence, 0);
2469 assert_eq!(producer.append_datagram(ts, &b"a"[..]).unwrap(), 1);
2470 assert_eq!(producer.append_group().unwrap().sequence, 2);
2471 assert_eq!(producer.append_datagram(ts, &b"b"[..]).unwrap(), 3);
2472 assert_eq!(producer.latest(), Some(3));
2473 }
2474
2475 #[tokio::test]
2476 async fn append_datagram_roundtrip() {
2477 let mut producer = track_producer("test", None);
2478 let mut dg = producer.subscribe(None);
2479
2480 let ts = Timestamp::from_millis(42).unwrap();
2481 let seq = producer.append_datagram(ts, &b"hello"[..]).unwrap();
2482
2483 let got = recv_datagram(&mut dg);
2484 assert_eq!(got.sequence, seq);
2485 assert_eq!(got.timestamp, ts);
2486 assert_eq!(&got.payload[..], b"hello");
2487 }
2488
2489 #[tokio::test]
2490 async fn write_datagram_preserves_sequence() {
2491 let mut producer = track_producer("test", None);
2492 let mut dg = producer.subscribe(None);
2493
2494 let ts = Timestamp::from_millis(5).unwrap();
2495 producer
2497 .write_datagram(Datagram {
2498 sequence: 100,
2499 timestamp: ts,
2500 payload: bytes::Bytes::from_static(b"x"),
2501 })
2502 .unwrap();
2503
2504 assert_eq!(recv_datagram(&mut dg).sequence, 100);
2505 assert_eq!(producer.append_group().unwrap().sequence, 101);
2507 }
2508
2509 #[tokio::test]
2510 async fn recv_datagram_advances_ordered_group_cursor() {
2511 let mut producer = track_producer("test", None);
2512 let mut subscriber = producer.subscribe(None);
2513 let ts = Timestamp::from_millis(5).unwrap();
2514
2515 producer
2516 .write_datagram(Datagram {
2517 sequence: 5,
2518 timestamp: ts,
2519 payload: bytes::Bytes::from_static(b"x"),
2520 })
2521 .unwrap();
2522 assert_eq!(recv_datagram(&mut subscriber).sequence, 5);
2523
2524 producer.create_group(group::Info { sequence: 3 }).unwrap();
2525 producer.create_group(group::Info { sequence: 6 }).unwrap();
2526
2527 let group = subscriber
2528 .next_group()
2529 .now_or_never()
2530 .expect("group would have blocked")
2531 .expect("would have errored")
2532 .expect("track was closed");
2533 assert_eq!(group.sequence, 6);
2534 }
2535
2536 #[tokio::test]
2537 async fn datagram_normalized_to_track_timescale() {
2538 let info = Info::default().with_timescale(Timescale::MICRO);
2539 let mut producer = track_producer("test", info);
2540 let mut dg = producer.subscribe(None);
2541
2542 producer
2544 .append_datagram(Timestamp::from_millis(2).unwrap(), &b"z"[..])
2545 .unwrap();
2546 let got = recv_datagram(&mut dg);
2547 assert_eq!(got.timestamp.scale(), Timescale::MICRO);
2548 assert_eq!(got.timestamp.value(), 2_000);
2549 }
2550
2551 #[tokio::test]
2552 async fn datagram_rejects_oversized() {
2553 let mut producer = track_producer("test", None);
2554 let big = bytes::Bytes::from(vec![0u8; crate::model::datagram::MAX_DATAGRAM_PAYLOAD + 1]);
2555 let ts = Timestamp::from_millis(0).unwrap();
2556 assert!(matches!(
2557 producer.append_datagram(ts, big.clone()),
2558 Err(Error::FrameTooLarge)
2559 ));
2560 assert!(matches!(
2561 producer.write_datagram(Datagram {
2562 sequence: 0,
2563 timestamp: ts,
2564 payload: big,
2565 }),
2566 Err(Error::FrameTooLarge)
2567 ));
2568 }
2569
2570 #[tokio::test]
2571 async fn datagram_fanout_to_subscribers() {
2572 let mut producer = track_producer("test", None);
2573 let mut a = producer.subscribe(None);
2575 let mut b = producer.subscribe(None);
2576 let ts = Timestamp::from_millis(1).unwrap();
2577
2578 producer.append_datagram(ts, &b"first"[..]).unwrap();
2579 producer.append_datagram(ts, &b"second"[..]).unwrap();
2580
2581 assert_eq!(&recv_datagram(&mut a).payload[..], b"first");
2583 assert_eq!(&recv_datagram(&mut a).payload[..], b"second");
2584 assert_eq!(&recv_datagram(&mut b).payload[..], b"first");
2585 assert_eq!(&recv_datagram(&mut b).payload[..], b"second");
2586 }
2587
2588 #[tokio::test]
2589 async fn datagram_evicts_stale() {
2590 tokio::time::pause();
2591
2592 let mut producer = track_producer("test", None);
2593 let mut dg = producer.subscribe(None);
2594 let ts = Timestamp::from_millis(0).unwrap();
2595
2596 producer.append_datagram(ts, &b"old"[..]).unwrap(); tokio::time::advance(MAX_DATAGRAM_AGE + Duration::from_millis(10)).await;
2600 producer.append_datagram(ts, &b"new"[..]).unwrap(); let got = recv_datagram(&mut dg);
2604 assert_eq!(got.sequence, 1);
2605 assert_eq!(&got.payload[..], b"new");
2606 }
2607
2608 #[tokio::test]
2609 async fn datagram_recv_pends_until_written() {
2610 let mut producer = track_producer("test", None);
2611 let mut dg = producer.subscribe(None);
2612
2613 assert!(
2614 dg.recv_datagram().now_or_never().is_none(),
2615 "should block with no datagrams"
2616 );
2617
2618 producer
2619 .append_datagram(Timestamp::from_millis(0).unwrap(), &b"go"[..])
2620 .unwrap();
2621 assert_eq!(&recv_datagram(&mut dg).payload[..], b"go");
2622 }
2623
2624 #[tokio::test]
2628 async fn datagram_wire_roundtrip_between_tracks() {
2629 use crate::coding::{Decode, Encode};
2630 use crate::lite;
2631
2632 let version = lite::Version::Lite05;
2633
2634 let mut origin = track_producer("test", None);
2636 let mut origin_dg = origin.subscribe(None);
2637 let ts = Timestamp::from_millis(7).unwrap();
2638 let seq = origin.append_datagram(ts, &b"payload"[..]).unwrap();
2639
2640 let d = recv_datagram(&mut origin_dg);
2641 let body = lite::Datagram {
2642 subscribe: 5,
2643 sequence: d.sequence,
2644 timestamp: d.timestamp.value(),
2645 payload: d.payload.clone(),
2646 }
2647 .encode_bytes(version)
2648 .unwrap();
2649
2650 let mut slice = &body[..];
2652 let wire = lite::Datagram::decode(&mut slice, version).unwrap();
2653 let mut downstream = track_producer("test", None);
2654 let mut downstream_dg = downstream.subscribe(None);
2655 downstream
2656 .write_datagram(Datagram {
2657 sequence: wire.sequence,
2658 timestamp: Timestamp::new(wire.timestamp, Timescale::MILLI).unwrap(),
2659 payload: wire.payload,
2660 })
2661 .unwrap();
2662
2663 let got = recv_datagram(&mut downstream_dg);
2664 assert_eq!(got.sequence, seq);
2665 assert_eq!(got.timestamp, ts);
2666 assert_eq!(&got.payload[..], b"payload");
2667 }
2668
2669 #[tokio::test]
2670 async fn evict_expired_groups() {
2671 tokio::time::pause();
2672
2673 let mut producer = track_producer("test", None);
2674
2675 producer.append_group().unwrap(); producer.append_group().unwrap(); producer.append_group().unwrap(); {
2681 let state = producer.state.read();
2682 assert_eq!(live_groups(&state), 3);
2683 assert_eq!(state.offset, 0);
2684 }
2685
2686 tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
2688
2689 producer.append_group().unwrap(); {
2695 let state = producer.state.read();
2696 assert_eq!(live_groups(&state), 1);
2697 assert_eq!(first_live_sequence(&state), 3);
2698 assert_eq!(state.offset, 3);
2699 assert!(!state.duplicates.contains(&0));
2700 assert!(!state.duplicates.contains(&1));
2701 assert!(!state.duplicates.contains(&2));
2702 assert!(state.duplicates.contains(&3));
2703 }
2704 }
2705
2706 #[tokio::test]
2710 async fn aging_out_a_finished_group_keeps_the_clean_end() {
2711 tokio::time::pause();
2712
2713 let mut producer = track_producer("test", None);
2714 let mut group = producer.create_group(group::Info { sequence: 0 }).unwrap();
2715 let mut consumer = group.consume();
2716
2717 group
2718 .write_frame(Timestamp::from_millis(0).unwrap(), b"hello".as_slice())
2719 .unwrap();
2720 assert_eq!(consumer.next_frame().await.unwrap().unwrap().size, 5);
2721
2722 tokio::time::advance(DEFAULT_LATENCY_MAX * 12).await;
2724 group.finish().unwrap();
2725 let _next = producer.create_group(group::Info { sequence: 1 }).unwrap();
2726
2727 assert!(consumer.next_frame().await.unwrap().is_none());
2728 }
2729
2730 #[tokio::test]
2731 async fn evict_keeps_max_sequence() {
2732 tokio::time::pause();
2733
2734 let mut producer = track_producer("test", None);
2735 producer.append_group().unwrap(); tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
2739
2740 producer.append_group().unwrap(); {
2744 let state = producer.state.read();
2745 assert_eq!(live_groups(&state), 1);
2746 assert_eq!(first_live_sequence(&state), 1);
2747 assert_eq!(state.offset, 1);
2748 }
2749 }
2750
2751 #[tokio::test]
2752 async fn no_eviction_when_fresh() {
2753 tokio::time::pause();
2754
2755 let mut producer = track_producer("test", None);
2756 producer.append_group().unwrap(); producer.append_group().unwrap(); producer.append_group().unwrap(); {
2761 let state = producer.state.read();
2762 assert_eq!(live_groups(&state), 3);
2763 assert_eq!(state.offset, 0);
2764 }
2765 }
2766
2767 #[tokio::test]
2768 async fn consumer_skips_evicted_groups() {
2769 tokio::time::pause();
2770
2771 let mut producer = track_producer("test", None);
2772 producer.append_group().unwrap(); let mut consumer = producer.subscribe(None);
2775
2776 tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
2777 producer.append_group().unwrap(); let group = consumer.assert_group();
2781 assert_eq!(group.sequence, 1);
2782 }
2783
2784 #[tokio::test]
2785 async fn cache_age_controls_eviction() {
2786 tokio::time::pause();
2787
2788 let mut producer = track_producer("test", Info::default().with_latency_max(Duration::from_secs(1)));
2790 producer.append_group().unwrap(); tokio::time::advance(Duration::from_secs(2)).await;
2794 producer.append_group().unwrap(); let state = producer.state.read();
2798 assert_eq!(live_groups(&state), 1);
2799 assert_eq!(first_live_sequence(&state), 1);
2800 }
2801
2802 #[test]
2803 fn latency_max_clamped_to_cache() {
2804 let producer = track_producer("test", Info::default().with_latency_max(Duration::from_secs(2)));
2805
2806 let mut subscriber = producer.subscribe(Subscription::default().with_latency_max(Duration::from_secs(10)));
2810 assert_eq!(subscriber.subscription().latency_max, Duration::from_secs(10));
2811 assert_eq!(producer.subscription().unwrap().latency_max, Duration::from_secs(2));
2812
2813 subscriber
2815 .update(Subscription::default().with_latency_max(Duration::from_millis(500)))
2816 .unwrap();
2817 assert_eq!(producer.subscription().unwrap().latency_max, Duration::from_millis(500));
2818
2819 subscriber
2820 .update(Subscription::default().with_latency_max(Duration::ZERO))
2821 .unwrap();
2822 assert_eq!(producer.subscription().unwrap().latency_max, Duration::ZERO);
2823 }
2824
2825 fn track_producer_capped(name: impl Into<Arc<str>>, info: Info, cap: Duration) -> Producer {
2828 let origin = crate::origin::Info::default().with_cache_duration(cap);
2829 Producer::new(Arc::new(broadcast::Info { origin }), name, info)
2830 }
2831
2832 #[test]
2833 fn origin_cache_duration_clamps_latency_max() {
2834 let capped = track_producer_capped(
2837 "test",
2838 Info::default().with_latency_max(Duration::from_secs(60)),
2839 Duration::from_secs(1),
2840 );
2841 assert_eq!(capped.state.read().latency_bound(), Some(Duration::from_secs(1)));
2842
2843 let under = track_producer_capped(
2844 "test",
2845 Info::default().with_latency_max(Duration::from_millis(500)),
2846 Duration::from_secs(1),
2847 );
2848 assert_eq!(under.state.read().latency_bound(), Some(Duration::from_millis(500)));
2849 }
2850
2851 #[tokio::test]
2852 async fn origin_cache_duration_caps_eviction() {
2853 tokio::time::pause();
2854
2855 let mut producer = track_producer_capped(
2857 "test",
2858 Info::default().with_latency_max(Duration::from_secs(60)),
2859 Duration::from_secs(1),
2860 );
2861 producer.append_group().unwrap(); tokio::time::advance(Duration::from_secs(2)).await;
2865 producer.append_group().unwrap(); let state = producer.state.read();
2869 assert_eq!(live_groups(&state), 1);
2870 assert_eq!(first_live_sequence(&state), 1);
2871 }
2872
2873 #[test]
2874 fn latency_max_clamped_via_every_update_path() {
2875 let producer = track_producer("test", Info::default().with_latency_max(Duration::from_secs(2)));
2876 let over = Subscription::default().with_latency_max(Duration::from_secs(10));
2877
2878 let mut subscriber = producer.subscribe(over.clone());
2881 assert_eq!(producer.subscription().unwrap().latency_max, Duration::from_secs(2));
2882
2883 subscriber.control().update(over.clone()).unwrap();
2884 assert_eq!(producer.subscription().unwrap().latency_max, Duration::from_secs(2));
2885
2886 subscriber.update(over).unwrap();
2887 assert_eq!(producer.subscription().unwrap().latency_max, Duration::from_secs(2));
2888 }
2889
2890 #[test]
2891 fn latency_max_aggregate_clamps_the_max_across_subscribers() {
2892 let producer = track_producer("test", Info::default().with_latency_max(Duration::from_secs(2)));
2893
2894 let _a = producer.subscribe(Subscription::default().with_latency_max(Duration::from_millis(500)));
2897 let _b = producer.subscribe(Subscription::default().with_latency_max(Duration::from_secs(10)));
2898
2899 assert_eq!(producer.subscription().unwrap().latency_max, Duration::from_secs(2));
2900 }
2901
2902 #[test]
2903 fn subscriber_control_updates_while_read_future_is_pending() {
2904 let producer = track_producer("test", None);
2905 let mut subscriber = producer.subscribe(None);
2906 let control = subscriber.control();
2907
2908 let mut recv = Box::pin(subscriber.recv_group());
2909 assert!(recv.as_mut().now_or_never().is_none());
2910
2911 control
2912 .update(Subscription::default().with_priority(7).with_ordered(false))
2913 .unwrap();
2914
2915 let aggregate = producer.subscription().expect("expected an active subscription");
2916 assert_eq!(aggregate.priority, 7);
2917 assert!(!aggregate.ordered);
2918 }
2919
2920 #[test]
2921 fn dropped_subscriber_leaves_no_ghost_in_aggregate() {
2922 let mut producer = track_producer("test", None);
2927 let a = producer.subscribe(Subscription::default().with_priority(5));
2928
2929 let waiter = kio::Waiter::noop();
2931 assert!(
2932 matches!(producer.poll_subscription_changed(&waiter), Poll::Ready(Ok(Some(_)))),
2933 "one live subscriber should aggregate to Some",
2934 );
2935
2936 drop(a);
2938
2939 assert!(
2941 matches!(producer.poll_subscription_changed(&waiter), Poll::Ready(Ok(None))),
2942 "a dropped subscriber must not linger in the aggregate",
2943 );
2944
2945 assert!(
2947 producer.subscription().is_none(),
2948 "snapshot must exclude a dropped subscriber",
2949 );
2950 }
2951
2952 #[test]
2953 fn dropped_subscriber_wakes_the_aggregate() {
2954 use std::sync::atomic::{AtomicBool, Ordering};
2961
2962 let mut producer = track_producer("test", None);
2963 let a = producer.subscribe(Subscription::default().with_priority(5));
2964
2965 let woken = Arc::new(AtomicBool::new(false));
2966 let waiter = kio::Waiter::new(futures::task::waker(Arc::new(FlagWake(woken.clone()))));
2967
2968 assert!(matches!(
2970 producer.poll_subscription_changed(&waiter),
2971 Poll::Ready(Ok(Some(_)))
2972 ));
2973 assert!(
2974 producer.poll_subscription_changed(&waiter).is_pending(),
2975 "the aggregate is unchanged, so this poll must park",
2976 );
2977 assert!(!woken.load(Ordering::SeqCst), "nothing happened yet");
2978
2979 drop(a);
2980 assert!(
2981 woken.load(Ordering::SeqCst),
2982 "the last subscriber leaving must wake the aggregate watcher",
2983 );
2984 }
2985
2986 struct FlagWake(Arc<std::sync::atomic::AtomicBool>);
2988
2989 impl futures::task::ArcWake for FlagWake {
2990 fn wake_by_ref(arc_self: &Arc<Self>) {
2991 arc_self.0.store(true, std::sync::atomic::Ordering::SeqCst);
2992 }
2993 }
2994
2995 #[tokio::test]
2996 async fn out_of_order_max_sequence_at_front() {
2997 tokio::time::pause();
2998
2999 let mut producer = track_producer("test", None);
3000
3001 producer.create_group(group::Info { sequence: 5 }).unwrap();
3003 producer.create_group(group::Info { sequence: 3 }).unwrap();
3004 producer.create_group(group::Info { sequence: 4 }).unwrap();
3005
3006 {
3008 let state = producer.state.read();
3009 assert_eq!(state.max_sequence, Some(5));
3010 }
3011
3012 tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
3014
3015 producer.append_group().unwrap(); {
3021 let state = producer.state.read();
3022 assert_eq!(live_groups(&state), 1);
3023 assert_eq!(first_live_sequence(&state), 6);
3024 assert!(!state.duplicates.contains(&3));
3025 assert!(!state.duplicates.contains(&4));
3026 assert!(!state.duplicates.contains(&5));
3027 assert!(state.duplicates.contains(&6));
3028 }
3029 }
3030
3031 #[tokio::test]
3032 async fn max_sequence_at_front_blocks_trim() {
3033 tokio::time::pause();
3034
3035 let mut producer = track_producer("test", None);
3036
3037 producer.create_group(group::Info { sequence: 5 }).unwrap();
3039
3040 tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
3041
3042 producer.create_group(group::Info { sequence: 3 }).unwrap();
3044
3045 {
3048 let state = producer.state.read();
3049 assert_eq!(live_groups(&state), 2);
3050 assert_eq!(state.offset, 0);
3051 }
3052
3053 tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
3055
3056 producer.create_group(group::Info { sequence: 2 }).unwrap();
3058
3059 {
3064 let state = producer.state.read();
3065 assert_eq!(live_groups(&state), 2);
3066 assert_eq!(state.offset, 0);
3067 assert!(state.duplicates.contains(&5));
3068 assert!(!state.duplicates.contains(&3));
3069 assert!(state.duplicates.contains(&2));
3070 }
3071
3072 let mut consumer = producer.subscribe(None);
3074 let group = consumer.assert_group();
3075 assert_eq!(group.sequence, 5);
3077 }
3078
3079 #[tokio::test]
3080 async fn abort_clears_cached_groups() {
3081 let mut producer = track_producer("test", None);
3082 producer.append_group().unwrap();
3083 producer.append_group().unwrap();
3084
3085 let mut consumer = producer.subscribe(None);
3087 assert_eq!(live_groups(&producer.state.read()), 2);
3088
3089 producer.clone().abort(Error::Cancel).unwrap();
3090
3091 {
3092 let state = producer.state.read();
3093 assert!(state.groups.is_empty(), "cached groups should be dropped on abort");
3094 assert!(state.duplicates.is_empty());
3095 }
3096
3097 let result = consumer.recv_group().now_or_never().expect("should not block");
3099 assert!(matches!(result, Err(Error::Cancel)));
3100 }
3101
3102 #[tokio::test]
3103 async fn drop_unfinished_clears_cached_groups() {
3104 let producer = track_producer("test", None);
3105 let mut writer = producer.clone();
3106 writer.append_group().unwrap();
3107
3108 let mut consumer = producer.subscribe(None);
3110 assert_eq!(live_groups(&producer.state.read()), 1);
3111
3112 drop(writer);
3114 drop(producer);
3115
3116 let result = consumer.recv_group().now_or_never().expect("should not block");
3117 assert!(matches!(result, Err(Error::Dropped)));
3118 }
3119
3120 #[tokio::test]
3121 async fn drop_finished_keeps_cached_groups() {
3122 let mut producer = track_producer("test", None);
3123 producer.append_group().unwrap();
3124 producer.finish().unwrap();
3125
3126 let mut consumer = producer.subscribe(None);
3127 drop(producer);
3128
3129 assert_eq!(consumer.assert_group().sequence, 0);
3131 let done = consumer.recv_group().now_or_never().expect("should not block").unwrap();
3132 assert!(done.is_none(), "consumer should drain then see clean finish");
3133 }
3134
3135 #[test]
3136 fn append_finish_cannot_be_rewritten() {
3137 let mut producer = track_producer("test", None);
3138
3139 assert!(producer.finish().is_ok());
3141 assert!(producer.finish().is_err());
3142 assert!(producer.append_group().is_err());
3143 }
3144
3145 #[test]
3146 fn finish_after_groups() {
3147 let mut producer = track_producer("test", None);
3148
3149 producer.append_group().unwrap();
3150 assert!(producer.finish().is_ok());
3151 assert!(producer.finish().is_err());
3152 assert!(producer.append_group().is_err());
3153 }
3154
3155 #[test]
3156 fn finish_at_rejects_a_boundary_at_or_below_the_live_edge() {
3157 let mut producer = track_producer("test", None);
3158 producer.create_group(group::Info { sequence: 5 }).unwrap();
3159
3160 assert!(producer.finish_at(4).is_err());
3163 assert!(producer.finish_at(5).is_err());
3164 assert!(producer.finish_at(6).is_ok());
3165
3166 {
3167 let state = producer.state.read();
3168 assert_eq!(state.final_sequence, Some(6));
3169 }
3170
3171 assert!(producer.finish_at(6).is_err());
3173 assert!(producer.create_group(group::Info { sequence: 4 }).is_ok());
3174 assert!(producer.create_group(group::Info { sequence: 6 }).is_err());
3175 }
3176
3177 #[test]
3178 fn final_sequence_reports_the_declared_boundary() {
3179 let mut producer = track_producer("test", None);
3180 assert_eq!(producer.final_sequence(), None);
3181
3182 producer.create_group(group::Info { sequence: 5 }).unwrap();
3183 assert_eq!(producer.final_sequence(), None, "a group does not declare a boundary");
3184
3185 producer.finish_at(9).unwrap();
3186 assert_eq!(producer.final_sequence(), Some(9));
3187
3188 assert!(producer.finish().is_err());
3190 }
3191
3192 #[test]
3193 fn final_sequence_reports_the_live_edge_after_finish() {
3194 let mut producer = track_producer("test", None);
3195 producer.create_group(group::Info { sequence: 5 }).unwrap();
3196 producer.finish().unwrap();
3197 assert_eq!(producer.final_sequence(), Some(6));
3198 }
3199
3200 #[tokio::test]
3201 async fn finish_at_declares_a_future_boundary() {
3202 let mut producer = track_producer("test", None);
3203 producer.create_group(group::Info { sequence: 5 }).unwrap();
3204
3205 producer.finish_at(7).unwrap();
3207
3208 let mut consumer = producer.subscribe(None);
3209 assert_eq!(consumer.assert_group().sequence, 5);
3210
3211 let boundary = consumer
3214 .finished()
3215 .now_or_never()
3216 .expect("boundary is known immediately")
3217 .expect("would have errored");
3218 assert_eq!(boundary, 7);
3219 assert!(
3220 consumer.recv_group().now_or_never().is_none(),
3221 "should wait for the outstanding group"
3222 );
3223
3224 producer.create_group(group::Info { sequence: 6 }).unwrap();
3226 assert_eq!(consumer.assert_group().sequence, 6);
3227 let done = consumer
3228 .recv_group()
3229 .now_or_never()
3230 .expect("should not block")
3231 .expect("would have errored");
3232 assert!(done.is_none(), "track completes once the boundary is reached");
3233 }
3234
3235 #[tokio::test]
3236 async fn recv_group_finishes_without_waiting_for_gaps() {
3237 let mut producer = track_producer("test", None);
3238 producer.create_group(group::Info { sequence: 1 }).unwrap();
3239 producer.finish().unwrap();
3240
3241 let mut consumer = producer.subscribe(None);
3242 assert_eq!(consumer.assert_group().sequence, 1);
3243
3244 let done = consumer
3245 .recv_group()
3246 .now_or_never()
3247 .expect("should not block")
3248 .expect("would have errored");
3249 assert!(done.is_none(), "track should finish without waiting for gaps");
3250 }
3251
3252 #[tokio::test]
3253 async fn next_group_skips_late_arrivals() {
3254 let mut producer = track_producer("test", None);
3255 let mut consumer = producer.subscribe(None);
3256
3257 producer.create_group(group::Info { sequence: 5 }).unwrap();
3259 let group = consumer
3260 .next_group()
3261 .now_or_never()
3262 .expect("should not block")
3263 .expect("would have errored")
3264 .expect("track should not be closed");
3265 assert_eq!(group.sequence, 5);
3266
3267 producer.create_group(group::Info { sequence: 3 }).unwrap();
3269 producer.create_group(group::Info { sequence: 4 }).unwrap();
3271 producer.create_group(group::Info { sequence: 7 }).unwrap();
3273
3274 let group = consumer
3275 .next_group()
3276 .now_or_never()
3277 .expect("should not block")
3278 .expect("would have errored")
3279 .expect("track should not be closed");
3280 assert_eq!(group.sequence, 7);
3281
3282 assert!(
3284 consumer.next_group().now_or_never().is_none(),
3285 "should block waiting for a higher sequence"
3286 );
3287 }
3288
3289 #[tokio::test]
3290 async fn next_group_returns_arrivals_in_order() {
3291 let mut producer = track_producer("test", None);
3292 let mut consumer = producer.subscribe(None);
3293
3294 producer.create_group(group::Info { sequence: 3 }).unwrap();
3296 producer.create_group(group::Info { sequence: 5 }).unwrap();
3297
3298 let group = consumer
3299 .next_group()
3300 .now_or_never()
3301 .expect("should not block")
3302 .expect("would have errored")
3303 .expect("track should not be closed");
3304 assert_eq!(group.sequence, 3);
3305
3306 let group = consumer
3307 .next_group()
3308 .now_or_never()
3309 .expect("should not block")
3310 .expect("would have errored")
3311 .expect("track should not be closed");
3312 assert_eq!(group.sequence, 5);
3313 }
3314
3315 #[tokio::test]
3316 async fn next_group_and_recv_group_use_independent_cursors() {
3317 let mut producer = track_producer("test", None);
3318 let mut consumer = producer.subscribe(None);
3319
3320 producer.create_group(group::Info { sequence: 5 }).unwrap();
3322 producer.create_group(group::Info { sequence: 3 }).unwrap();
3323
3324 let group = consumer
3327 .next_group()
3328 .now_or_never()
3329 .expect("should not block")
3330 .expect("would have errored")
3331 .expect("track should not be closed");
3332 assert_eq!(group.sequence, 3);
3333
3334 assert_eq!(consumer.assert_group().sequence, 5);
3337 }
3338
3339 #[tokio::test]
3340 async fn end_at_caps_next_group() {
3341 let mut producer = track_producer("test", None);
3342 let mut consumer = producer.subscribe(None);
3343
3344 for s in 0..6 {
3345 producer.create_group(group::Info { sequence: s }).unwrap();
3346 }
3347
3348 consumer.end_at(2);
3349
3350 assert_eq!(
3352 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3353 0
3354 );
3355 assert_eq!(
3356 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3357 1
3358 );
3359 assert_eq!(
3360 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3361 2
3362 );
3363
3364 assert!(
3366 consumer.next_group().now_or_never().is_none(),
3367 "capped consumer must block instead of returning out-of-range groups"
3368 );
3369 }
3370
3371 #[tokio::test]
3372 async fn end_at_release_drains_cached_groups() {
3373 let mut producer = track_producer("test", None);
3374 let mut consumer = producer.subscribe(None);
3375
3376 for s in 0..6 {
3377 producer.create_group(group::Info { sequence: s }).unwrap();
3378 }
3379
3380 consumer.end_at(1);
3381 assert_eq!(
3382 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3383 0
3384 );
3385 assert_eq!(
3386 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3387 1
3388 );
3389 assert!(consumer.next_group().now_or_never().is_none(), "capped at 1");
3390
3391 consumer.end_at(4);
3393 assert_eq!(
3394 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3395 2
3396 );
3397 assert_eq!(
3398 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3399 3
3400 );
3401 assert_eq!(
3402 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3403 4
3404 );
3405 assert!(consumer.next_group().now_or_never().is_none(), "capped at 4");
3406
3407 consumer.end_at(None);
3409 assert_eq!(
3410 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3411 5
3412 );
3413 assert!(consumer.next_group().now_or_never().is_none(), "no more groups");
3414 }
3415
3416 #[tokio::test]
3417 async fn end_at_lower_than_cursor_parks_consumer() {
3418 let mut producer = track_producer("test", None);
3419 let mut consumer = producer.subscribe(None);
3420
3421 for s in 0..3 {
3422 producer.create_group(group::Info { sequence: s }).unwrap();
3423 }
3424
3425 assert_eq!(
3427 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3428 0
3429 );
3430 assert_eq!(
3431 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3432 1
3433 );
3434 assert_eq!(
3435 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3436 2
3437 );
3438
3439 consumer.end_at(1);
3441 producer.create_group(group::Info { sequence: 3 }).unwrap();
3442 producer.create_group(group::Info { sequence: 4 }).unwrap();
3443 assert!(
3444 consumer.next_group().now_or_never().is_none(),
3445 "cap is below cursor; nothing returnable until cap rises"
3446 );
3447
3448 consumer.end_at(None);
3450 assert_eq!(
3451 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3452 3
3453 );
3454 assert_eq!(
3455 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3456 4
3457 );
3458 }
3459
3460 #[tokio::test]
3461 async fn end_at_toggling_around_late_arrivals() {
3462 let mut producer = track_producer("test", None);
3463 let mut consumer = producer.subscribe(None);
3464
3465 consumer.end_at(5);
3466
3467 producer.create_group(group::Info { sequence: 2 }).unwrap();
3469 producer.create_group(group::Info { sequence: 5 }).unwrap();
3470 producer.create_group(group::Info { sequence: 3 }).unwrap();
3471 producer.create_group(group::Info { sequence: 8 }).unwrap();
3473 producer.create_group(group::Info { sequence: 4 }).unwrap();
3474
3475 assert_eq!(
3477 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3478 2
3479 );
3480 assert_eq!(
3481 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3482 3
3483 );
3484 assert_eq!(
3485 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3486 4
3487 );
3488 assert_eq!(
3489 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3490 5
3491 );
3492 assert!(consumer.next_group().now_or_never().is_none());
3494
3495 consumer.end_at(10);
3497 assert_eq!(
3498 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3499 8
3500 );
3501 }
3502
3503 #[tokio::test]
3504 async fn read_frame_returns_single_frame_per_group() {
3505 let mut producer = track_producer("test", None);
3506 let mut consumer = producer.subscribe(None);
3507
3508 producer.write_frame(Timestamp::ZERO, b"hello".as_slice()).unwrap();
3509 producer.write_frame(Timestamp::ZERO, b"world".as_slice()).unwrap();
3510
3511 let frame = consumer
3512 .read_frame()
3513 .now_or_never()
3514 .expect("should not block")
3515 .expect("would have errored")
3516 .expect("track should not be closed");
3517 assert_eq!(&frame.payload[..], b"hello");
3518
3519 let frame = consumer
3520 .read_frame()
3521 .now_or_never()
3522 .expect("should not block")
3523 .expect("would have errored")
3524 .expect("track should not be closed");
3525 assert_eq!(&frame.payload[..], b"world");
3526 }
3527
3528 #[tokio::test]
3529 async fn read_frame_preserves_timestamp() {
3530 let mut producer = track_producer("test", None);
3531 let mut consumer = producer.subscribe(None);
3532
3533 producer
3534 .write_frame(Timestamp::from_micros(20_000).unwrap(), b"hello".as_slice())
3535 .unwrap();
3536
3537 let frame = consumer
3538 .read_frame()
3539 .now_or_never()
3540 .expect("should not block")
3541 .expect("would have errored")
3542 .expect("track should not be closed");
3543 assert_eq!(frame.timestamp.as_micros(), 20_000);
3544 assert_eq!(&frame.payload[..], b"hello");
3545 }
3546
3547 #[tokio::test]
3548 async fn read_frame_skips_stalled_group_for_newer_ready_frame() {
3549 let mut producer = track_producer("test", None);
3550 let mut consumer = producer.subscribe(None);
3551
3552 let _stalled = producer.create_group(group::Info { sequence: 3 }).unwrap();
3554 let mut g5 = producer.create_group(group::Info { sequence: 5 }).unwrap();
3556 g5.write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"later"))
3557 .unwrap();
3558 g5.finish().unwrap();
3559
3560 let frame = consumer
3562 .read_frame()
3563 .now_or_never()
3564 .expect("should not block on stalled earlier group")
3565 .expect("would have errored")
3566 .expect("track should not be closed");
3567 assert_eq!(&frame.payload[..], b"later");
3568 }
3569
3570 #[tokio::test]
3571 async fn read_frame_discards_rest_of_multi_frame_group() {
3572 let mut producer = track_producer("test", None);
3573 let mut consumer = producer.subscribe(None);
3574
3575 let mut g0 = producer.create_group(group::Info { sequence: 0 }).unwrap();
3577 g0.write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"one"))
3578 .unwrap();
3579 g0.write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"two"))
3580 .unwrap();
3581 g0.finish().unwrap();
3582
3583 producer.write_frame(Timestamp::ZERO, b"next".as_slice()).unwrap();
3585
3586 let frame = consumer
3587 .read_frame()
3588 .now_or_never()
3589 .expect("should not block")
3590 .expect("would have errored")
3591 .expect("track should not be closed");
3592 assert_eq!(&frame.payload[..], b"one");
3593
3594 let frame = consumer
3596 .read_frame()
3597 .now_or_never()
3598 .expect("should not block")
3599 .expect("would have errored")
3600 .expect("track should not be closed");
3601 assert_eq!(&frame.payload[..], b"next");
3602 }
3603
3604 #[tokio::test]
3605 async fn read_frame_waits_for_pending_group_after_finish() {
3606 let mut producer = track_producer("test", None);
3609 let mut consumer = producer.subscribe(None);
3610
3611 let mut g0 = producer.create_group(group::Info { sequence: 0 }).unwrap();
3612 producer.finish().unwrap();
3613
3614 assert!(
3616 consumer.read_frame().now_or_never().is_none(),
3617 "read_frame must block on a pending group even after finish()"
3618 );
3619
3620 g0.write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"late"))
3622 .unwrap();
3623 let frame = consumer
3624 .read_frame()
3625 .now_or_never()
3626 .expect("should not block once a frame is written")
3627 .expect("would have errored")
3628 .expect("track should not be closed");
3629 assert_eq!(&frame.payload[..], b"late");
3630 }
3631
3632 #[tokio::test]
3633 async fn read_frame_respects_start_at() {
3634 let mut producer = track_producer("test", None);
3637 let mut consumer = producer.subscribe(None);
3638 consumer.start_at(5);
3639
3640 let mut g3 = producer.create_group(group::Info { sequence: 3 }).unwrap();
3642 g3.write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"skip-me"))
3643 .unwrap();
3644 g3.finish().unwrap();
3645
3646 let mut g5 = producer.create_group(group::Info { sequence: 5 }).unwrap();
3647 g5.write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"keep"))
3648 .unwrap();
3649 g5.finish().unwrap();
3650
3651 let frame = consumer
3652 .read_frame()
3653 .now_or_never()
3654 .expect("should not block")
3655 .expect("would have errored")
3656 .expect("track should not be closed");
3657 assert_eq!(&frame.payload[..], b"keep");
3658 }
3659
3660 #[tokio::test]
3661 async fn read_frame_returns_none_when_finished() {
3662 let mut producer = track_producer("test", None);
3663 let mut consumer = producer.subscribe(None);
3664
3665 producer.write_frame(Timestamp::ZERO, b"only".as_slice()).unwrap();
3666 producer.finish().unwrap();
3667
3668 let frame = consumer
3669 .read_frame()
3670 .now_or_never()
3671 .expect("should not block")
3672 .expect("would have errored")
3673 .expect("track should not be closed");
3674 assert_eq!(&frame.payload[..], b"only");
3675
3676 let done = consumer
3677 .read_frame()
3678 .now_or_never()
3679 .expect("should not block")
3680 .expect("would have errored");
3681 assert!(done.is_none());
3682 }
3683
3684 #[test]
3685 fn append_group_returns_bounds_exceeded_on_sequence_overflow() {
3686 let mut producer = track_producer("test", None);
3687 {
3688 let mut state = producer.state.write().ok().unwrap();
3689 state.max_sequence = Some(u64::MAX);
3690 }
3691
3692 assert!(matches!(producer.append_group(), Err(Error::BoundsExceeded(_))));
3693 }
3694
3695 #[tokio::test]
3696 async fn fetch_cache_hit() {
3697 let mut producer = track_producer("test", None);
3698
3699 let mut group = producer.append_group().unwrap(); group
3702 .write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"hello"))
3703 .unwrap();
3704 group.finish().unwrap();
3705
3706 let dynamic = producer.dynamic();
3709 let consumer = producer.consume();
3710 assert!(consumer.peek_group(0).is_some());
3711 let mut g = consumer.fetch_group(0, None).await.unwrap();
3712 assert_eq!(g.sequence, 0);
3713 assert_eq!(&g.read_frame().await.unwrap().unwrap().payload[..], b"hello");
3714
3715 assert!(dynamic.poll_requested_group(&kio::Waiter::noop()).is_pending());
3717 }
3718
3719 #[tokio::test]
3720 async fn fetch_miss_signals_dynamic() {
3721 let producer = track_producer("test", None);
3722 let dynamic = producer.dynamic();
3723 let consumer = producer.consume();
3724
3725 assert!(consumer.peek_group(5).is_none());
3729 let pending = consumer.fetch_group(5, group::Fetch::default().with_priority(7));
3730 assert!(kio::Pollable::poll(&*pending, &kio::Waiter::noop()).is_pending());
3731
3732 let req = dynamic
3733 .requested_group()
3734 .now_or_never()
3735 .expect("should not block")
3736 .unwrap();
3737 assert_eq!(req.sequence(), 5);
3738 assert_eq!(req.priority(), 7);
3739
3740 let mut group = req.accept(None).unwrap();
3742 group
3743 .write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"hi"))
3744 .unwrap();
3745 group.finish().unwrap();
3746
3747 let mut g = pending.await.unwrap();
3748 assert_eq!(g.sequence, 5);
3749 assert_eq!(&g.read_frame().await.unwrap().unwrap().payload[..], b"hi");
3750 }
3751
3752 #[tokio::test]
3753 async fn fetch_miss_rejects() {
3754 let producer = track_producer("test", None);
3755 let dynamic = producer.dynamic();
3756 let consumer = producer.consume();
3757
3758 let pending = consumer.fetch_group(5, None);
3759 let req = dynamic
3760 .requested_group()
3761 .now_or_never()
3762 .expect("should not block")
3763 .unwrap();
3764
3765 req.reject(Error::Cancel);
3766 assert!(matches!(pending.await, Err(Error::Cancel)));
3767 let fetch = producer.state.read().fetch.clone();
3768 assert!(fetch.read().is_empty());
3769 }
3770
3771 #[tokio::test]
3772 async fn fetch_miss_drop_rejects() {
3773 let producer = track_producer("test", None);
3774 let dynamic = producer.dynamic();
3775 let consumer = producer.consume();
3776
3777 let pending = consumer.fetch_group(5, None);
3778 let req = dynamic
3779 .requested_group()
3780 .now_or_never()
3781 .expect("should not block")
3782 .unwrap();
3783
3784 drop(req);
3785 assert!(matches!(pending.await, Err(Error::Dropped)));
3786 }
3787
3788 #[tokio::test]
3789 async fn fetch_reject_does_not_poison_retry() {
3790 let producer = track_producer("test", None);
3791 let dynamic = producer.dynamic();
3792 let consumer = producer.consume();
3793
3794 let pending = consumer.fetch_group(5, None);
3795 let req = dynamic
3796 .requested_group()
3797 .now_or_never()
3798 .expect("should not block")
3799 .unwrap();
3800 req.reject(Error::Cancel);
3801 assert!(matches!(pending.await, Err(Error::Cancel)));
3802
3803 let retry = consumer.fetch_group(5, None);
3804 let req = dynamic
3805 .requested_group()
3806 .now_or_never()
3807 .expect("should not block")
3808 .unwrap();
3809 let mut group = req.accept(None).unwrap();
3810 group
3811 .write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"retry"))
3812 .unwrap();
3813 group.finish().unwrap();
3814
3815 let mut group = retry.await.unwrap();
3816 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"retry");
3817 }
3818
3819 #[tokio::test]
3820 async fn fetch_coalesces_concurrent() {
3821 let producer = track_producer("test", None);
3822 let dynamic = producer.dynamic();
3823 let consumer = producer.consume();
3824
3825 let first = consumer.fetch_group(5, group::Fetch::default().with_priority(1));
3828 let second = consumer.fetch_group(5, group::Fetch::default().with_priority(7));
3829 assert!(kio::Pollable::poll(&*first, &kio::Waiter::noop()).is_pending());
3830
3831 let req = dynamic
3832 .requested_group()
3833 .now_or_never()
3834 .expect("should not block")
3835 .unwrap();
3836 assert_eq!(req.sequence(), 5);
3837 assert_eq!(req.priority(), 7);
3838 assert!(
3839 dynamic.poll_requested_group(&kio::Waiter::noop()).is_pending(),
3840 "the second fetch queued a duplicate request"
3841 );
3842
3843 let third = consumer.fetch_group(5, None);
3845
3846 let mut group = req.accept(None).unwrap();
3848 group
3849 .write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"hi"))
3850 .unwrap();
3851 group.finish().unwrap();
3852
3853 assert_eq!(first.await.unwrap().sequence, 5);
3854 assert_eq!(second.await.unwrap().sequence, 5);
3855 assert_eq!(third.await.unwrap().sequence, 5);
3856 }
3857
3858 #[tokio::test]
3859 async fn fetch_coalesced_reject_fails_all() {
3860 let producer = track_producer("test", None);
3861 let dynamic = producer.dynamic();
3862 let consumer = producer.consume();
3863
3864 let first = consumer.fetch_group(5, None);
3865 let second = consumer.fetch_group(5, None);
3866 let req = dynamic
3867 .requested_group()
3868 .now_or_never()
3869 .expect("should not block")
3870 .unwrap();
3871 req.reject(Error::Cancel);
3872
3873 assert!(matches!(first.await, Err(Error::Cancel)));
3874 assert!(matches!(second.await, Err(Error::Cancel)));
3875
3876 let retry = consumer.fetch_group(5, None);
3878 assert!(kio::Pollable::poll(&*retry, &kio::Waiter::noop()).is_pending());
3879 let req = dynamic
3880 .requested_group()
3881 .now_or_never()
3882 .expect("should not block")
3883 .unwrap();
3884 assert_eq!(req.sequence(), 5);
3885 }
3886
3887 #[tokio::test]
3888 async fn fetch_queued_fails_when_handlers_leave() {
3889 let producer = track_producer("test", None);
3890 let dynamic = producer.dynamic();
3891 let consumer = producer.consume();
3892
3893 let pending = consumer.fetch_group(5, None);
3895 assert!(kio::Pollable::poll(&*pending, &kio::Waiter::noop()).is_pending());
3896 drop(dynamic);
3897 assert!(matches!(pending.await, Err(Error::NotFound)));
3898
3899 let fetch = producer.state.read().fetch.clone();
3901 assert!(fetch.read().is_empty());
3902 }
3903
3904 #[tokio::test]
3905 async fn fetch_miss_no_dynamic_not_found() {
3906 let mut producer = track_producer("test", None);
3909 producer.append_group().unwrap(); let consumer = producer.consume();
3911 assert!(matches!(consumer.fetch_group(5, None).await, Err(Error::NotFound)));
3912 }
3913
3914 #[tokio::test]
3915 async fn fetch_past_final_not_found() {
3916 let mut producer = track_producer("test", None);
3917 producer.append_group().unwrap(); producer.finish().unwrap(); let dynamic = producer.dynamic();
3923 let consumer = producer.consume();
3924 assert!(matches!(consumer.fetch_group(5, None).await, Err(Error::NotFound)));
3925
3926 assert!(dynamic.poll_requested_group(&kio::Waiter::noop()).is_pending());
3928 }
3929
3930 fn pooled_producer(capacity: u64) -> (Producer, cache::Pool) {
3932 let pool = cache::Pool::new(capacity);
3933 let broadcast = broadcast::Info {
3934 origin: crate::origin::Info::default().with_pool(pool.clone()),
3935 ..Default::default()
3936 };
3937 let producer = Producer::new(Arc::new(broadcast), "test", None);
3938 (producer, pool)
3939 }
3940
3941 fn finished_group(producer: &mut Producer, size: usize) -> u64 {
3942 let mut group = producer.append_group().unwrap();
3943 group
3944 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; size]))
3945 .unwrap();
3946 group.finish().unwrap();
3947 group.sequence
3948 }
3949
3950 #[tokio::test]
3951 async fn pool_evicts_oldest_group() {
3952 tokio::time::pause();
3953
3954 let (mut producer, pool) = pooled_producer(3000);
3956
3957 finished_group(&mut producer, 1000); tokio::time::advance(Duration::from_millis(10)).await;
3959 finished_group(&mut producer, 1000); tokio::time::advance(Duration::from_millis(10)).await;
3961 finished_group(&mut producer, 1000); assert!(pool.used() <= 3000, "pool should be back under budget");
3966
3967 let consumer = producer.consume();
3968 assert!(consumer.peek_group(0).is_none(), "evicted group is a cache miss");
3969 assert!(consumer.peek_group(1).is_some());
3970 assert!(consumer.peek_group(2).is_some());
3971
3972 let mut subscriber = producer.subscribe(None);
3974 assert_eq!(subscriber.assert_group().sequence, 1);
3975 assert_eq!(subscriber.assert_group().sequence, 2);
3976 }
3977
3978 #[tokio::test]
3979 async fn pool_never_evicts_latest() {
3980 tokio::time::pause();
3981
3982 let (mut producer, pool) = pooled_producer(100);
3984 finished_group(&mut producer, 1000);
3985
3986 assert!(pool.used() > 100, "pinned latest may exceed the budget");
3987 let mut subscriber = producer.subscribe(None);
3988 let mut group = subscriber.assert_group();
3989 assert_eq!(group.read_frame().await.unwrap().unwrap().payload.len(), 1000);
3990 }
3991
3992 #[tokio::test]
3993 async fn pool_reads_bump_recency() {
3994 tokio::time::pause();
3995
3996 let (mut producer, pool) = pooled_producer(3000);
3997 let mut subscriber = producer.subscribe(None);
3998
3999 finished_group(&mut producer, 1000); tokio::time::advance(Duration::from_millis(10)).await;
4001 finished_group(&mut producer, 1000); tokio::time::advance(Duration::from_millis(10)).await;
4003
4004 let mut group = subscriber.assert_group();
4006 assert_eq!(group.sequence, 0);
4007 group.read_frame().await.unwrap().unwrap();
4008 tokio::time::advance(Duration::from_millis(10)).await;
4009
4010 finished_group(&mut producer, 1000); let consumer = producer.consume();
4014 assert!(
4015 consumer.peek_group(0).is_some(),
4016 "recently read group survives: {:?}",
4017 pool.debug_entries()
4018 );
4019 assert!(
4020 consumer.peek_group(1).is_none(),
4021 "stale group is evicted: {:?}",
4022 pool.debug_entries()
4023 );
4024 }
4025
4026 #[tokio::test]
4027 async fn pool_eviction_aborts_readers() {
4028 tokio::time::pause();
4029
4030 let (mut producer, pool) = pooled_producer(3000);
4031 let mut subscriber = producer.subscribe(None);
4032
4033 finished_group(&mut producer, 1000); let group0 = subscriber.assert_group();
4035
4036 tokio::time::advance(Duration::from_millis(10)).await;
4037 finished_group(&mut producer, 1000); tokio::time::advance(Duration::from_millis(10)).await;
4039 finished_group(&mut producer, 1000); let mut group0 = group0;
4044 let read = group0.read_frame().await;
4045 assert!(
4046 matches!(read, Err(Error::Evicted)),
4047 "expected Evicted, got {read:?}: {:?}",
4048 pool.debug_entries()
4049 );
4050 }
4051
4052 #[tokio::test]
4053 async fn pool_growth_on_old_group_charges() {
4054 tokio::time::pause();
4055
4056 let (mut producer, pool) = pooled_producer(3000);
4057
4058 let mut group0 = producer.append_group().unwrap();
4060 tokio::time::advance(Duration::from_millis(10)).await;
4061 let _group1 = producer.append_group().unwrap();
4063 tokio::time::advance(Duration::from_millis(10)).await;
4064
4065 group0
4068 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; 4000]))
4069 .unwrap();
4070 assert!(pool.used() <= 3000, "growth on an old group triggers eviction");
4071 assert!(matches!(group0.abort(Error::Cancel), Err(Error::Evicted)));
4072 }
4073
4074 #[tokio::test]
4075 async fn refetched_latest_group_is_repinned() {
4076 tokio::time::pause();
4077
4078 let (mut producer, pool) = pooled_producer(3000);
4079 let dynamic = producer.dynamic();
4080
4081 let mut straggler = producer.append_group().unwrap();
4083 straggler
4084 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; 1000]))
4085 .unwrap();
4086 tokio::time::advance(Duration::from_millis(10)).await;
4087
4088 let latest = producer.append_group().unwrap(); latest.abort(Error::Cancel).unwrap();
4091 tokio::time::advance(Duration::from_millis(10)).await;
4092
4093 let consumer = producer.consume();
4096 let pending = consumer.fetch_group(1, None);
4097 let req = dynamic
4098 .requested_group()
4099 .now_or_never()
4100 .expect("should not block")
4101 .unwrap();
4102 let mut group = req.accept(None).unwrap();
4103 group
4104 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; 1000]))
4105 .unwrap();
4106 group.finish().unwrap();
4107 pending.await.unwrap();
4108 tokio::time::advance(Duration::from_millis(10)).await;
4109
4110 straggler
4113 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; 4000]))
4114 .unwrap();
4115
4116 assert!(pool.used() <= 3000);
4117 let mut group = consumer.peek_group(1).expect("refetched latest must stay pinned");
4118 assert_eq!(group.read_frame().await.unwrap().unwrap().payload.len(), 1000);
4119 }
4120
4121 #[tokio::test]
4122 async fn pool_eviction_allows_refetch() {
4123 tokio::time::pause();
4124
4125 let (mut producer, _pool) = pooled_producer(3000);
4126 let dynamic = producer.dynamic();
4127
4128 finished_group(&mut producer, 1000); tokio::time::advance(Duration::from_millis(10)).await;
4130 finished_group(&mut producer, 1000); tokio::time::advance(Duration::from_millis(10)).await;
4132 finished_group(&mut producer, 1000); let consumer = producer.consume();
4137 assert!(consumer.peek_group(0).is_none());
4138 let pending = consumer.fetch_group(0, None);
4139
4140 let req = dynamic
4141 .requested_group()
4142 .now_or_never()
4143 .expect("should not block")
4144 .unwrap();
4145 assert_eq!(req.sequence(), 0);
4146
4147 let mut group = req.accept(None).unwrap();
4149 group
4150 .write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"refetched"))
4151 .unwrap();
4152 group.finish().unwrap();
4153
4154 let mut group = pending.await.unwrap();
4155 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"refetched");
4156 }
4157
4158 #[tokio::test]
4159 async fn fetch_aborts_with_track() {
4160 let producer = track_producer("test", None);
4161 let dynamic = producer.dynamic();
4162 let consumer = producer.consume();
4163
4164 let pending = consumer.fetch_group(3, None);
4165 assert!(kio::Pollable::poll(&*pending, &kio::Waiter::noop()).is_pending());
4166
4167 producer.abort(Error::Cancel).unwrap();
4168 assert!(pending.await.is_err());
4169 drop(dynamic);
4170 }
4171}