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::{BTreeMap, HashMap, VecDeque},
25 sync::Arc,
26 sync::OnceLock,
27 sync::atomic::{AtomicBool, Ordering},
28 task::{Poll, ready},
29 time::Duration,
30};
31
32pub const DEFAULT_LATENCY_MAX: Duration = Duration::from_secs(5);
34
35const MAX_DATAGRAM_AGE: Duration = Duration::from_millis(50);
41
42const EVICT_SLACK: usize = 64;
45
46const EVICT_SCAN: usize = 4;
50
51#[derive(Clone, Debug)]
62#[non_exhaustive]
63pub struct Info {
64 pub timescale: Timescale,
71 pub latency_max: Duration,
80 pub priority: u8,
83 pub ordered: bool,
87}
88
89impl Default for Info {
90 fn default() -> Self {
91 Self {
92 timescale: Timescale::default(),
93 latency_max: DEFAULT_LATENCY_MAX,
94 priority: 0,
95 ordered: false,
96 }
97 }
98}
99
100impl Info {
101 pub fn with_timescale(mut self, timescale: Timescale) -> Self {
106 self.timescale = timescale;
107 self
108 }
109
110 pub fn with_latency_max(mut self, latency_max: Duration) -> Self {
112 self.latency_max = latency_max;
113 self
114 }
115
116 pub fn with_priority(mut self, priority: u8) -> Self {
118 self.priority = priority;
119 self
120 }
121
122 pub fn with_ordered(mut self, ordered: bool) -> Self {
126 self.ordered = ordered;
127 self
128 }
129}
130
131#[derive(Default)]
132pub(crate) struct TrackState {
133 info: Option<Info>,
136
137 broadcast: Arc<broadcast::Info>,
140
141 cache: Arc<cache::Track>,
145
146 lookup: HashMap<u64, Slot>,
150
151 arrival: VecDeque<(u64, u32)>,
156
157 evict: VecDeque<(u64, u32)>,
165
166 debt: u64,
171
172 datagrams: VecDeque<(Datagram, web_async::time::Instant)>,
176
177 datagram_offset: usize,
180
181 offset: usize,
184
185 max_sequence: Option<u64>,
188
189 latest_group: Option<u64>,
194
195 next_stamp: u32,
197
198 expire_cursor: usize,
201
202 final_sequence: Option<u64>,
204
205 abort: Option<Error>,
207
208 subscriptions: kio::Shared<Subscriptions>,
212
213 fetch: kio::Shared<FetchState>,
216}
217
218struct Slot {
224 group: group::Producer,
225
226 stamp: u32,
231}
232
233type Subscriptions = Vec<kio::Consumer<Subscription>>;
235
236type FetchState = Requests<u64, PendingFetch>;
241
242struct PendingFetch {
244 priority: u8,
246
247 result: kio::Producer<FetchOutcome>,
252}
253
254#[derive(Default)]
257struct FetchOutcome {
258 rejected: Option<Error>,
259}
260
261impl TrackState {
262 fn poll_info(&self) -> Poll<Result<Info>> {
263 if let Some(info) = &self.info {
264 Poll::Ready(Ok(info.clone()))
265 } else {
266 Poll::Pending
267 }
268 }
269
270 fn poll_recv_group(&self, index: usize, min_sequence: u64) -> Poll<Result<Option<(group::Consumer, usize)>>> {
274 let start = index.saturating_sub(self.offset);
275 for (i, (sequence, stamp)) in self.arrival.iter().enumerate().skip(start) {
276 if *sequence >= min_sequence
277 && let Some(slot) = self.lookup.get(sequence)
278 && slot.stamp == *stamp
279 && !slot.group.is_aborted()
280 {
281 slot.group.cache_refresh();
284 return Poll::Ready(Ok(Some((slot.group.consume(), self.offset + i))));
285 }
286 }
287
288 if self.is_complete() {
290 Poll::Ready(Ok(None))
291 } else if let Some(err) = &self.abort {
292 Poll::Ready(Err(err.clone()))
293 } else {
294 Poll::Pending
295 }
296 }
297
298 fn poll_recv_datagram(&self, index: usize) -> Poll<Result<Option<(Datagram, usize)>>> {
304 let start = index.saturating_sub(self.datagram_offset);
305 if let Some((datagram, _)) = self.datagrams.get(start) {
306 return Poll::Ready(Ok(Some((datagram.clone(), self.datagram_offset + start))));
307 }
308
309 if self.is_complete() {
311 Poll::Ready(Ok(None))
312 } else if let Some(err) = &self.abort {
313 Poll::Ready(Err(err.clone()))
314 } else {
315 Poll::Pending
316 }
317 }
318
319 fn push_datagram(&mut self, datagram: Datagram) {
321 let now = web_async::time::Instant::now();
322 self.datagrams.push_back((datagram, now));
323 while let Some((_, at)) = self.datagrams.front() {
324 if now.duration_since(*at) <= MAX_DATAGRAM_AGE {
325 break;
326 }
327 self.datagrams.pop_front();
328 self.datagram_offset += 1;
329 }
330 }
331
332 fn poll_read_frame(
336 &self,
337 index: usize,
338 next_sequence: u64,
339 waiter: &kio::Waiter,
340 ) -> Poll<Result<Option<(frame::Frame, usize, u64)>>> {
341 let start = index.saturating_sub(self.offset);
342 let mut pending_seen = false;
343 for (i, (sequence, stamp)) in self.arrival.iter().enumerate().skip(start) {
344 if *sequence < next_sequence {
345 continue;
346 }
347 let Some(slot) = self.lookup.get(sequence) else {
348 continue;
349 };
350 if slot.stamp != *stamp {
351 continue;
354 }
355
356 let mut consumer = slot.group.consume();
357 match consumer.poll_read_frame(waiter) {
358 Poll::Ready(Ok(Some(frame))) => {
359 return Poll::Ready(Ok(Some((frame, self.offset + i, *sequence))));
360 }
361 Poll::Ready(Ok(None)) => continue,
362 Poll::Ready(Err(_)) => continue,
365 Poll::Pending => {
366 pending_seen = true;
367 continue;
368 }
369 }
370 }
371
372 if pending_seen {
375 Poll::Pending
376 } else if self.is_complete() {
377 Poll::Ready(Ok(None))
378 } else if let Some(err) = &self.abort {
379 Poll::Ready(Err(err.clone()))
380 } else {
381 Poll::Pending
382 }
383 }
384
385 fn poll_next_in_range(
395 &self,
396 next_sequence: u64,
397 end_sequence: Option<u64>,
398 ) -> Poll<Result<Option<group::Consumer>>> {
399 if let Some(end) = end_sequence
403 && end < next_sequence
404 {
405 if let Some(err) = &self.abort {
406 return Poll::Ready(Err(err.clone()));
407 }
408 return Poll::Pending;
409 }
410
411 let mut best: Option<&group::Producer> = None;
412 for slot in self.lookup.values() {
413 let group = &slot.group;
414 if group.sequence < next_sequence {
415 continue;
416 }
417 if let Some(end) = end_sequence
418 && group.sequence > end
419 {
420 continue;
421 }
422 if group.is_aborted() {
423 continue;
424 }
425 if best.is_none_or(|b| group.sequence < b.sequence) {
426 best = Some(group);
427 }
428 }
429
430 if let Some(group) = best {
431 group.cache_refresh();
433 return Poll::Ready(Ok(Some(group.consume())));
434 }
435
436 if let Some(err) = &self.abort {
438 return Poll::Ready(Err(err.clone()));
439 }
440 if let Some(fin) = self.final_sequence
443 && next_sequence >= fin
444 {
445 return Poll::Ready(Ok(None));
446 }
447 Poll::Pending
448 }
449
450 #[cfg(test)]
455 fn cached_group(&self, sequence: u64) -> Option<group::Consumer> {
456 let slot = self.lookup.get(&sequence)?;
457 if slot.group.is_aborted() {
458 return None;
459 }
460 Some(slot.group.consume())
461 }
462
463 fn latency_bound(&self) -> Option<Duration> {
466 self.info.as_ref().map(|info| info.latency_max)
467 }
468
469 fn poll_fetch_cached(&self, sequence: u64) -> Poll<Result<group::Consumer>> {
474 if let Some(slot) = self.lookup.get(&sequence)
475 && !slot.group.is_aborted()
476 {
477 slot.group.cache_refresh();
481 return Poll::Ready(Ok(slot.group.consume()));
482 }
483
484 if let Some(err) = &self.abort {
485 return Poll::Ready(Err(err.clone()));
486 }
487
488 if self.final_sequence.is_some_and(|fin| sequence >= fin) {
490 return Poll::Ready(Err(Error::NotFound));
491 }
492
493 Poll::Pending
494 }
495
496 fn evict_expired(&mut self, max_age: Duration) {
505 let now = self.cache.pool().now();
506 let max_ticks = cache::Pool::ticks(max_age);
507
508 let len = self.evict.len();
509 if len > 0 {
510 let start = self.expire_cursor % len;
511 for step in 0..len.min(EVICT_SCAN) {
512 let (sequence, stamp) = self.evict[(start + step) % len];
513 let Some(slot) = self.lookup.get(&sequence) else {
514 continue;
515 };
516 if slot.stamp != stamp {
517 continue;
519 }
520 if slot.group.is_aborted() {
523 self.lookup.remove(&sequence);
524 continue;
525 }
526 if Some(sequence) == self.latest_group || now.saturating_sub(slot.group.cache_accessed()) <= max_ticks {
527 continue;
528 }
529 let slot = self.lookup.remove(&sequence).unwrap();
533 let _ = slot.group.abort(Error::Old);
534 }
535 self.expire_cursor = (start + EVICT_SCAN) % len;
536 }
537
538 while let Some((sequence, stamp)) = self.arrival.front() {
541 if self.lookup.get(sequence).is_some_and(|slot| slot.stamp == *stamp) {
542 break;
543 }
544 self.arrival.pop_front();
545 self.offset += 1;
546 }
547
548 while let Some((sequence, stamp)) = self.evict.front() {
550 if self.lookup.get(sequence).is_some_and(|slot| slot.stamp == *stamp) {
551 break;
552 }
553 self.evict.pop_front();
554 }
555
556 if self.evict.len() > 2 * self.lookup.len() + EVICT_SLACK {
559 let lookup = &self.lookup;
560 self.evict
561 .retain(|(sequence, stamp)| lookup.get(sequence).is_some_and(|slot| slot.stamp == *stamp));
562 }
563 }
564
565 fn clear_cache(&mut self) {
568 self.lookup.clear();
569 self.arrival.clear();
570 self.evict.clear();
571 self.latest_group = None;
572 self.debt = 0;
573 }
574
575 fn install(&mut self, mut info: Info) {
581 info.latency_max = info.latency_max.min(self.broadcast.origin.cache_duration);
582 self.info = Some(info);
583 }
584
585 fn spawn(broadcast: Arc<broadcast::Info>) -> kio::Producer<Self> {
592 let state = kio::Producer::new(Self {
593 broadcast: broadcast.clone(),
594 ..Default::default()
595 });
596 let cache = cache::Track::new(broadcast.origin.pool.clone(), state.downgrade());
597 state.write().ok().expect("a new track is open").cache = cache;
598 state
599 }
600
601 fn claim_sequence(&mut self, sequence: u64) -> Result<()> {
607 if let Some(slot) = self.lookup.get(&sequence) {
608 if !slot.group.is_aborted() {
609 return Err(Error::Duplicate);
610 }
611 self.lookup.remove(&sequence);
612 }
613 Ok(())
614 }
615
616 fn insert_group(&mut self, group: &group::Producer, visible: bool) {
623 let sequence = group.sequence;
624 self.next_stamp = self.next_stamp.wrapping_add(1);
625 let stamp = self.next_stamp;
626
627 if self.latest_group.is_none_or(|latest| sequence >= latest) {
631 if let Some(latest) = self.latest_group
634 && sequence > latest
635 && let Some(prev) = self.lookup.get(&latest)
636 {
637 prev.group.cache_demote();
638 self.evict.push_back((latest, prev.stamp));
639 }
640 self.latest_group = Some(sequence);
641 } else {
642 group.cache_demote();
643 self.evict.push_back((sequence, stamp));
644 }
645
646 self.max_sequence = Some(self.max_sequence.map_or(sequence, |max| max.max(sequence)));
647 self.lookup.insert(
648 sequence,
649 Slot {
650 group: group.clone(),
651 stamp,
652 },
653 );
654 if visible {
655 self.arrival.push_back((sequence, stamp));
656 }
657 }
658
659 fn commit_group(&mut self, group: &group::Producer, visible: bool, latency_max: Duration) {
663 self.charge_debt();
664 self.insert_group(group, visible);
665 self.evict_expired(latency_max);
666 }
667
668 pub(super) fn charge_debt(&mut self) {
681 let written = self.cache.take_written();
682 let pool = self.cache.pool().clone();
683 match pool.accrue(written) {
684 Some(mut accrued) => {
685 if self.oldest_is_stale(&pool) {
686 accrued = accrued.saturating_mul(2);
687 }
688 self.debt = self.debt.saturating_add(accrued).min(pool.used());
691 self.pay_debt(&pool, written.saturating_mul(2));
694 }
695 None => self.debt = 0,
698 }
699 }
700
701 fn oldest_is_stale(&self, pool: &cache::Pool) -> bool {
705 let Some(average) = pool.average() else {
706 return false;
707 };
708 let Some((sequence, stamp)) = self.evict.front() else {
709 return false;
710 };
711 let Some(slot) = self.lookup.get(sequence) else {
712 return false;
713 };
714 slot.stamp == *stamp && !slot.group.is_aborted() && slot.group.cache_accessed() <= average
715 }
716
717 fn pay_debt(&mut self, pool: &cache::Pool, cap: u64) {
730 let average = pool.average().unwrap_or(0);
731 let mut paid = 0u64;
732 let mut scanned = 0usize;
733 for _ in 0..self.evict.len() {
734 if self.debt == 0 || paid >= cap || scanned >= EVICT_SCAN {
735 return;
736 }
737 let Some((sequence, stamp)) = self.evict.pop_front() else {
738 return;
739 };
740 let Some(slot) = self.lookup.get(&sequence) else {
741 continue;
743 };
744 if slot.stamp != stamp {
745 continue;
747 }
748 if slot.group.is_aborted() {
749 self.lookup.remove(&sequence);
751 continue;
752 }
753 if Some(sequence) == self.latest_group {
754 self.evict.push_back((sequence, stamp));
756 continue;
757 }
758
759 scanned += 1;
760 if slot.group.cache_accessed() > average {
764 self.evict.push_back((sequence, stamp));
765 continue;
766 }
767 let size = slot.group.cache_size();
770 if size > self.debt {
771 self.evict.push_front((sequence, stamp));
772 return;
773 }
774
775 self.debt -= size;
776 paid = paid.saturating_add(size);
777 let slot = self.lookup.remove(&sequence).unwrap();
778 let _ = slot.group.abort(Error::Evicted);
779 }
780 }
781
782 fn set_final(&mut self, final_sequence: u64) -> Result<()> {
785 if self.final_sequence.is_some() {
786 return Err(Error::Closed);
787 }
788 if let Some(max) = self.max_sequence
789 && final_sequence <= max
790 {
791 return Err(Error::ProtocolViolation);
792 }
793 self.final_sequence = Some(final_sequence);
794 Ok(())
795 }
796
797 fn is_complete(&self) -> bool {
803 self.final_sequence
804 .is_some_and(|fin| self.max_sequence.map_or(0, |max| max.saturating_add(1)) >= fin)
805 }
806
807 fn poll_finished(&self) -> Poll<Result<u64>> {
808 if let Some(fin) = self.final_sequence {
809 Poll::Ready(Ok(fin))
810 } else if let Some(err) = &self.abort {
811 Poll::Ready(Err(err.clone()))
812 } else {
813 Poll::Pending
814 }
815 }
816
817 fn modify(producer: &kio::Producer<Self>) -> Result<kio::Mut<'_, Self>> {
818 producer.write().map_err(|r| r.abort.clone().unwrap_or(Error::Dropped))
819 }
820
821 fn insert_group_request(&mut self, sequence: u64, info: Option<Info>) -> Result<group::Producer> {
827 if let Some(err) = &self.abort {
828 return Err(err.clone());
829 }
830 if let Some(fin) = self.final_sequence
831 && sequence >= fin
832 {
833 return Err(Error::Closed);
834 }
835
836 if self.info.is_none() {
840 self.install(info.unwrap_or_default());
841 }
842 let info = self.info.clone().unwrap();
843
844 self.claim_sequence(sequence)?;
846
847 let latency_max = info.latency_max;
848 let group = group::Producer::new(group::Info { sequence }, info, self.cache.clone());
849 group.cache_refresh();
854 self.commit_group(&group, false, latency_max);
855 Ok(group)
856 }
857}
858
859#[derive(Clone)]
861pub struct Producer {
862 name: Arc<str>,
863 broadcast: Arc<broadcast::Info>,
866 state: kio::Producer<TrackState>,
867 prev_subscription: Option<Subscription>,
868 alive: Arc<Alive>,
870 stats: stats::Scope,
874}
875
876impl Producer {
877 pub(crate) fn new(
885 broadcast: Arc<broadcast::Info>,
886 name: impl Into<Arc<str>>,
887 info: impl Into<Option<Info>>,
888 ) -> Self {
889 let name = name.into();
890 let state = TrackState::spawn(broadcast.clone());
891 state
892 .write()
893 .ok()
894 .expect("a new track is open")
895 .install(info.into().unwrap_or_default());
896 let alive = Alive::new(name.clone(), state.clone());
897 alive.publish(None);
898 Self {
899 name,
900 state,
901 broadcast,
902 prev_subscription: None,
903 alive,
904 stats: stats::Scope::default(),
905 }
906 }
907
908 pub(crate) fn with_stats(mut self, scope: stats::Scope) -> Self {
912 self.alive.publish(Some(&scope));
913 self.stats = scope;
914 self
915 }
916
917 pub fn name(&self) -> &str {
919 &self.name
920 }
921
922 pub fn broadcast(&self) -> &broadcast::Info {
924 &self.broadcast
925 }
926
927 pub fn create_group(&mut self, group: group::Info) -> Result<group::Producer> {
929 let mut state = self.modify()?;
930 if let Some(fin) = state.final_sequence
931 && group.sequence >= fin
932 {
933 return Err(Error::Closed);
934 }
935 let track = state.info.clone().unwrap();
936 let latency_max = track.latency_max;
937
938 state.claim_sequence(group.sequence)?;
940
941 let group = group::Producer::new(group, track, state.cache.clone()).with_meter(self.stats.meter());
942 state.commit_group(&group, true, latency_max);
943
944 Ok(group)
945 }
946
947 pub fn append_group(&mut self) -> Result<group::Producer> {
949 let mut state = self.modify()?;
950 let sequence = match state.max_sequence {
951 Some(s) => s.checked_add(1).ok_or(coding::BoundsExceeded)?,
952 None => 0,
953 };
954 if let Some(fin) = state.final_sequence
955 && sequence >= fin
956 {
957 return Err(Error::Closed);
958 }
959
960 let track = state.info.clone().unwrap();
961 let latency_max = track.latency_max;
962
963 let group =
964 group::Producer::new(group::Info { sequence }, track, state.cache.clone()).with_meter(self.stats.meter());
965 state.commit_group(&group, true, latency_max);
966
967 Ok(group)
968 }
969
970 pub fn append_datagram<B: crate::IntoBytes>(&mut self, timestamp: Timestamp, payload: B) -> Result<u64> {
982 let payload = payload.into_bytes();
983 if payload.len() > super::datagram::MAX_DATAGRAM_PAYLOAD {
984 return Err(Error::FrameTooLarge);
985 }
986 let meter = self.stats.meter();
988 let mut state = self.modify()?;
989 let timescale = state.info.as_ref().unwrap().timescale;
991 let timestamp = timestamp.convert(timescale).map_err(|_| Error::TimestampMismatch)?;
992 let sequence = match state.max_sequence {
993 Some(s) => s.checked_add(1).ok_or(coding::BoundsExceeded)?,
994 None => 0,
995 };
996 if let Some(fin) = state.final_sequence
997 && sequence >= fin
998 {
999 return Err(Error::Closed);
1000 }
1001 state.max_sequence = Some(sequence);
1002 meter.datagram(payload.len() as u64);
1003 state.push_datagram(Datagram {
1004 sequence,
1005 timestamp,
1006 payload,
1007 });
1008 Ok(sequence)
1009 }
1010
1011 pub fn write_datagram(&mut self, mut datagram: Datagram) -> Result<()> {
1017 if datagram.payload.len() > super::datagram::MAX_DATAGRAM_PAYLOAD {
1018 return Err(Error::FrameTooLarge);
1019 }
1020 let meter = self.stats.meter();
1022 let mut state = self.modify()?;
1023 let timescale = state.info.as_ref().unwrap().timescale;
1025 datagram.timestamp = datagram
1026 .timestamp
1027 .convert(timescale)
1028 .map_err(|_| Error::TimestampMismatch)?;
1029 if let Some(fin) = state.final_sequence
1030 && datagram.sequence >= fin
1031 {
1032 return Err(Error::Closed);
1033 }
1034 state.max_sequence = Some(state.max_sequence.unwrap_or(0).max(datagram.sequence));
1035 meter.datagram(datagram.payload.len() as u64);
1036 state.push_datagram(datagram);
1037 Ok(())
1038 }
1039
1040 pub fn write_frame<B: crate::IntoBytes>(&mut self, timestamp: Timestamp, frame: B) -> Result<()> {
1045 let frame = crate::IntoBytes::into_bytes(frame);
1046 if frame.len() as u64 > group::MAX_GROUP_CACHE {
1047 return Err(Error::FrameTooLarge);
1048 }
1049 let mut group = self.append_group()?;
1050 group.write_frame(timestamp, frame)?;
1051 group.finish()?;
1052 Ok(())
1053 }
1054
1055 pub fn finish(&mut self) -> Result<()> {
1061 let mut state = self.modify()?;
1062 let final_sequence = match state.max_sequence {
1063 Some(max) => max.checked_add(1).ok_or(coding::BoundsExceeded)?,
1064 None => 0,
1065 };
1066 state.set_final(final_sequence)
1067 }
1068
1069 pub fn finish_at(&mut self, final_sequence: u64) -> Result<()> {
1082 self.modify()?.set_final(final_sequence)
1083 }
1084
1085 pub fn final_sequence(&self) -> Option<u64> {
1090 self.state.read().final_sequence
1091 }
1092
1093 pub fn abort(self, err: Error) -> Result<()> {
1104 let mut guard = self.modify()?;
1105 guard.abort = Some(err);
1106 guard.clear_cache();
1107 guard.datagrams.clear();
1108 guard.close();
1109 Ok(())
1110 }
1111
1112 pub async fn unused(&self) -> Result<()> {
1114 self.state.unused().await.map_err(|_| self.abort_reason())
1115 }
1116
1117 pub async fn used(&self) -> Result<()> {
1119 self.state.used().await.map_err(|_| self.abort_reason())
1120 }
1121
1122 pub async fn closed(&self) -> Error {
1124 kio::wait(|waiter| self.poll_closed(waiter)).await
1125 }
1126
1127 pub fn poll_closed(&self, waiter: &kio::Waiter) -> Poll<Error> {
1129 self.state.poll_closed(waiter).map(|()| self.abort_reason())
1130 }
1131
1132 fn abort_reason(&self) -> Error {
1134 self.state.read().abort.clone().unwrap_or(Error::Dropped)
1135 }
1136
1137 pub fn is_closed(&self) -> bool {
1139 self.state.read().is_closed()
1140 }
1141
1142 pub fn latest(&self) -> Option<u64> {
1144 self.state.read().max_sequence
1145 }
1146
1147 pub fn is_clone(&self, other: &Self) -> bool {
1149 self.state.same_channel(&other.state)
1150 }
1151
1152 pub(crate) fn weak(&self) -> TrackWeak {
1154 TrackWeak {
1155 name: self.name.clone(),
1156 state: self.state.weak(),
1157 }
1158 }
1159
1160 pub fn demand(&self) -> Demand {
1168 Demand {
1169 name: self.name.clone(),
1170 state: self.state.weak(),
1171 }
1172 }
1173
1174 pub fn consume(&self) -> Consumer {
1179 Consumer::plain(self.name.clone(), self.state.consume())
1180 }
1181
1182 pub fn subscribe(&self, subscription: impl Into<Option<Subscription>>) -> Subscriber {
1187 let preferences = subscription.into().unwrap_or_default();
1188
1189 let info = self.state.read().info.clone().expect("producer always has info");
1194 let subscription = kio::Producer::new(preferences);
1195 register_subscription(self.state.read(), &subscription);
1196
1197 Subscriber {
1198 name: self.name.clone(),
1199 info,
1200 inner: SubscriberKind::Plain(PlainSubscriber {
1201 state: self.state.consume(),
1202 subscription,
1203 index: 0,
1204 datagram_index: 0,
1205 min_sequence: 0,
1206 next_sequence: 0,
1207 end_sequence: None,
1208 parked: BTreeMap::new(),
1209 }),
1210 stats: stats::Scope::default(),
1212 _stats_sub: stats::Subscription::default(),
1213 }
1214 }
1215
1216 pub async fn subscription_changed(&mut self) -> Result<Option<Subscription>> {
1222 kio::wait(|waiter| self.poll_subscription_changed(waiter)).await
1223 }
1224
1225 pub fn subscription(&self) -> Option<Subscription> {
1233 let state = self.state.read();
1234 let (subs, bound) = (state.subscriptions.clone(), state.latency_bound());
1235 drop(state);
1236 snapshot_subscription(&subs, bound)
1237 }
1238
1239 pub fn poll_subscription_changed(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<Subscription>>> {
1243 if self.state.poll_closed(waiter).is_ready() {
1246 let abort = self.state.read().abort.clone();
1247 return Poll::Ready(Err(abort.unwrap_or(Error::Dropped)));
1248 }
1249
1250 let state = self.state.read();
1252 let (subs, bound) = (state.subscriptions.clone(), state.latency_bound());
1253 drop(state);
1254
1255 let prev = &self.prev_subscription;
1256 let mut combined = None;
1257 let mut guard = match subs.poll(waiter, |subs| {
1258 let next = combined_subscription(subs, bound, waiter);
1259 if &next == prev {
1260 Poll::Pending
1261 } else {
1262 combined = next;
1263 Poll::Ready(())
1264 }
1265 }) {
1266 Poll::Ready(guard) => guard,
1267 Poll::Pending => return Poll::Pending,
1268 };
1269 guard.retain(|sub| !sub.is_closed());
1271 drop(guard);
1272 self.prev_subscription = combined.clone();
1273 Poll::Ready(Ok(combined))
1274 }
1275
1276 pub fn poll_unused(&self, waiter: &kio::Waiter) -> Poll<()> {
1278 self.state.poll_unused(waiter).map(|_| ())
1279 }
1280
1281 pub fn dynamic(&self) -> Dynamic {
1285 Dynamic::new(self.name.clone(), self.state.clone(), self.alive.clone())
1286 }
1287
1288 fn modify(&self) -> Result<kio::Mut<'_, TrackState>> {
1289 TrackState::modify(&self.state)
1290 }
1291}
1292
1293fn poll_requested_group(
1297 state: &kio::Producer<TrackState>,
1298 fetch: &kio::Shared<FetchState>,
1299 waiter: &kio::Waiter,
1300) -> Poll<Result<GroupRequest>> {
1301 if let Poll::Ready(mut guard) = fetch.poll(waiter, |fetch| {
1303 if fetch.has_queued() {
1304 Poll::Ready(())
1305 } else {
1306 Poll::Pending
1307 }
1308 }) {
1309 let sequence = guard.pop().expect("predicate guaranteed a request");
1310 let pending = guard.get(&sequence).expect("popped key must be pending");
1314 let priority = pending.priority;
1315 let result = pending.result.clone();
1316 drop(guard);
1317 return Poll::Ready(Ok(GroupRequest {
1318 state: state.clone(),
1319 fetch: fetch.clone(),
1320 sequence,
1321 priority,
1322 result,
1323 done: false,
1324 }));
1325 }
1326
1327 match state.poll_ref(waiter, |state| match &state.abort {
1329 Some(err) => Poll::Ready(err.clone()),
1330 None => Poll::Pending,
1331 }) {
1332 Poll::Ready(Ok(err)) => Poll::Ready(Err(err)),
1333 Poll::Ready(Err(closed)) => Poll::Ready(Err(closed.abort.clone().unwrap_or(Error::Dropped))),
1334 Poll::Pending => Poll::Pending,
1335 }
1336}
1337
1338pub struct Dynamic {
1348 name: Arc<str>,
1349 state: kio::Producer<TrackState>,
1351 fetch: kio::Shared<FetchState>,
1353 alive: Arc<Alive>,
1356}
1357
1358impl Dynamic {
1359 fn new(name: Arc<str>, state: kio::Producer<TrackState>, alive: Arc<Alive>) -> Self {
1360 let fetch = state.read().fetch.clone();
1361 fetch.lock().add_handler();
1362 Self {
1363 name,
1364 state,
1365 fetch,
1366 alive,
1367 }
1368 }
1369
1370 pub fn name(&self) -> &str {
1372 &self.name
1373 }
1374
1375 pub async fn requested_group(&self) -> Result<GroupRequest> {
1381 kio::wait(|waiter| self.poll_requested_group(waiter)).await
1382 }
1383
1384 pub fn poll_requested_group(&self, waiter: &kio::Waiter) -> Poll<Result<GroupRequest>> {
1386 poll_requested_group(&self.state, &self.fetch, waiter)
1387 }
1388
1389 pub fn poll_unused(&self, waiter: &kio::Waiter) -> Poll<()> {
1391 self.state.poll_unused(waiter).map(|_| ())
1392 }
1393}
1394
1395impl Clone for Dynamic {
1396 fn clone(&self) -> Self {
1397 self.fetch.lock().add_handler();
1399 Self {
1400 name: self.name.clone(),
1401 state: self.state.clone(),
1402 fetch: self.fetch.clone(),
1403 alive: self.alive.clone(),
1404 }
1405 }
1406}
1407
1408impl Drop for Dynamic {
1409 fn drop(&mut self) {
1410 let mut fetch = self.fetch.lock();
1416 if fetch.remove_handler() {
1417 fetch.drain_queued();
1418 }
1419 }
1420}
1421
1422struct Alive {
1431 name: Arc<str>,
1432 state: kio::Producer<TrackState>,
1433
1434 published: AtomicBool,
1437
1438 stats: OnceLock<stats::Subscription>,
1441}
1442
1443impl Alive {
1444 fn new(name: Arc<str>, state: kio::Producer<TrackState>) -> Arc<Self> {
1445 Arc::new(Self {
1446 name,
1447 state,
1448 published: Default::default(),
1449 stats: Default::default(),
1450 })
1451 }
1452
1453 fn publish(&self, stats: Option<&stats::Scope>) {
1457 self.published.store(true, Ordering::Relaxed);
1458 if let Some(scope) = stats {
1459 let _ = self.stats.set(scope.subscribe());
1462 }
1463 }
1464}
1465
1466impl Drop for Alive {
1467 fn drop(&mut self) {
1468 if !self.published.load(Ordering::Relaxed) {
1470 return;
1471 }
1472 match self.state.write() {
1480 Ok(mut state) => {
1481 if state.final_sequence.is_some() || state.abort.is_some() {
1482 return;
1483 }
1484 tracing::warn!(
1485 track = %self.name,
1486 "track::Producer dropped without finish() or abort()"
1487 );
1488 state.clear_cache();
1489 state.datagrams.clear();
1490 }
1491 Err(state) => {
1492 if state.final_sequence.is_some() || state.abort.is_some() {
1493 return;
1494 }
1495 tracing::warn!(
1496 track = %self.name,
1497 "track::Producer dropped without finish() or abort()"
1498 );
1499 }
1500 }
1501 }
1502}
1503
1504fn combined_subscription(subs: &Subscriptions, bound: Option<Duration>, waiter: &kio::Waiter) -> Option<Subscription> {
1510 let mut combined = None;
1511 for sub in subs.iter() {
1512 if sub.is_closed() {
1517 continue;
1518 }
1519 let _ = sub.poll_closed(waiter);
1524 if let Poll::Ready(Ok(sub)) = sub.poll(waiter, |sub| sub.poll_combined(&combined)) {
1525 combined = Some(sub);
1526 }
1527 }
1528 clamp_combined(combined, bound)
1529}
1530
1531fn snapshot_subscription(subs: &kio::Shared<Subscriptions>, bound: Option<Duration>) -> Option<Subscription> {
1533 let mut combined: Option<Subscription> = None;
1534 for sub in subs.read().iter() {
1535 if sub.is_closed() {
1537 continue;
1538 }
1539 if let Poll::Ready(merged) = sub.read().poll_combined(&combined) {
1540 combined = Some(merged);
1541 }
1542 }
1543 clamp_combined(combined, bound)
1544}
1545
1546fn clamp_combined(combined: Option<Subscription>, bound: Option<Duration>) -> Option<Subscription> {
1554 let mut combined = combined?;
1555 if let Some(bound) = bound {
1556 combined.latency_max = combined.latency_max.min(bound);
1557 }
1558 Some(combined)
1559}
1560
1561fn register_subscription(state: kio::Ref<'_, TrackState>, subscription: &kio::Producer<Subscription>) {
1565 if state.is_closed() {
1566 return;
1567 }
1568 let subs = state.subscriptions.clone();
1569 drop(state);
1570 subs.lock().push(subscription.consume());
1571}
1572
1573#[derive(Clone)]
1575pub(crate) struct TrackWeak {
1576 name: Arc<str>,
1577 state: kio::ProducerWeak<TrackState>,
1578}
1579
1580impl TrackWeak {
1581 pub fn consume(&self) -> Consumer {
1582 Consumer::plain(self.name.clone(), self.state.consume())
1583 }
1584
1585 pub(crate) fn name(&self) -> &Arc<str> {
1588 &self.name
1589 }
1590
1591 pub(crate) fn is_used(&self) -> bool {
1594 !self.state.is_closed() && self.state.is_used()
1595 }
1596
1597 pub(crate) fn poll_used(&self, waiter: &kio::Waiter) {
1600 let _ = self.state.poll_used(waiter);
1601 }
1602
1603 pub(crate) fn poll_unused(&self, waiter: &kio::Waiter) {
1606 let _ = self.state.poll_unused(waiter);
1607 }
1608}
1609
1610impl super::WeakEntry for TrackWeak {
1611 fn is_closed(&self) -> bool {
1612 self.state.is_closed()
1613 }
1614
1615 fn same_channel(&self, other: &Self) -> bool {
1616 self.state.same_channel(&other.state)
1617 }
1618}
1619
1620#[derive(Clone)]
1629pub struct Demand {
1630 name: Arc<str>,
1631 state: kio::ProducerWeak<TrackState>,
1632}
1633
1634impl Demand {
1635 pub fn name(&self) -> &str {
1637 &self.name
1638 }
1639
1640 pub async fn used(&self) -> Result<()> {
1642 self.state.used().await.map_err(|_| self.abort_reason())
1643 }
1644
1645 pub async fn unused(&self) -> Result<()> {
1647 self.state.unused().await.map_err(|_| self.abort_reason())
1648 }
1649
1650 pub async fn closed(&self) -> Error {
1652 self.state.closed().await;
1653 self.abort_reason()
1654 }
1655
1656 fn abort_reason(&self) -> Error {
1658 self.state.read().abort.clone().unwrap_or(Error::Dropped)
1659 }
1660}
1661
1662#[derive(Clone)]
1673pub struct Consumer {
1674 name: Arc<str>,
1675 inner: ConsumerKind,
1676 stats: stats::Scope,
1679}
1680
1681#[derive(Clone)]
1682enum ConsumerKind {
1683 Plain(kio::Consumer<TrackState>),
1684 Spliced(super::resume::Consumer),
1685}
1686
1687impl Consumer {
1688 fn plain(name: Arc<str>, state: kio::Consumer<TrackState>) -> Self {
1689 Self {
1690 name,
1691 inner: ConsumerKind::Plain(state),
1692 stats: stats::Scope::default(),
1693 }
1694 }
1695
1696 pub(crate) fn spliced(name: Arc<str>, resume: super::resume::Consumer) -> Self {
1698 Self {
1699 name,
1700 inner: ConsumerKind::Spliced(resume),
1701 stats: stats::Scope::default(),
1702 }
1703 }
1704
1705 pub(crate) fn with_stats(mut self, scope: stats::Scope) -> Self {
1708 self.stats = scope;
1709 self
1710 }
1711
1712 pub fn name(&self) -> &str {
1714 &self.name
1715 }
1716
1717 pub fn subscribe(&self, subscription: impl Into<Option<Subscription>>) -> kio::Pending<Subscribing> {
1723 let subscription = kio::Producer::new(subscription.into().unwrap_or_default());
1724
1725 let inner = match &self.inner {
1726 ConsumerKind::Plain(state) => {
1727 register_subscription(state.read(), &subscription);
1730 SubscribingKind::Plain(state.clone())
1731 }
1732 ConsumerKind::Spliced(resume) => SubscribingKind::Spliced(resume.clone()),
1734 };
1735
1736 kio::Pending::new(Subscribing {
1737 name: self.name.clone(),
1738 inner,
1739 subscription,
1740 stats: self.stats.clone(),
1741 })
1742 }
1743
1744 #[cfg(test)]
1748 pub(crate) fn peek_group(&self, sequence: u64) -> Option<group::Consumer> {
1749 match &self.inner {
1750 ConsumerKind::Plain(state) => state.read().cached_group(sequence),
1751 ConsumerKind::Spliced(_) => None,
1754 }
1755 }
1756
1757 pub fn fetch_group(&self, sequence: u64, options: impl Into<Option<group::Fetch>>) -> kio::Pending<Fetching> {
1769 let options = options.into().unwrap_or_default();
1770
1771 self.stats.fetch();
1775
1776 let state = match &self.inner {
1777 ConsumerKind::Plain(state) => state,
1778 ConsumerKind::Spliced(resume) => {
1781 return kio::Pending::new(Fetching {
1782 inner: FetchingKind::Spliced(resume.fetch_group(sequence, options)),
1783 stats: self.stats.clone(),
1784 });
1785 }
1786 };
1787
1788 let mut result = None;
1789
1790 let (fetch, unresolved) = {
1794 let state = state.read();
1795 (state.fetch.clone(), state.poll_fetch_cached(sequence).is_pending())
1796 };
1797
1798 if unresolved {
1799 let mut fetch = fetch.lock();
1800 if let Some(pending) = fetch.join(&sequence) {
1801 pending.priority = pending.priority.max(options.priority);
1804 result = Some(pending.result.consume());
1805 } else {
1806 let producer = kio::Producer::<FetchOutcome>::default();
1810 let consumer = producer.consume();
1811 let attempt = PendingFetch {
1812 priority: options.priority,
1813 result: producer,
1814 };
1815 if fetch.insert(sequence, attempt).is_ok() {
1816 result = Some(consumer);
1817 }
1818 }
1819 }
1820
1821 kio::Pending::new(Fetching {
1822 inner: FetchingKind::Plain {
1823 state: state.clone(),
1824 fetch,
1825 sequence,
1826 result,
1827 },
1828 stats: self.stats.clone(),
1829 })
1830 }
1831
1832 pub fn info(&self) -> kio::Pending<Querying> {
1839 kio::Pending::new(Querying {
1840 inner: match &self.inner {
1841 ConsumerKind::Plain(state) => QueryingKind::Plain(state.clone()),
1842 ConsumerKind::Spliced(resume) => QueryingKind::Spliced(resume.clone()),
1843 },
1844 })
1845 }
1846
1847 pub fn latest(&self) -> Option<u64> {
1849 match &self.inner {
1850 ConsumerKind::Plain(state) => state.read().max_sequence,
1851 ConsumerKind::Spliced(resume) => resume.latest(),
1852 }
1853 }
1854
1855 pub(crate) fn poll_complete(&self, waiter: &kio::Waiter) -> Poll<Result<()>> {
1860 let ConsumerKind::Plain(state) = &self.inner else {
1861 return Poll::Pending;
1863 };
1864 match ready!(state.poll(waiter, |state| {
1865 if state.is_complete() {
1866 Poll::Ready(())
1867 } else {
1868 Poll::Pending
1869 }
1870 })) {
1871 Ok(_) => Poll::Ready(Ok(())),
1872 Err(closed) => Poll::Ready(Err(closed.abort.clone().unwrap_or(Error::Dropped))),
1875 }
1876 }
1877}
1878
1879pub struct Subscribing {
1882 name: Arc<str>,
1883 inner: SubscribingKind,
1884 subscription: kio::Producer<Subscription>,
1885 stats: stats::Scope,
1886}
1887
1888enum SubscribingKind {
1889 Plain(kio::Consumer<TrackState>),
1890 Spliced(super::resume::Consumer),
1891}
1892
1893impl Subscribing {
1894 pub fn poll_ok(&self, waiter: &kio::Waiter) -> Poll<Result<Subscriber>> {
1897 match &self.inner {
1898 SubscribingKind::Plain(state) => {
1899 let info = ready!(state.poll(waiter, |state| state.poll_info()))
1901 .map_err(|e| e.abort.clone().unwrap_or(Error::Dropped))??;
1902
1903 Poll::Ready(Ok(Subscriber {
1904 name: self.name.clone(),
1905 info,
1906 inner: SubscriberKind::Plain(PlainSubscriber {
1907 state: state.clone(),
1908 subscription: self.subscription.clone(),
1909 index: 0,
1910 datagram_index: 0,
1911 min_sequence: 0,
1912 next_sequence: 0,
1913 end_sequence: None,
1914 parked: BTreeMap::new(),
1915 }),
1916 stats: self.stats.clone(),
1917 _stats_sub: self.stats.subscribe(),
1918 }))
1919 }
1920 SubscribingKind::Spliced(resume) => {
1921 let info = ready!(resume.poll_info(waiter))?;
1924
1925 Poll::Ready(Ok(Subscriber {
1926 name: self.name.clone(),
1927 info,
1928 inner: SubscriberKind::Spliced(Box::new(resume.subscribe_shared(self.subscription.clone()))),
1929 stats: self.stats.clone(),
1930 _stats_sub: self.stats.subscribe(),
1931 }))
1932 }
1933 }
1934 }
1935
1936 pub fn update(&mut self, subscription: Subscription) -> Result<()> {
1941 let mut state = self.subscription.write().map_err(|_| Error::Closed)?;
1942 *state = subscription;
1943 Ok(())
1944 }
1945}
1946
1947impl kio::Pollable for Subscribing {
1948 type Output = Result<Subscriber>;
1949
1950 fn poll(&self, waiter: &kio::Waiter) -> Poll<Self::Output> {
1951 self.poll_ok(waiter)
1952 }
1953}
1954
1955pub struct Querying {
1958 inner: QueryingKind,
1959}
1960
1961enum QueryingKind {
1962 Plain(kio::Consumer<TrackState>),
1963 Spliced(super::resume::Consumer),
1964}
1965
1966impl Querying {
1967 pub fn poll_ok(&self, waiter: &kio::Waiter) -> Poll<Result<Info>> {
1969 match &self.inner {
1970 QueryingKind::Plain(state) => {
1971 let info = ready!(state.poll(waiter, |state| state.poll_info()))
1973 .map_err(|e| e.abort.clone().unwrap_or(Error::Dropped))??;
1974 Poll::Ready(Ok(info))
1975 }
1976 QueryingKind::Spliced(resume) => resume.poll_info(waiter),
1977 }
1978 }
1979}
1980
1981impl kio::Pollable for Querying {
1982 type Output = Result<Info>;
1983
1984 fn poll(&self, waiter: &kio::Waiter) -> Poll<Self::Output> {
1985 self.poll_ok(waiter)
1986 }
1987}
1988
1989pub struct GroupRequest {
1998 state: kio::Producer<TrackState>,
1999 fetch: kio::Shared<FetchState>,
2001 sequence: u64,
2002 priority: u8,
2003 result: kio::Producer<FetchOutcome>,
2005 done: bool,
2006}
2007
2008impl GroupRequest {
2009 pub fn sequence(&self) -> u64 {
2011 self.sequence
2012 }
2013
2014 pub fn priority(&self) -> u8 {
2016 self.priority
2017 }
2018
2019 pub fn accept(mut self, info: impl Into<Option<Info>>) -> Result<group::Producer> {
2027 self.done = true;
2028 let res = TrackState::modify(&self.state)
2032 .and_then(|mut state| state.insert_group_request(self.sequence, info.into()));
2033 self.remove();
2034 res
2035 }
2036
2037 pub fn reject(mut self, err: Error) {
2039 self.done = true;
2040 self.remove();
2043 if let Ok(mut outcome) = self.result.write() {
2044 outcome.rejected = Some(err);
2045 }
2046 }
2047
2048 fn remove(&self) {
2051 self.fetch
2052 .lock()
2053 .remove_if(&self.sequence, |pending| pending.result.same_channel(&self.result));
2054 }
2055}
2056
2057impl Drop for GroupRequest {
2058 fn drop(&mut self) {
2059 if self.done {
2060 return;
2061 }
2062 self.remove();
2063 if let Ok(mut outcome) = self.result.write() {
2064 outcome.rejected = Some(Error::Dropped);
2065 }
2066 }
2067}
2068
2069pub struct Fetching {
2075 inner: FetchingKind,
2076 stats: stats::Scope,
2079}
2080
2081enum FetchingKind {
2082 Plain {
2083 state: kio::Consumer<TrackState>,
2084 fetch: kio::Shared<FetchState>,
2085 sequence: u64,
2086 result: Option<kio::Consumer<FetchOutcome>>,
2088 },
2089 Spliced(kio::Pending<super::resume::Fetching>),
2091}
2092
2093impl kio::Pollable for Fetching {
2094 type Output = Result<group::Consumer>;
2095
2096 fn poll(&self, waiter: &kio::Waiter) -> Poll<Self::Output> {
2097 let (state, fetch, sequence, result) = match &self.inner {
2098 FetchingKind::Plain {
2099 state,
2100 fetch,
2101 sequence,
2102 result,
2103 } => (state, fetch, *sequence, result.as_ref()),
2104 FetchingKind::Spliced(spliced) => {
2105 return kio::Pollable::poll(&**spliced, waiter)
2108 .map(|res| res.map(|group| group.with_meter(self.stats.meter())));
2109 }
2110 };
2111
2112 match state.poll(waiter, |state| state.poll_fetch_cached(sequence)) {
2115 Poll::Ready(Ok(res)) => return Poll::Ready(res.map(|group| group.with_meter(self.stats.meter()))),
2116 Poll::Ready(Err(closed)) => {
2117 return Poll::Ready(Err(closed.abort.clone().unwrap_or(Error::Dropped)));
2118 }
2119 Poll::Pending => {}
2120 }
2121
2122 let Some(result) = result else {
2124 return match fetch.poll(waiter, |fetch| match fetch.has_handlers() {
2127 false => Poll::Ready(()),
2128 true => Poll::Pending,
2129 }) {
2130 Poll::Ready(_guard) => Poll::Ready(Err(Error::NotFound)),
2131 Poll::Pending => Poll::Pending,
2132 };
2133 };
2134
2135 match result.poll(waiter, |outcome| match &outcome.rejected {
2138 Some(err) => Poll::Ready(err.clone()),
2139 None => Poll::Pending,
2140 }) {
2141 Poll::Ready(Ok(err)) => Poll::Ready(Err(err)),
2142 Poll::Ready(Err(_closed)) => Poll::Ready(Err(Error::NotFound)),
2143 Poll::Pending => Poll::Pending,
2144 }
2145 }
2146}
2147
2148pub struct Subscriber {
2171 name: Arc<str>,
2172 info: Info,
2173 inner: SubscriberKind,
2174 stats: stats::Scope,
2177 _stats_sub: stats::Subscription,
2180}
2181
2182enum SubscriberKind {
2183 Plain(PlainSubscriber),
2184 Spliced(Box<super::resume::Subscriber>),
2186}
2187
2188struct PlainSubscriber {
2190 state: kio::Consumer<TrackState>,
2191
2192 subscription: kio::Producer<Subscription>,
2193 index: usize,
2195 datagram_index: usize,
2197 min_sequence: u64,
2199 next_sequence: u64,
2202 end_sequence: Option<u64>,
2207 parked: BTreeMap<u64, group::Consumer>,
2212}
2213
2214impl PlainSubscriber {
2215 fn poll<F, R>(&self, waiter: &kio::Waiter, f: F) -> Poll<Result<R>>
2217 where
2218 F: Fn(&kio::Ref<'_, TrackState>) -> Poll<Result<R>>,
2219 {
2220 Poll::Ready(match ready!(self.state.poll(waiter, f)) {
2221 Ok(res) => res,
2222 Err(state) => Err(state.abort.clone().unwrap_or(Error::Dropped)),
2224 })
2225 }
2226
2227 fn poll_recv_group(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<group::Consumer>>> {
2228 let watch = |group: &group::Consumer| match group.poll_closed(waiter) {
2236 Poll::Pending => true,
2237 Poll::Ready(()) => !group.is_aborted(),
2238 };
2239
2240 let min_sequence = self.min_sequence;
2245 self.parked
2246 .retain(|sequence, group| *sequence >= min_sequence && watch(group));
2247
2248 if let Some(&sequence) = self.parked.keys().next()
2250 && self.end_sequence.is_none_or(|end| sequence <= end)
2251 {
2252 let group = self.parked.remove(&sequence).expect("parked key just observed");
2253 group.cache_refresh();
2255 return Poll::Ready(Ok(Some(group)));
2256 }
2257
2258 loop {
2259 let Some((consumer, found_index)) =
2260 ready!(self.poll(waiter, |state| state.poll_recv_group(self.index, self.min_sequence))?)
2261 else {
2262 if self.parked.is_empty() {
2265 return Poll::Ready(Ok(None));
2266 }
2267 return Poll::Pending;
2268 };
2269 self.index = found_index + 1;
2270
2271 if self.end_sequence.is_some_and(|end| consumer.sequence > end) {
2274 if watch(&consumer) {
2278 self.parked.insert(consumer.sequence, consumer);
2279 }
2280 continue;
2281 }
2282 return Poll::Ready(Ok(Some(consumer)));
2283 }
2284 }
2285
2286 fn poll_recv_datagram(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<Datagram>>> {
2287 let Some((datagram, found_index)) =
2288 ready!(self.poll(waiter, |state| state.poll_recv_datagram(self.datagram_index))?)
2289 else {
2290 return Poll::Ready(Ok(None));
2291 };
2292
2293 self.datagram_index = found_index + 1;
2294 self.next_sequence = self.next_sequence.max(datagram.sequence.saturating_add(1));
2295 Poll::Ready(Ok(Some(datagram)))
2296 }
2297
2298 fn poll_next_group(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<group::Consumer>>> {
2299 let floor = self.next_sequence.max(self.min_sequence);
2300 let Some(group) = ready!(self.poll(waiter, |state| state.poll_next_in_range(floor, self.end_sequence))?) else {
2301 return Poll::Ready(Ok(None));
2302 };
2303 self.next_sequence = group.sequence.saturating_add(1);
2304 Poll::Ready(Ok(Some(group)))
2305 }
2306
2307 fn poll_read_frame(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<frame::Frame>>> {
2308 let lower = self.min_sequence.max(self.next_sequence);
2309 let Some((frame, found_index, sequence)) =
2310 ready!(self.poll(waiter, |state| { state.poll_read_frame(self.index, lower, waiter) })?)
2311 else {
2312 return Poll::Ready(Ok(None));
2313 };
2314
2315 self.index = found_index + 1;
2316 self.next_sequence = sequence.saturating_add(1);
2317 Poll::Ready(Ok(Some(frame)))
2318 }
2319}
2320
2321#[derive(Clone)]
2327pub struct SubscriberControl {
2328 subscription: kio::Producer<Subscription>,
2329}
2330
2331impl SubscriberControl {
2332 pub fn subscription(&self) -> Subscription {
2334 self.subscription.read().clone()
2335 }
2336
2337 pub fn update(&self, subscription: Subscription) -> Result<()> {
2342 let mut state = self.subscription.write().map_err(|_| Error::Closed)?;
2343 *state = subscription;
2344 Ok(())
2345 }
2346}
2347
2348impl Subscriber {
2349 pub fn info(&self) -> &Info {
2354 &self.info
2355 }
2356
2357 pub fn name(&self) -> &str {
2359 &self.name
2360 }
2361
2362 pub fn control(&self) -> SubscriberControl {
2364 SubscriberControl {
2365 subscription: match &self.inner {
2366 SubscriberKind::Plain(plain) => plain.subscription.clone(),
2367 SubscriberKind::Spliced(spliced) => spliced.prefs(),
2368 },
2369 }
2370 }
2371
2372 pub fn poll_recv_group(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<group::Consumer>>> {
2389 let meter = self.stats.meter();
2390 let res = match &mut self.inner {
2391 SubscriberKind::Plain(plain) => plain.poll_recv_group(waiter),
2392 SubscriberKind::Spliced(spliced) => spliced.poll_recv_group(waiter),
2393 };
2394 res.map(|res| res.map(|group| group.map(|group| group.with_meter(meter))))
2395 }
2396
2397 pub async fn recv_group(&mut self) -> Result<Option<group::Consumer>> {
2404 kio::wait(|waiter| self.poll_recv_group(waiter)).await
2405 }
2406
2407 pub fn poll_recv_datagram(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<Datagram>>> {
2418 let meter = self.stats.meter();
2419 let res = match &mut self.inner {
2420 SubscriberKind::Plain(plain) => plain.poll_recv_datagram(waiter),
2421 SubscriberKind::Spliced(spliced) => spliced.poll_recv_datagram(waiter),
2422 };
2423 if let Poll::Ready(Ok(Some(datagram))) = &res {
2426 meter.datagram(datagram.payload.len() as u64);
2427 }
2428 res
2429 }
2430
2431 pub async fn recv_datagram(&mut self) -> Result<Option<Datagram>> {
2438 kio::wait(|waiter| self.poll_recv_datagram(waiter)).await
2439 }
2440
2441 pub fn poll_next_group(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<group::Consumer>>> {
2450 let meter = self.stats.meter();
2451 let res = match &mut self.inner {
2452 SubscriberKind::Plain(plain) => plain.poll_next_group(waiter),
2453 SubscriberKind::Spliced(spliced) => spliced.poll_next_group(waiter),
2454 };
2455 res.map(|res| res.map(|group| group.map(|group| group.with_meter(meter))))
2456 }
2457
2458 pub async fn next_group(&mut self) -> Result<Option<group::Consumer>> {
2464 kio::wait(|waiter| self.poll_next_group(waiter)).await
2465 }
2466
2467 pub fn poll_read_frame(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<frame::Frame>>> {
2471 let meter = self.stats.meter();
2472 let res = match &mut self.inner {
2473 SubscriberKind::Plain(plain) => plain.poll_read_frame(waiter),
2474 SubscriberKind::Spliced(spliced) => spliced.poll_read_frame(waiter),
2475 };
2476 if let Poll::Ready(Ok(Some(frame))) = &res {
2479 meter.group();
2480 meter.frames(1);
2481 meter.bytes(frame.payload.len() as u64);
2482 }
2483 res
2484 }
2485
2486 pub async fn read_frame(&mut self) -> Result<Option<frame::Frame>> {
2491 kio::wait(|waiter| self.poll_read_frame(waiter)).await
2492 }
2493
2494 pub fn is_clone(&self, other: &Self) -> bool {
2496 match (&self.inner, &other.inner) {
2497 (SubscriberKind::Plain(a), SubscriberKind::Plain(b)) => a.state.same_channel(&b.state),
2498 (SubscriberKind::Spliced(a), SubscriberKind::Spliced(b)) => a.is_clone(b),
2499 _ => false,
2500 }
2501 }
2502
2503 pub fn poll_finished(&mut self, waiter: &kio::Waiter) -> Poll<Result<u64>> {
2505 match &mut self.inner {
2506 SubscriberKind::Plain(plain) => plain.poll(waiter, |state| state.poll_finished()),
2507 SubscriberKind::Spliced(spliced) => spliced.poll_finished(waiter),
2508 }
2509 }
2510
2511 pub async fn finished(&mut self) -> Result<u64> {
2519 kio::wait(|waiter| self.poll_finished(waiter)).await
2520 }
2521
2522 pub fn start_at(&mut self, sequence: u64) {
2529 match &mut self.inner {
2530 SubscriberKind::Plain(plain) => plain.min_sequence = sequence,
2531 SubscriberKind::Spliced(spliced) => spliced.start_at(sequence),
2532 }
2533 }
2534
2535 pub fn end_at(&mut self, sequence: impl Into<Option<u64>>) {
2548 match &mut self.inner {
2549 SubscriberKind::Plain(plain) => plain.end_sequence = sequence.into(),
2550 SubscriberKind::Spliced(spliced) => spliced.end_at(sequence),
2551 }
2552 }
2553
2554 pub fn subscription(&self) -> Subscription {
2556 self.control().subscription()
2557 }
2558
2559 pub fn update(&mut self, subscription: Subscription) -> Result<()> {
2565 match &mut self.inner {
2566 SubscriberKind::Plain(plain) => {
2567 let mut state = plain.subscription.write().map_err(|_| Error::Closed)?;
2568 *state = subscription;
2569 }
2570 SubscriberKind::Spliced(spliced) => spliced.update(subscription),
2571 }
2572 Ok(())
2573 }
2574
2575 pub fn latest(&self) -> Option<u64> {
2577 match &self.inner {
2578 SubscriberKind::Plain(plain) => plain.state.read().max_sequence,
2579 SubscriberKind::Spliced(spliced) => spliced.latest(),
2580 }
2581 }
2582}
2583
2584pub struct Request {
2596 name: Arc<str>,
2597 broadcast: Arc<broadcast::Info>,
2599 state: kio::Producer<TrackState>,
2600
2601 prev_subscription: Option<Subscription>,
2603
2604 alive: Arc<Alive>,
2607
2608 _dynamic: Dynamic,
2613
2614 stats: stats::Scope,
2617}
2618
2619impl Request {
2620 pub(crate) fn new(broadcast: Arc<broadcast::Info>, name: impl Into<Arc<str>>) -> Self {
2621 let name = name.into();
2622 let state = TrackState::spawn(broadcast.clone());
2623 let alive = Alive::new(name.clone(), state.clone());
2624 let dynamic = Dynamic::new(name.clone(), state.clone(), alive.clone());
2625 Self {
2626 name,
2627 broadcast,
2628 state,
2629 prev_subscription: None,
2630 alive,
2631 _dynamic: dynamic,
2632 stats: stats::Scope::default(),
2633 }
2634 }
2635
2636 pub(crate) fn with_stats(mut self, scope: stats::Scope) -> Self {
2639 self.stats = scope;
2640 self
2641 }
2642
2643 pub fn name(&self) -> &str {
2645 &self.name
2646 }
2647
2648 pub fn consume(&self) -> Consumer {
2650 Consumer::plain(self.name.clone(), self.state.consume())
2651 }
2652
2653 pub fn dynamic(&self) -> Dynamic {
2657 Dynamic::new(self.name.clone(), self.state.clone(), self.alive.clone())
2658 }
2659
2660 pub fn poll_unused(&self, waiter: &kio::Waiter) -> Poll<()> {
2663 self.state.poll_unused(waiter).map(|_| ())
2664 }
2665
2666 pub fn accept(self, info: impl Into<Option<Info>>) -> Producer {
2673 if let Ok(mut state) = self.state.write() {
2676 state.install(info.into().unwrap_or_default());
2677 }
2678 self.alive.publish(Some(&self.stats));
2681 Producer {
2682 name: self.name,
2683 broadcast: self.broadcast,
2684 state: self.state,
2685 prev_subscription: None,
2686 alive: self.alive,
2687 stats: self.stats,
2688 }
2689 }
2690
2691 pub fn reject(self, err: Error) {
2693 if let Ok(mut state) = self.state.write() {
2694 state.abort = Some(err);
2695 }
2696 }
2697
2698 pub fn subscription(&self) -> Option<Subscription> {
2701 let state = self.state.read();
2702 let (subs, bound) = (state.subscriptions.clone(), state.latency_bound());
2703 drop(state);
2704 snapshot_subscription(&subs, bound)
2705 }
2706
2707 pub async fn subscription_changed(&mut self) -> Option<Subscription> {
2710 kio::wait(|waiter| self.poll_subscription_changed(waiter)).await
2711 }
2712
2713 pub fn poll_subscription_changed(&mut self, waiter: &kio::Waiter) -> Poll<Option<Subscription>> {
2715 let state = self.state.read();
2716 let (subs, bound) = (state.subscriptions.clone(), state.latency_bound());
2717 drop(state);
2718
2719 let prev = &self.prev_subscription;
2720 let mut combined = None;
2721 let mut guard = ready!(subs.poll(waiter, |subs| {
2722 let next = combined_subscription(subs, bound, waiter);
2723 if &next == prev {
2724 Poll::Pending
2725 } else {
2726 combined = next;
2727 Poll::Ready(())
2728 }
2729 }));
2730 guard.retain(|sub| !sub.is_closed());
2732 drop(guard);
2733 self.prev_subscription = combined.clone();
2734 Poll::Ready(combined)
2735 }
2736
2737 pub(super) fn weak(&self) -> TrackWeak {
2738 TrackWeak {
2739 name: self.name.clone(),
2740 state: self.state.weak(),
2741 }
2742 }
2743}
2744
2745#[cfg(test)]
2746use futures::FutureExt;
2747
2748#[cfg(test)]
2749#[allow(missing_docs)] impl Subscriber {
2751 pub fn assert_group(&mut self) -> group::Consumer {
2752 self.recv_group()
2753 .now_or_never()
2754 .expect("group would have blocked")
2755 .expect("would have errored")
2756 .expect("track was closed")
2757 }
2758
2759 pub fn assert_no_group(&mut self) {
2760 assert!(
2761 self.recv_group().now_or_never().is_none(),
2762 "recv_group would not have blocked"
2763 );
2764 }
2765
2766 pub fn assert_not_closed(&mut self) {
2767 assert!(self.finished().now_or_never().is_none(), "should not be closed");
2768 }
2769
2770 pub fn assert_closed(&mut self) {
2771 assert!(self.finished().now_or_never().is_some(), "should be closed");
2772 }
2773
2774 pub fn assert_error(&mut self) {
2776 assert!(
2777 self.finished().now_or_never().expect("should not block").is_err(),
2778 "should be error"
2779 );
2780 }
2781
2782 pub fn assert_is_clone(&self, other: &Self) {
2783 assert!(self.is_clone(other), "should be clone");
2784 }
2785
2786 pub fn assert_not_clone(&self, other: &Self) {
2787 assert!(!self.is_clone(other), "should not be clone");
2788 }
2789}
2790
2791#[cfg(test)]
2792mod test {
2793 use super::*;
2794 use crate::model::test_tracing::count_drop_warnings;
2795
2796 fn track_producer(name: impl Into<Arc<str>>, info: impl Into<Option<Info>>) -> Producer {
2799 Producer::new(Arc::new(broadcast::Info::default()), name, info)
2800 }
2801
2802 fn live_groups(state: &TrackState) -> usize {
2804 state.lookup.len()
2805 }
2806
2807 fn first_live_sequence(state: &TrackState) -> u64 {
2809 state
2810 .arrival
2811 .iter()
2812 .find(|(sequence, stamp)| state.lookup.get(sequence).is_some_and(|slot| slot.stamp == *stamp))
2813 .map(|(sequence, _)| *sequence)
2814 .unwrap()
2815 }
2816
2817 fn recv_datagram(dg: &mut Subscriber) -> Datagram {
2819 dg.recv_datagram()
2820 .now_or_never()
2821 .expect("datagram would have blocked")
2822 .expect("would have errored")
2823 .expect("track was closed")
2824 }
2825
2826 #[tokio::test]
2827 async fn append_datagram_shares_group_sequence() {
2828 let mut producer = track_producer("test", None);
2829 let ts = Timestamp::from_millis(10).unwrap();
2830
2831 assert_eq!(producer.append_group().unwrap().sequence, 0);
2833 assert_eq!(producer.append_datagram(ts, &b"a"[..]).unwrap(), 1);
2834 assert_eq!(producer.append_group().unwrap().sequence, 2);
2835 assert_eq!(producer.append_datagram(ts, &b"b"[..]).unwrap(), 3);
2836 assert_eq!(producer.latest(), Some(3));
2837 }
2838
2839 #[tokio::test]
2840 async fn append_datagram_roundtrip() {
2841 let mut producer = track_producer("test", None);
2842 let mut dg = producer.subscribe(None);
2843
2844 let ts = Timestamp::from_millis(42).unwrap();
2845 let seq = producer.append_datagram(ts, &b"hello"[..]).unwrap();
2846
2847 let got = recv_datagram(&mut dg);
2848 assert_eq!(got.sequence, seq);
2849 assert_eq!(got.timestamp, ts);
2850 assert_eq!(&got.payload[..], b"hello");
2851 }
2852
2853 #[tokio::test]
2854 async fn write_datagram_preserves_sequence() {
2855 let mut producer = track_producer("test", None);
2856 let mut dg = producer.subscribe(None);
2857
2858 let ts = Timestamp::from_millis(5).unwrap();
2859 producer
2861 .write_datagram(Datagram {
2862 sequence: 100,
2863 timestamp: ts,
2864 payload: bytes::Bytes::from_static(b"x"),
2865 })
2866 .unwrap();
2867
2868 assert_eq!(recv_datagram(&mut dg).sequence, 100);
2869 assert_eq!(producer.append_group().unwrap().sequence, 101);
2871 }
2872
2873 #[tokio::test]
2874 async fn recv_datagram_advances_ordered_group_cursor() {
2875 let mut producer = track_producer("test", None);
2876 let mut subscriber = producer.subscribe(None);
2877 let ts = Timestamp::from_millis(5).unwrap();
2878
2879 producer
2880 .write_datagram(Datagram {
2881 sequence: 5,
2882 timestamp: ts,
2883 payload: bytes::Bytes::from_static(b"x"),
2884 })
2885 .unwrap();
2886 assert_eq!(recv_datagram(&mut subscriber).sequence, 5);
2887
2888 producer.create_group(group::Info { sequence: 3 }).unwrap();
2889 producer.create_group(group::Info { sequence: 6 }).unwrap();
2890
2891 let group = subscriber
2892 .next_group()
2893 .now_or_never()
2894 .expect("group would have blocked")
2895 .expect("would have errored")
2896 .expect("track was closed");
2897 assert_eq!(group.sequence, 6);
2898 }
2899
2900 #[tokio::test]
2901 async fn datagram_normalized_to_track_timescale() {
2902 let info = Info::default().with_timescale(Timescale::MICRO);
2903 let mut producer = track_producer("test", info);
2904 let mut dg = producer.subscribe(None);
2905
2906 producer
2908 .append_datagram(Timestamp::from_millis(2).unwrap(), &b"z"[..])
2909 .unwrap();
2910 let got = recv_datagram(&mut dg);
2911 assert_eq!(got.timestamp.scale(), Timescale::MICRO);
2912 assert_eq!(got.timestamp.value(), 2_000);
2913 }
2914
2915 #[tokio::test]
2916 async fn datagram_rejects_oversized() {
2917 let mut producer = track_producer("test", None);
2918 let big = bytes::Bytes::from(vec![0u8; crate::model::datagram::MAX_DATAGRAM_PAYLOAD + 1]);
2919 let ts = Timestamp::from_millis(0).unwrap();
2920 assert!(matches!(
2921 producer.append_datagram(ts, big.clone()),
2922 Err(Error::FrameTooLarge)
2923 ));
2924 assert!(matches!(
2925 producer.write_datagram(Datagram {
2926 sequence: 0,
2927 timestamp: ts,
2928 payload: big,
2929 }),
2930 Err(Error::FrameTooLarge)
2931 ));
2932 }
2933
2934 #[tokio::test]
2935 async fn datagram_fanout_to_subscribers() {
2936 let mut producer = track_producer("test", None);
2937 let mut a = producer.subscribe(None);
2939 let mut b = producer.subscribe(None);
2940 let ts = Timestamp::from_millis(1).unwrap();
2941
2942 producer.append_datagram(ts, &b"first"[..]).unwrap();
2943 producer.append_datagram(ts, &b"second"[..]).unwrap();
2944
2945 assert_eq!(&recv_datagram(&mut a).payload[..], b"first");
2947 assert_eq!(&recv_datagram(&mut a).payload[..], b"second");
2948 assert_eq!(&recv_datagram(&mut b).payload[..], b"first");
2949 assert_eq!(&recv_datagram(&mut b).payload[..], b"second");
2950 }
2951
2952 #[tokio::test]
2953 async fn datagram_evicts_stale() {
2954 tokio::time::pause();
2955
2956 let mut producer = track_producer("test", None);
2957 let mut dg = producer.subscribe(None);
2958 let ts = Timestamp::from_millis(0).unwrap();
2959
2960 producer.append_datagram(ts, &b"old"[..]).unwrap(); tokio::time::advance(MAX_DATAGRAM_AGE + Duration::from_millis(10)).await;
2964 producer.append_datagram(ts, &b"new"[..]).unwrap(); let got = recv_datagram(&mut dg);
2968 assert_eq!(got.sequence, 1);
2969 assert_eq!(&got.payload[..], b"new");
2970 }
2971
2972 #[tokio::test]
2973 async fn datagram_recv_pends_until_written() {
2974 let mut producer = track_producer("test", None);
2975 let mut dg = producer.subscribe(None);
2976
2977 assert!(
2978 dg.recv_datagram().now_or_never().is_none(),
2979 "should block with no datagrams"
2980 );
2981
2982 producer
2983 .append_datagram(Timestamp::from_millis(0).unwrap(), &b"go"[..])
2984 .unwrap();
2985 assert_eq!(&recv_datagram(&mut dg).payload[..], b"go");
2986 }
2987
2988 #[tokio::test]
2992 async fn datagram_wire_roundtrip_between_tracks() {
2993 use crate::coding::{Decode, Encode};
2994 use crate::lite;
2995
2996 let version = lite::Version::Lite05;
2997
2998 let mut origin = track_producer("test", None);
3000 let mut origin_dg = origin.subscribe(None);
3001 let ts = Timestamp::from_millis(7).unwrap();
3002 let seq = origin.append_datagram(ts, &b"payload"[..]).unwrap();
3003
3004 let d = recv_datagram(&mut origin_dg);
3005 let body = lite::Datagram {
3006 subscribe: 5,
3007 sequence: d.sequence,
3008 timestamp: d.timestamp.value(),
3009 payload: d.payload.clone(),
3010 }
3011 .encode_bytes(version)
3012 .unwrap();
3013
3014 let mut slice = &body[..];
3016 let wire = lite::Datagram::decode(&mut slice, version).unwrap();
3017 let mut downstream = track_producer("test", None);
3018 let mut downstream_dg = downstream.subscribe(None);
3019 downstream
3020 .write_datagram(Datagram {
3021 sequence: wire.sequence,
3022 timestamp: Timestamp::new(wire.timestamp, Timescale::MILLI).unwrap(),
3023 payload: wire.payload,
3024 })
3025 .unwrap();
3026
3027 let got = recv_datagram(&mut downstream_dg);
3028 assert_eq!(got.sequence, seq);
3029 assert_eq!(got.timestamp, ts);
3030 assert_eq!(&got.payload[..], b"payload");
3031 }
3032
3033 #[tokio::test]
3034 async fn evict_expired_groups() {
3035 tokio::time::pause();
3036
3037 let mut producer = track_producer("test", None);
3038
3039 producer.append_group().unwrap(); producer.append_group().unwrap(); producer.append_group().unwrap(); {
3045 let state = producer.state.read();
3046 assert_eq!(live_groups(&state), 3);
3047 assert_eq!(state.offset, 0);
3048 }
3049
3050 tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
3052
3053 producer.append_group().unwrap(); {
3060 let state = producer.state.read();
3061 assert_eq!(live_groups(&state), 1);
3062 assert_eq!(first_live_sequence(&state), 3);
3063 assert_eq!(state.offset, 3);
3064 assert!(!state.lookup.contains_key(&0));
3065 assert!(!state.lookup.contains_key(&1));
3066 assert!(!state.lookup.contains_key(&2));
3067 assert!(state.lookup.contains_key(&3));
3068 }
3069 }
3070
3071 #[tokio::test]
3075 async fn aging_out_a_finished_group_keeps_the_clean_end() {
3076 tokio::time::pause();
3077
3078 let mut producer = track_producer("test", None);
3079 let mut group = producer.create_group(group::Info { sequence: 0 }).unwrap();
3080 let mut consumer = group.consume();
3081
3082 group
3083 .write_frame(Timestamp::from_millis(0).unwrap(), b"hello".as_slice())
3084 .unwrap();
3085 assert_eq!(consumer.next_frame().await.unwrap().unwrap().size, 5);
3086
3087 tokio::time::advance(DEFAULT_LATENCY_MAX * 12).await;
3089 group.finish().unwrap();
3090 let _next = producer.create_group(group::Info { sequence: 1 }).unwrap();
3091
3092 assert!(consumer.next_frame().await.unwrap().is_none());
3093 }
3094
3095 #[tokio::test]
3099 async fn active_reader_survives_expiry() {
3100 tokio::time::pause();
3101
3102 let mut producer = track_producer("test", None);
3103 let mut subscriber = producer.subscribe(None);
3104
3105 let mut group = producer.create_group(0u64.into()).unwrap();
3107 for _ in 0..10 {
3108 group.write_frame(Timestamp::ZERO, b"x".as_slice()).unwrap();
3109 }
3110 group.finish().unwrap();
3111 let mut reading = subscriber.assert_group();
3112
3113 producer.create_group(1u64.into()).unwrap().finish().unwrap();
3115
3116 for seq in 2..12u64 {
3119 tokio::time::advance(DEFAULT_LATENCY_MAX / 2).await;
3120 let frame = reading.next_frame().await;
3121 assert!(
3122 matches!(frame, Ok(Some(_))),
3123 "an actively-read group must not expire mid-read (step {seq})"
3124 );
3125 producer.create_group(seq.into()).unwrap().finish().unwrap();
3126 }
3127
3128 let state = producer.state.read();
3129 assert!(state.lookup.contains_key(&0), "the read group survived");
3130 assert!(!state.lookup.contains_key(&1), "the unread group still expired");
3131 }
3132
3133 #[tokio::test]
3138 async fn slow_prefetch_reader_survives_expiry() {
3139 tokio::time::pause();
3140
3141 let mut producer = track_producer("test", None);
3142 let mut subscriber = producer.subscribe(None);
3143
3144 let mut group = producer.create_group(0u64.into()).unwrap();
3145 for _ in 0..20 {
3146 group.write_frame(Timestamp::ZERO, b"x".as_slice()).unwrap();
3147 }
3148 group.finish().unwrap();
3149 let mut reading = subscriber.assert_group();
3150
3151 for seq in 1..20u64 {
3154 tokio::time::advance(DEFAULT_LATENCY_MAX / 2).await;
3155 let frame = reading.read_frame().await;
3156 assert!(
3157 matches!(frame, Ok(Some(_))),
3158 "a slow prefetch reader must not expire mid-read (step {seq})"
3159 );
3160 producer.create_group(seq.into()).unwrap().finish().unwrap();
3161 }
3162 }
3163
3164 #[tokio::test]
3168 async fn delivery_restarts_the_expiry_clock() {
3169 tokio::time::pause();
3170
3171 let mut producer = track_producer("test", None);
3172 let mut subscriber = producer.subscribe(None);
3173
3174 let mut group = producer.create_group(0u64.into()).unwrap();
3175 group.write_frame(Timestamp::ZERO, b"x".as_slice()).unwrap();
3176 group.finish().unwrap();
3177 producer.create_group(1u64.into()).unwrap().finish().unwrap();
3179
3180 tokio::time::advance(DEFAULT_LATENCY_MAX - Duration::from_secs(1)).await;
3182 let mut reading = subscriber.assert_group();
3183
3184 tokio::time::advance(DEFAULT_LATENCY_MAX - Duration::from_secs(1)).await;
3187 producer.create_group(2u64.into()).unwrap().finish().unwrap();
3188
3189 let frame = reading.read_frame().await.unwrap();
3190 assert!(frame.is_some(), "a just-delivered group must not expire unread");
3191 }
3192
3193 #[tokio::test]
3197 async fn streaming_frame_writes_keep_the_group_alive() {
3198 tokio::time::pause();
3199
3200 let mut producer = track_producer("test", None);
3201 let mut straggler = producer.create_group(0u64.into()).unwrap();
3202 producer.create_group(1u64.into()).unwrap().finish().unwrap();
3204
3205 let mut frame = straggler
3206 .create_frame(frame::Info {
3207 size: 10,
3208 timestamp: Timestamp::ZERO,
3209 })
3210 .unwrap();
3211 for seq in 2..12u64 {
3214 tokio::time::advance(DEFAULT_LATENCY_MAX / 2).await;
3215 frame.write(bytes::Bytes::from_static(b"x")).unwrap();
3216 producer.create_group(seq.into()).unwrap().finish().unwrap();
3217 }
3218 frame.finish().unwrap();
3219 straggler.finish().unwrap();
3220
3221 let state = producer.state.read();
3222 assert!(
3223 state.lookup.contains_key(&0),
3224 "a group streaming a frame survives expiry"
3225 );
3226 }
3227
3228 #[tokio::test]
3231 async fn parked_reoffer_restarts_the_expiry_clock() {
3232 tokio::time::pause();
3233
3234 let mut producer = track_producer("test", None);
3235 let mut subscriber = producer.subscribe(None);
3236 subscriber.end_at(0);
3237
3238 for seq in 0..2u64 {
3239 let mut group = producer.create_group(seq.into()).unwrap();
3240 group.write_frame(Timestamp::ZERO, b"x".as_slice()).unwrap();
3241 group.finish().unwrap();
3242 }
3243
3244 assert_eq!(subscriber.assert_group().sequence, 0);
3246 subscriber.assert_no_group();
3247
3248 tokio::time::advance(DEFAULT_LATENCY_MAX - Duration::from_secs(1)).await;
3250 subscriber.end_at(1);
3251 let mut reading = subscriber.assert_group();
3252 assert_eq!(reading.sequence, 1);
3253
3254 tokio::time::advance(DEFAULT_LATENCY_MAX - Duration::from_secs(1)).await;
3257 producer.create_group(2u64.into()).unwrap().finish().unwrap();
3258
3259 let frame = reading.read_frame().await.unwrap();
3260 assert!(frame.is_some(), "a just-re-offered group must not expire unread");
3261 }
3262
3263 #[tokio::test]
3264 async fn evict_keeps_max_sequence() {
3265 tokio::time::pause();
3266
3267 let mut producer = track_producer("test", None);
3268 producer.append_group().unwrap(); tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
3272
3273 producer.append_group().unwrap(); {
3277 let state = producer.state.read();
3278 assert_eq!(live_groups(&state), 1);
3279 assert_eq!(first_live_sequence(&state), 1);
3280 assert_eq!(state.offset, 1);
3281 }
3282 }
3283
3284 #[tokio::test]
3285 async fn no_eviction_when_fresh() {
3286 tokio::time::pause();
3287
3288 let mut producer = track_producer("test", None);
3289 producer.append_group().unwrap(); producer.append_group().unwrap(); producer.append_group().unwrap(); {
3294 let state = producer.state.read();
3295 assert_eq!(live_groups(&state), 3);
3296 assert_eq!(state.offset, 0);
3297 }
3298 }
3299
3300 #[tokio::test]
3301 async fn consumer_skips_evicted_groups() {
3302 tokio::time::pause();
3303
3304 let mut producer = track_producer("test", None);
3305 producer.append_group().unwrap(); let mut consumer = producer.subscribe(None);
3308
3309 tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
3310 producer.append_group().unwrap(); let group = consumer.assert_group();
3314 assert_eq!(group.sequence, 1);
3315 }
3316
3317 #[tokio::test]
3318 async fn cache_age_controls_eviction() {
3319 tokio::time::pause();
3320
3321 let mut producer = track_producer("test", Info::default().with_latency_max(Duration::from_secs(1)));
3323 producer.append_group().unwrap(); tokio::time::advance(Duration::from_secs(2)).await;
3327 producer.append_group().unwrap(); let state = producer.state.read();
3331 assert_eq!(live_groups(&state), 1);
3332 assert_eq!(first_live_sequence(&state), 1);
3333 }
3334
3335 #[test]
3336 fn latency_max_clamped_to_cache() {
3337 let producer = track_producer("test", Info::default().with_latency_max(Duration::from_secs(2)));
3338
3339 let mut subscriber = producer.subscribe(Subscription::default().with_latency_max(Duration::from_secs(10)));
3343 assert_eq!(subscriber.subscription().latency_max, Duration::from_secs(10));
3344 assert_eq!(producer.subscription().unwrap().latency_max, Duration::from_secs(2));
3345
3346 subscriber
3348 .update(Subscription::default().with_latency_max(Duration::from_millis(500)))
3349 .unwrap();
3350 assert_eq!(producer.subscription().unwrap().latency_max, Duration::from_millis(500));
3351
3352 subscriber
3353 .update(Subscription::default().with_latency_max(Duration::ZERO))
3354 .unwrap();
3355 assert_eq!(producer.subscription().unwrap().latency_max, Duration::ZERO);
3356 }
3357
3358 fn track_producer_capped(name: impl Into<Arc<str>>, info: Info, cap: Duration) -> Producer {
3361 let origin = crate::origin::Info::default().with_cache_duration(cap);
3362 Producer::new(Arc::new(broadcast::Info { origin }), name, info)
3363 }
3364
3365 #[test]
3366 fn origin_cache_duration_clamps_latency_max() {
3367 let capped = track_producer_capped(
3370 "test",
3371 Info::default().with_latency_max(Duration::from_secs(60)),
3372 Duration::from_secs(1),
3373 );
3374 assert_eq!(capped.state.read().latency_bound(), Some(Duration::from_secs(1)));
3375
3376 let under = track_producer_capped(
3377 "test",
3378 Info::default().with_latency_max(Duration::from_millis(500)),
3379 Duration::from_secs(1),
3380 );
3381 assert_eq!(under.state.read().latency_bound(), Some(Duration::from_millis(500)));
3382 }
3383
3384 #[tokio::test]
3385 async fn origin_cache_duration_caps_eviction() {
3386 tokio::time::pause();
3387
3388 let mut producer = track_producer_capped(
3390 "test",
3391 Info::default().with_latency_max(Duration::from_secs(60)),
3392 Duration::from_secs(1),
3393 );
3394 producer.append_group().unwrap(); tokio::time::advance(Duration::from_secs(2)).await;
3398 producer.append_group().unwrap(); let state = producer.state.read();
3402 assert_eq!(live_groups(&state), 1);
3403 assert_eq!(first_live_sequence(&state), 1);
3404 }
3405
3406 #[test]
3407 fn latency_max_clamped_via_every_update_path() {
3408 let producer = track_producer("test", Info::default().with_latency_max(Duration::from_secs(2)));
3409 let over = Subscription::default().with_latency_max(Duration::from_secs(10));
3410
3411 let mut subscriber = producer.subscribe(over.clone());
3414 assert_eq!(producer.subscription().unwrap().latency_max, Duration::from_secs(2));
3415
3416 subscriber.control().update(over.clone()).unwrap();
3417 assert_eq!(producer.subscription().unwrap().latency_max, Duration::from_secs(2));
3418
3419 subscriber.update(over).unwrap();
3420 assert_eq!(producer.subscription().unwrap().latency_max, Duration::from_secs(2));
3421 }
3422
3423 #[test]
3424 fn latency_max_aggregate_clamps_the_max_across_subscribers() {
3425 let producer = track_producer("test", Info::default().with_latency_max(Duration::from_secs(2)));
3426
3427 let _a = producer.subscribe(Subscription::default().with_latency_max(Duration::from_millis(500)));
3430 let _b = producer.subscribe(Subscription::default().with_latency_max(Duration::from_secs(10)));
3431
3432 assert_eq!(producer.subscription().unwrap().latency_max, Duration::from_secs(2));
3433 }
3434
3435 #[test]
3436 fn subscriber_control_updates_while_read_future_is_pending() {
3437 let producer = track_producer("test", None);
3438 let mut subscriber = producer.subscribe(None);
3439 let control = subscriber.control();
3440
3441 let mut recv = Box::pin(subscriber.recv_group());
3442 assert!(recv.as_mut().now_or_never().is_none());
3443
3444 control
3445 .update(Subscription::default().with_priority(7).with_ordered(false))
3446 .unwrap();
3447
3448 let aggregate = producer.subscription().expect("expected an active subscription");
3449 assert_eq!(aggregate.priority, 7);
3450 assert!(!aggregate.ordered);
3451 }
3452
3453 #[test]
3454 fn dropped_subscriber_leaves_no_ghost_in_aggregate() {
3455 let mut producer = track_producer("test", None);
3460 let a = producer.subscribe(Subscription::default().with_priority(5));
3461
3462 let waiter = kio::Waiter::noop();
3464 assert!(
3465 matches!(producer.poll_subscription_changed(&waiter), Poll::Ready(Ok(Some(_)))),
3466 "one live subscriber should aggregate to Some",
3467 );
3468
3469 drop(a);
3471
3472 assert!(
3474 matches!(producer.poll_subscription_changed(&waiter), Poll::Ready(Ok(None))),
3475 "a dropped subscriber must not linger in the aggregate",
3476 );
3477
3478 assert!(
3480 producer.subscription().is_none(),
3481 "snapshot must exclude a dropped subscriber",
3482 );
3483 }
3484
3485 #[test]
3486 fn dropped_subscriber_wakes_the_aggregate() {
3487 use std::sync::atomic::{AtomicBool, Ordering};
3494
3495 let mut producer = track_producer("test", None);
3496 let a = producer.subscribe(Subscription::default().with_priority(5));
3497
3498 let woken = Arc::new(AtomicBool::new(false));
3499 let waiter = kio::Waiter::new(futures::task::waker(Arc::new(FlagWake(woken.clone()))));
3500
3501 assert!(matches!(
3503 producer.poll_subscription_changed(&waiter),
3504 Poll::Ready(Ok(Some(_)))
3505 ));
3506 assert!(
3507 producer.poll_subscription_changed(&waiter).is_pending(),
3508 "the aggregate is unchanged, so this poll must park",
3509 );
3510 assert!(!woken.load(Ordering::SeqCst), "nothing happened yet");
3511
3512 drop(a);
3513 assert!(
3514 woken.load(Ordering::SeqCst),
3515 "the last subscriber leaving must wake the aggregate watcher",
3516 );
3517 }
3518
3519 struct FlagWake(Arc<std::sync::atomic::AtomicBool>);
3521
3522 impl futures::task::ArcWake for FlagWake {
3523 fn wake_by_ref(arc_self: &Arc<Self>) {
3524 arc_self.0.store(true, std::sync::atomic::Ordering::SeqCst);
3525 }
3526 }
3527
3528 #[tokio::test]
3529 async fn out_of_order_max_sequence_at_front() {
3530 tokio::time::pause();
3531
3532 let mut producer = track_producer("test", None);
3533
3534 producer.create_group(group::Info { sequence: 5 }).unwrap();
3536 producer.create_group(group::Info { sequence: 3 }).unwrap();
3537 producer.create_group(group::Info { sequence: 4 }).unwrap();
3538
3539 {
3541 let state = producer.state.read();
3542 assert_eq!(state.max_sequence, Some(5));
3543 }
3544
3545 tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
3547
3548 producer.append_group().unwrap(); {
3554 let state = producer.state.read();
3555 assert_eq!(live_groups(&state), 1);
3556 assert_eq!(first_live_sequence(&state), 6);
3557 assert!(!state.lookup.contains_key(&3));
3558 assert!(!state.lookup.contains_key(&4));
3559 assert!(!state.lookup.contains_key(&5));
3560 assert!(state.lookup.contains_key(&6));
3561 }
3562 }
3563
3564 #[tokio::test]
3565 async fn max_sequence_at_front_blocks_trim() {
3566 tokio::time::pause();
3567
3568 let mut producer = track_producer("test", None);
3569
3570 producer.create_group(group::Info { sequence: 5 }).unwrap();
3572
3573 tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
3574
3575 producer.create_group(group::Info { sequence: 3 }).unwrap();
3577
3578 {
3581 let state = producer.state.read();
3582 assert_eq!(live_groups(&state), 2);
3583 assert_eq!(state.offset, 0);
3584 }
3585
3586 tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
3588
3589 producer.create_group(group::Info { sequence: 2 }).unwrap();
3591
3592 {
3597 let state = producer.state.read();
3598 assert_eq!(live_groups(&state), 2);
3599 assert_eq!(state.offset, 0);
3600 assert!(state.lookup.contains_key(&5));
3601 assert!(!state.lookup.contains_key(&3));
3602 assert!(state.lookup.contains_key(&2));
3603 }
3604
3605 let mut consumer = producer.subscribe(None);
3607 let group = consumer.assert_group();
3608 assert_eq!(group.sequence, 5);
3610 }
3611
3612 #[tokio::test]
3613 async fn abort_clears_cached_groups() {
3614 let mut producer = track_producer("test", None);
3615 producer.append_group().unwrap();
3616 producer.append_group().unwrap();
3617
3618 let mut consumer = producer.subscribe(None);
3620 assert_eq!(live_groups(&producer.state.read()), 2);
3621
3622 producer.clone().abort(Error::Cancel).unwrap();
3623
3624 {
3625 let state = producer.state.read();
3626 assert!(state.lookup.is_empty(), "cached groups should be dropped on abort");
3627 assert!(state.arrival.is_empty());
3628 assert!(state.evict.is_empty());
3629 }
3630
3631 let result = consumer.recv_group().now_or_never().expect("should not block");
3633 assert!(matches!(result, Err(Error::Cancel)));
3634 }
3635
3636 #[tokio::test]
3637 async fn drop_unfinished_clears_cached_groups() {
3638 let producer = track_producer("test", None);
3639 let mut writer = producer.clone();
3640 writer.append_group().unwrap();
3641
3642 let mut consumer = producer.subscribe(None);
3644 assert_eq!(live_groups(&producer.state.read()), 1);
3645
3646 drop(writer);
3648 drop(producer);
3649
3650 let result = consumer.recv_group().now_or_never().expect("should not block");
3651 assert!(matches!(result, Err(Error::Dropped)));
3652 }
3653
3654 #[tokio::test]
3655 async fn drop_after_abort_does_not_warn() {
3656 let warns = count_drop_warnings("track::Producer dropped without finish", || {
3659 let producer = track_producer("test", None);
3660 let keep = producer.clone();
3661 let mut writer = producer.clone();
3662 let mut group = writer.append_group().unwrap();
3663 group.finish().unwrap();
3664 let _consumer = producer.subscribe(None);
3665 writer.abort(Error::Cancel).unwrap();
3666 drop(keep);
3667 });
3668 assert_eq!(warns, 0, "abort-then-drop must not emit unfinished-producer WARN");
3669 }
3670
3671 #[tokio::test]
3672 async fn drop_unfinished_warns() {
3673 let warns = count_drop_warnings("track::Producer dropped without finish", || {
3674 let producer = track_producer("test", None);
3675 let mut writer = producer.clone();
3676 writer.append_group().unwrap();
3677 let _consumer = producer.subscribe(None);
3678 drop(writer);
3679 drop(producer);
3680 });
3681 assert!(warns >= 1, "unfinished drop must emit unfinished-producer WARN");
3682 }
3683
3684 #[tokio::test]
3685 async fn drop_finished_keeps_cached_groups() {
3686 let mut producer = track_producer("test", None);
3687 producer.append_group().unwrap();
3688 producer.finish().unwrap();
3689
3690 let mut consumer = producer.subscribe(None);
3691 drop(producer);
3692
3693 assert_eq!(consumer.assert_group().sequence, 0);
3695 let done = consumer.recv_group().now_or_never().expect("should not block").unwrap();
3696 assert!(done.is_none(), "consumer should drain then see clean finish");
3697 }
3698
3699 #[test]
3700 fn append_finish_cannot_be_rewritten() {
3701 let mut producer = track_producer("test", None);
3702
3703 assert!(producer.finish().is_ok());
3705 assert!(producer.finish().is_err());
3706 assert!(producer.append_group().is_err());
3707 }
3708
3709 #[test]
3710 fn finish_after_groups() {
3711 let mut producer = track_producer("test", None);
3712
3713 producer.append_group().unwrap();
3714 assert!(producer.finish().is_ok());
3715 assert!(producer.finish().is_err());
3716 assert!(producer.append_group().is_err());
3717 }
3718
3719 #[test]
3720 fn finish_at_rejects_a_boundary_at_or_below_the_live_edge() {
3721 let mut producer = track_producer("test", None);
3722 producer.create_group(group::Info { sequence: 5 }).unwrap();
3723
3724 assert!(producer.finish_at(4).is_err());
3727 assert!(producer.finish_at(5).is_err());
3728 assert!(producer.finish_at(6).is_ok());
3729
3730 {
3731 let state = producer.state.read();
3732 assert_eq!(state.final_sequence, Some(6));
3733 }
3734
3735 assert!(producer.finish_at(6).is_err());
3737 assert!(producer.create_group(group::Info { sequence: 4 }).is_ok());
3738 assert!(producer.create_group(group::Info { sequence: 6 }).is_err());
3739 }
3740
3741 #[test]
3742 fn final_sequence_reports_the_declared_boundary() {
3743 let mut producer = track_producer("test", None);
3744 assert_eq!(producer.final_sequence(), None);
3745
3746 producer.create_group(group::Info { sequence: 5 }).unwrap();
3747 assert_eq!(producer.final_sequence(), None, "a group does not declare a boundary");
3748
3749 producer.finish_at(9).unwrap();
3750 assert_eq!(producer.final_sequence(), Some(9));
3751
3752 assert!(producer.finish().is_err());
3754 }
3755
3756 #[test]
3757 fn final_sequence_reports_the_live_edge_after_finish() {
3758 let mut producer = track_producer("test", None);
3759 producer.create_group(group::Info { sequence: 5 }).unwrap();
3760 producer.finish().unwrap();
3761 assert_eq!(producer.final_sequence(), Some(6));
3762 }
3763
3764 #[tokio::test]
3765 async fn finish_at_declares_a_future_boundary() {
3766 let mut producer = track_producer("test", None);
3767 producer.create_group(group::Info { sequence: 5 }).unwrap();
3768
3769 producer.finish_at(7).unwrap();
3771
3772 let mut consumer = producer.subscribe(None);
3773 assert_eq!(consumer.assert_group().sequence, 5);
3774
3775 let boundary = consumer
3778 .finished()
3779 .now_or_never()
3780 .expect("boundary is known immediately")
3781 .expect("would have errored");
3782 assert_eq!(boundary, 7);
3783 assert!(
3784 consumer.recv_group().now_or_never().is_none(),
3785 "should wait for the outstanding group"
3786 );
3787
3788 producer.create_group(group::Info { sequence: 6 }).unwrap();
3790 assert_eq!(consumer.assert_group().sequence, 6);
3791 let done = consumer
3792 .recv_group()
3793 .now_or_never()
3794 .expect("should not block")
3795 .expect("would have errored");
3796 assert!(done.is_none(), "track completes once the boundary is reached");
3797 }
3798
3799 #[tokio::test]
3800 async fn recv_group_finishes_without_waiting_for_gaps() {
3801 let mut producer = track_producer("test", None);
3802 producer.create_group(group::Info { sequence: 1 }).unwrap();
3803 producer.finish().unwrap();
3804
3805 let mut consumer = producer.subscribe(None);
3806 assert_eq!(consumer.assert_group().sequence, 1);
3807
3808 let done = consumer
3809 .recv_group()
3810 .now_or_never()
3811 .expect("should not block")
3812 .expect("would have errored");
3813 assert!(done.is_none(), "track should finish without waiting for gaps");
3814 }
3815
3816 #[tokio::test]
3817 async fn next_group_skips_late_arrivals() {
3818 let mut producer = track_producer("test", None);
3819 let mut consumer = producer.subscribe(None);
3820
3821 producer.create_group(group::Info { sequence: 5 }).unwrap();
3823 let group = consumer
3824 .next_group()
3825 .now_or_never()
3826 .expect("should not block")
3827 .expect("would have errored")
3828 .expect("track should not be closed");
3829 assert_eq!(group.sequence, 5);
3830
3831 producer.create_group(group::Info { sequence: 3 }).unwrap();
3833 producer.create_group(group::Info { sequence: 4 }).unwrap();
3835 producer.create_group(group::Info { sequence: 7 }).unwrap();
3837
3838 let group = consumer
3839 .next_group()
3840 .now_or_never()
3841 .expect("should not block")
3842 .expect("would have errored")
3843 .expect("track should not be closed");
3844 assert_eq!(group.sequence, 7);
3845
3846 assert!(
3848 consumer.next_group().now_or_never().is_none(),
3849 "should block waiting for a higher sequence"
3850 );
3851 }
3852
3853 #[tokio::test]
3854 async fn next_group_returns_arrivals_in_order() {
3855 let mut producer = track_producer("test", None);
3856 let mut consumer = producer.subscribe(None);
3857
3858 producer.create_group(group::Info { sequence: 3 }).unwrap();
3860 producer.create_group(group::Info { sequence: 5 }).unwrap();
3861
3862 let group = consumer
3863 .next_group()
3864 .now_or_never()
3865 .expect("should not block")
3866 .expect("would have errored")
3867 .expect("track should not be closed");
3868 assert_eq!(group.sequence, 3);
3869
3870 let group = consumer
3871 .next_group()
3872 .now_or_never()
3873 .expect("should not block")
3874 .expect("would have errored")
3875 .expect("track should not be closed");
3876 assert_eq!(group.sequence, 5);
3877 }
3878
3879 #[tokio::test]
3880 async fn next_group_and_recv_group_use_independent_cursors() {
3881 let mut producer = track_producer("test", None);
3882 let mut consumer = producer.subscribe(None);
3883
3884 producer.create_group(group::Info { sequence: 5 }).unwrap();
3886 producer.create_group(group::Info { sequence: 3 }).unwrap();
3887
3888 let group = consumer
3891 .next_group()
3892 .now_or_never()
3893 .expect("should not block")
3894 .expect("would have errored")
3895 .expect("track should not be closed");
3896 assert_eq!(group.sequence, 3);
3897
3898 assert_eq!(consumer.assert_group().sequence, 5);
3901 }
3902
3903 #[tokio::test]
3904 async fn end_at_caps_next_group() {
3905 let mut producer = track_producer("test", None);
3906 let mut consumer = producer.subscribe(None);
3907
3908 for s in 0..6 {
3909 producer.create_group(group::Info { sequence: s }).unwrap();
3910 }
3911
3912 consumer.end_at(2);
3913
3914 assert_eq!(
3916 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3917 0
3918 );
3919 assert_eq!(
3920 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3921 1
3922 );
3923 assert_eq!(
3924 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3925 2
3926 );
3927
3928 assert!(
3930 consumer.next_group().now_or_never().is_none(),
3931 "capped consumer must block instead of returning out-of-range groups"
3932 );
3933 }
3934
3935 #[tokio::test]
3936 async fn end_at_release_drains_cached_groups() {
3937 let mut producer = track_producer("test", None);
3938 let mut consumer = producer.subscribe(None);
3939
3940 for s in 0..6 {
3941 producer.create_group(group::Info { sequence: s }).unwrap();
3942 }
3943
3944 consumer.end_at(1);
3945 assert_eq!(
3946 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3947 0
3948 );
3949 assert_eq!(
3950 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3951 1
3952 );
3953 assert!(consumer.next_group().now_or_never().is_none(), "capped at 1");
3954
3955 consumer.end_at(4);
3957 assert_eq!(
3958 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3959 2
3960 );
3961 assert_eq!(
3962 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3963 3
3964 );
3965 assert_eq!(
3966 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3967 4
3968 );
3969 assert!(consumer.next_group().now_or_never().is_none(), "capped at 4");
3970
3971 consumer.end_at(None);
3973 assert_eq!(
3974 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3975 5
3976 );
3977 assert!(consumer.next_group().now_or_never().is_none(), "no more groups");
3978 }
3979
3980 #[tokio::test]
3981 async fn end_at_lower_than_cursor_parks_consumer() {
3982 let mut producer = track_producer("test", None);
3983 let mut consumer = producer.subscribe(None);
3984
3985 for s in 0..3 {
3986 producer.create_group(group::Info { sequence: s }).unwrap();
3987 }
3988
3989 assert_eq!(
3991 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3992 0
3993 );
3994 assert_eq!(
3995 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
3996 1
3997 );
3998 assert_eq!(
3999 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4000 2
4001 );
4002
4003 consumer.end_at(1);
4005 producer.create_group(group::Info { sequence: 3 }).unwrap();
4006 producer.create_group(group::Info { sequence: 4 }).unwrap();
4007 assert!(
4008 consumer.next_group().now_or_never().is_none(),
4009 "cap is below cursor; nothing returnable until cap rises"
4010 );
4011
4012 consumer.end_at(None);
4014 assert_eq!(
4015 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4016 3
4017 );
4018 assert_eq!(
4019 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4020 4
4021 );
4022 }
4023
4024 #[tokio::test]
4025 async fn end_at_toggling_around_late_arrivals() {
4026 let mut producer = track_producer("test", None);
4027 let mut consumer = producer.subscribe(None);
4028
4029 consumer.end_at(5);
4030
4031 producer.create_group(group::Info { sequence: 2 }).unwrap();
4033 producer.create_group(group::Info { sequence: 5 }).unwrap();
4034 producer.create_group(group::Info { sequence: 3 }).unwrap();
4035 producer.create_group(group::Info { sequence: 8 }).unwrap();
4037 producer.create_group(group::Info { sequence: 4 }).unwrap();
4038
4039 assert_eq!(
4041 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4042 2
4043 );
4044 assert_eq!(
4045 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4046 3
4047 );
4048 assert_eq!(
4049 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4050 4
4051 );
4052 assert_eq!(
4053 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4054 5
4055 );
4056 assert!(consumer.next_group().now_or_never().is_none());
4058
4059 consumer.end_at(10);
4061 assert_eq!(
4062 consumer.next_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4063 8
4064 );
4065 }
4066
4067 #[tokio::test]
4071 async fn end_at_parks_recv_group() {
4072 let mut producer = track_producer("test", None);
4073 let mut consumer = producer.subscribe(None);
4074
4075 for s in 0..3 {
4076 producer.create_group(group::Info { sequence: s }).unwrap();
4077 }
4078
4079 consumer.end_at(1);
4080 assert_eq!(
4081 consumer.recv_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4082 0
4083 );
4084 assert_eq!(
4085 consumer.recv_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4086 1
4087 );
4088 assert!(consumer.recv_group().now_or_never().is_none(), "capped at 1");
4089
4090 producer.finish().unwrap();
4092 assert!(
4093 consumer.recv_group().now_or_never().is_none(),
4094 "still parked after finish"
4095 );
4096
4097 consumer.end_at(None);
4098 assert_eq!(
4099 consumer.recv_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4100 2
4101 );
4102 assert!(
4103 matches!(consumer.recv_group().now_or_never(), Some(Ok(None))),
4104 "finished once the parked group drains"
4105 );
4106 }
4107
4108 #[tokio::test]
4111 async fn recv_group_serves_arrivals_behind_the_cap() {
4112 let mut producer = track_producer("test", None);
4113 let mut consumer = producer.subscribe(None);
4114
4115 consumer.end_at(1);
4116
4117 producer.create_group(group::Info { sequence: 2 }).unwrap();
4119 producer.create_group(group::Info { sequence: 0 }).unwrap();
4120 producer.create_group(group::Info { sequence: 1 }).unwrap();
4121
4122 assert_eq!(
4123 consumer.recv_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4124 0
4125 );
4126 assert_eq!(
4127 consumer.recv_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4128 1
4129 );
4130 assert!(consumer.recv_group().now_or_never().is_none(), "capped at 1");
4131
4132 consumer.end_at(2);
4133 assert_eq!(
4134 consumer.recv_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4135 2
4136 );
4137 }
4138
4139 #[tokio::test]
4142 async fn start_at_drops_parked_recv_groups() {
4143 let mut producer = track_producer("test", None);
4144 let mut consumer = producer.subscribe(None);
4145
4146 consumer.end_at(0);
4147 producer.create_group(group::Info { sequence: 1 }).unwrap();
4148 assert!(
4149 consumer.recv_group().now_or_never().is_none(),
4150 "group 1 parked at the cap"
4151 );
4152
4153 consumer.start_at(2);
4154 consumer.end_at(None);
4155 producer.create_group(group::Info { sequence: 2 }).unwrap();
4156 assert_eq!(
4157 consumer.recv_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4158 2,
4159 "the overtaken parked group is dropped, not re-offered"
4160 );
4161 }
4162
4163 #[tokio::test]
4167 async fn evicted_parked_recv_groups_are_dropped() {
4168 let mut producer = track_producer("test", None);
4169 let mut consumer = producer.subscribe(None);
4170
4171 producer.create_group(group::Info { sequence: 0 }).unwrap();
4172 assert_eq!(
4173 consumer.recv_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4174 0
4175 );
4176
4177 consumer.end_at(0);
4178 let straggler = producer.create_group(group::Info { sequence: 1 }).unwrap();
4179 assert!(
4180 consumer.recv_group().now_or_never().is_none(),
4181 "group 1 parked at the cap"
4182 );
4183
4184 straggler.abort(Error::Old).unwrap();
4186 producer.finish().unwrap();
4187
4188 consumer.end_at(None);
4189 assert!(
4190 matches!(consumer.recv_group().now_or_never(), Some(Ok(None))),
4191 "a dead parked group must not be delivered or hold the stream open"
4192 );
4193 }
4194
4195 #[tokio::test]
4199 async fn evicted_parked_group_wakes_the_clean_end() {
4200 use std::sync::atomic::{AtomicUsize, Ordering};
4201 use std::task::{Context, Wake};
4202
4203 struct CountWaker(AtomicUsize);
4206 impl Wake for CountWaker {
4207 fn wake(self: std::sync::Arc<Self>) {
4208 self.0.fetch_add(1, Ordering::SeqCst);
4209 }
4210 }
4211
4212 let mut producer = track_producer("test", None);
4213 let mut consumer = producer.subscribe(None);
4214
4215 producer.create_group(group::Info { sequence: 0 }).unwrap();
4216 assert_eq!(
4217 consumer.recv_group().now_or_never().unwrap().unwrap().unwrap().sequence,
4218 0
4219 );
4220
4221 consumer.end_at(0);
4222 let straggler = producer.create_group(group::Info { sequence: 1 }).unwrap();
4223 assert!(consumer.recv_group().now_or_never().is_none(), "parked at the cap");
4224 producer.finish().unwrap();
4225
4226 let counter = std::sync::Arc::new(CountWaker(AtomicUsize::new(0)));
4227 let waker = std::task::Waker::from(counter.clone());
4228 let mut cx = Context::from_waker(&waker);
4229 let mut fut = std::pin::pin!(consumer.recv_group());
4230 assert!(
4231 fut.as_mut().poll(&mut cx).is_pending(),
4232 "the parked group holds it open"
4233 );
4234
4235 straggler.abort(Error::Old).unwrap();
4236 assert!(counter.0.load(Ordering::SeqCst) > 0, "the eviction wakeup was lost");
4237 assert!(matches!(fut.as_mut().poll(&mut cx), Poll::Ready(Ok(None))));
4238 }
4239
4240 #[tokio::test]
4241 async fn read_frame_returns_single_frame_per_group() {
4242 let mut producer = track_producer("test", None);
4243 let mut consumer = producer.subscribe(None);
4244
4245 producer.write_frame(Timestamp::ZERO, b"hello".as_slice()).unwrap();
4246 producer.write_frame(Timestamp::ZERO, b"world".as_slice()).unwrap();
4247
4248 let frame = consumer
4249 .read_frame()
4250 .now_or_never()
4251 .expect("should not block")
4252 .expect("would have errored")
4253 .expect("track should not be closed");
4254 assert_eq!(&frame.payload[..], b"hello");
4255
4256 let frame = consumer
4257 .read_frame()
4258 .now_or_never()
4259 .expect("should not block")
4260 .expect("would have errored")
4261 .expect("track should not be closed");
4262 assert_eq!(&frame.payload[..], b"world");
4263 }
4264
4265 #[test]
4266 fn write_frame_rejects_an_oversized_frame_before_appending_its_group() {
4267 let mut producer = track_producer("test", None);
4268 let frame = bytes::Bytes::from(vec![0; group::MAX_GROUP_CACHE as usize + 1]);
4269
4270 assert!(matches!(
4271 producer.write_frame(Timestamp::ZERO, frame),
4272 Err(Error::FrameTooLarge)
4273 ));
4274 assert_eq!(producer.latest(), None, "the rejected frame did not publish a group");
4275 }
4276
4277 #[tokio::test]
4278 async fn read_frame_preserves_timestamp() {
4279 let mut producer = track_producer("test", None);
4280 let mut consumer = producer.subscribe(None);
4281
4282 producer
4283 .write_frame(Timestamp::from_micros(20_000).unwrap(), b"hello".as_slice())
4284 .unwrap();
4285
4286 let frame = consumer
4287 .read_frame()
4288 .now_or_never()
4289 .expect("should not block")
4290 .expect("would have errored")
4291 .expect("track should not be closed");
4292 assert_eq!(frame.timestamp.as_micros(), 20_000);
4293 assert_eq!(&frame.payload[..], b"hello");
4294 }
4295
4296 #[tokio::test]
4297 async fn read_frame_skips_stalled_group_for_newer_ready_frame() {
4298 let mut producer = track_producer("test", None);
4299 let mut consumer = producer.subscribe(None);
4300
4301 let _stalled = producer.create_group(group::Info { sequence: 3 }).unwrap();
4303 let mut g5 = producer.create_group(group::Info { sequence: 5 }).unwrap();
4305 g5.write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"later"))
4306 .unwrap();
4307 g5.finish().unwrap();
4308
4309 let frame = consumer
4311 .read_frame()
4312 .now_or_never()
4313 .expect("should not block on stalled earlier group")
4314 .expect("would have errored")
4315 .expect("track should not be closed");
4316 assert_eq!(&frame.payload[..], b"later");
4317 }
4318
4319 #[tokio::test]
4320 async fn read_frame_discards_rest_of_multi_frame_group() {
4321 let mut producer = track_producer("test", None);
4322 let mut consumer = producer.subscribe(None);
4323
4324 let mut g0 = producer.create_group(group::Info { sequence: 0 }).unwrap();
4326 g0.write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"one"))
4327 .unwrap();
4328 g0.write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"two"))
4329 .unwrap();
4330 g0.finish().unwrap();
4331
4332 producer.write_frame(Timestamp::ZERO, b"next".as_slice()).unwrap();
4334
4335 let frame = consumer
4336 .read_frame()
4337 .now_or_never()
4338 .expect("should not block")
4339 .expect("would have errored")
4340 .expect("track should not be closed");
4341 assert_eq!(&frame.payload[..], b"one");
4342
4343 let frame = consumer
4345 .read_frame()
4346 .now_or_never()
4347 .expect("should not block")
4348 .expect("would have errored")
4349 .expect("track should not be closed");
4350 assert_eq!(&frame.payload[..], b"next");
4351 }
4352
4353 #[tokio::test]
4354 async fn read_frame_waits_for_pending_group_after_finish() {
4355 let mut producer = track_producer("test", None);
4358 let mut consumer = producer.subscribe(None);
4359
4360 let mut g0 = producer.create_group(group::Info { sequence: 0 }).unwrap();
4361 producer.finish().unwrap();
4362
4363 assert!(
4365 consumer.read_frame().now_or_never().is_none(),
4366 "read_frame must block on a pending group even after finish()"
4367 );
4368
4369 g0.write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"late"))
4371 .unwrap();
4372 let frame = consumer
4373 .read_frame()
4374 .now_or_never()
4375 .expect("should not block once a frame is written")
4376 .expect("would have errored")
4377 .expect("track should not be closed");
4378 assert_eq!(&frame.payload[..], b"late");
4379 }
4380
4381 #[tokio::test]
4382 async fn read_frame_respects_start_at() {
4383 let mut producer = track_producer("test", None);
4386 let mut consumer = producer.subscribe(None);
4387 consumer.start_at(5);
4388
4389 let mut g3 = producer.create_group(group::Info { sequence: 3 }).unwrap();
4391 g3.write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"skip-me"))
4392 .unwrap();
4393 g3.finish().unwrap();
4394
4395 let mut g5 = producer.create_group(group::Info { sequence: 5 }).unwrap();
4396 g5.write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"keep"))
4397 .unwrap();
4398 g5.finish().unwrap();
4399
4400 let frame = consumer
4401 .read_frame()
4402 .now_or_never()
4403 .expect("should not block")
4404 .expect("would have errored")
4405 .expect("track should not be closed");
4406 assert_eq!(&frame.payload[..], b"keep");
4407 }
4408
4409 #[tokio::test]
4410 async fn read_frame_returns_none_when_finished() {
4411 let mut producer = track_producer("test", None);
4412 let mut consumer = producer.subscribe(None);
4413
4414 producer.write_frame(Timestamp::ZERO, b"only".as_slice()).unwrap();
4415 producer.finish().unwrap();
4416
4417 let frame = consumer
4418 .read_frame()
4419 .now_or_never()
4420 .expect("should not block")
4421 .expect("would have errored")
4422 .expect("track should not be closed");
4423 assert_eq!(&frame.payload[..], b"only");
4424
4425 let done = consumer
4426 .read_frame()
4427 .now_or_never()
4428 .expect("should not block")
4429 .expect("would have errored");
4430 assert!(done.is_none());
4431 }
4432
4433 #[test]
4434 fn append_group_returns_bounds_exceeded_on_sequence_overflow() {
4435 let mut producer = track_producer("test", None);
4436 {
4437 let mut state = producer.state.write().ok().unwrap();
4438 state.max_sequence = Some(u64::MAX);
4439 }
4440
4441 assert!(matches!(producer.append_group(), Err(Error::BoundsExceeded(_))));
4442 }
4443
4444 #[tokio::test]
4445 async fn fetch_cache_hit() {
4446 let mut producer = track_producer("test", None);
4447
4448 let mut group = producer.append_group().unwrap(); group
4451 .write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"hello"))
4452 .unwrap();
4453 group.finish().unwrap();
4454
4455 let dynamic = producer.dynamic();
4458 let consumer = producer.consume();
4459 assert!(consumer.peek_group(0).is_some());
4460 let mut g = consumer.fetch_group(0, None).await.unwrap();
4461 assert_eq!(g.sequence, 0);
4462 assert_eq!(&g.read_frame().await.unwrap().unwrap().payload[..], b"hello");
4463
4464 assert!(dynamic.poll_requested_group(&kio::Waiter::noop()).is_pending());
4466 }
4467
4468 #[tokio::test]
4469 async fn fetch_miss_signals_dynamic() {
4470 let producer = track_producer("test", None);
4471 let dynamic = producer.dynamic();
4472 let consumer = producer.consume();
4473
4474 assert!(consumer.peek_group(5).is_none());
4478 let pending = consumer.fetch_group(5, group::Fetch::default().with_priority(7));
4479 assert!(kio::Pollable::poll(&*pending, &kio::Waiter::noop()).is_pending());
4480
4481 let req = dynamic
4482 .requested_group()
4483 .now_or_never()
4484 .expect("should not block")
4485 .unwrap();
4486 assert_eq!(req.sequence(), 5);
4487 assert_eq!(req.priority(), 7);
4488
4489 let mut group = req.accept(None).unwrap();
4491 group
4492 .write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"hi"))
4493 .unwrap();
4494 group.finish().unwrap();
4495
4496 let mut g = pending.await.unwrap();
4497 assert_eq!(g.sequence, 5);
4498 assert_eq!(&g.read_frame().await.unwrap().unwrap().payload[..], b"hi");
4499 }
4500
4501 #[tokio::test]
4502 async fn fetch_miss_rejects() {
4503 let producer = track_producer("test", None);
4504 let dynamic = producer.dynamic();
4505 let consumer = producer.consume();
4506
4507 let pending = consumer.fetch_group(5, None);
4508 let req = dynamic
4509 .requested_group()
4510 .now_or_never()
4511 .expect("should not block")
4512 .unwrap();
4513
4514 req.reject(Error::Cancel);
4515 assert!(matches!(pending.await, Err(Error::Cancel)));
4516 let fetch = producer.state.read().fetch.clone();
4517 assert!(fetch.read().is_empty());
4518 }
4519
4520 #[tokio::test]
4521 async fn fetch_miss_drop_rejects() {
4522 let producer = track_producer("test", None);
4523 let dynamic = producer.dynamic();
4524 let consumer = producer.consume();
4525
4526 let pending = consumer.fetch_group(5, None);
4527 let req = dynamic
4528 .requested_group()
4529 .now_or_never()
4530 .expect("should not block")
4531 .unwrap();
4532
4533 drop(req);
4534 assert!(matches!(pending.await, Err(Error::Dropped)));
4535 }
4536
4537 #[tokio::test]
4538 async fn fetch_reject_does_not_poison_retry() {
4539 let producer = track_producer("test", None);
4540 let dynamic = producer.dynamic();
4541 let consumer = producer.consume();
4542
4543 let pending = consumer.fetch_group(5, None);
4544 let req = dynamic
4545 .requested_group()
4546 .now_or_never()
4547 .expect("should not block")
4548 .unwrap();
4549 req.reject(Error::Cancel);
4550 assert!(matches!(pending.await, Err(Error::Cancel)));
4551
4552 let retry = consumer.fetch_group(5, None);
4553 let req = dynamic
4554 .requested_group()
4555 .now_or_never()
4556 .expect("should not block")
4557 .unwrap();
4558 let mut group = req.accept(None).unwrap();
4559 group
4560 .write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"retry"))
4561 .unwrap();
4562 group.finish().unwrap();
4563
4564 let mut group = retry.await.unwrap();
4565 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"retry");
4566 }
4567
4568 #[tokio::test]
4569 async fn fetch_coalesces_concurrent() {
4570 let producer = track_producer("test", None);
4571 let dynamic = producer.dynamic();
4572 let consumer = producer.consume();
4573
4574 let first = consumer.fetch_group(5, group::Fetch::default().with_priority(1));
4577 let second = consumer.fetch_group(5, group::Fetch::default().with_priority(7));
4578 assert!(kio::Pollable::poll(&*first, &kio::Waiter::noop()).is_pending());
4579
4580 let req = dynamic
4581 .requested_group()
4582 .now_or_never()
4583 .expect("should not block")
4584 .unwrap();
4585 assert_eq!(req.sequence(), 5);
4586 assert_eq!(req.priority(), 7);
4587 assert!(
4588 dynamic.poll_requested_group(&kio::Waiter::noop()).is_pending(),
4589 "the second fetch queued a duplicate request"
4590 );
4591
4592 let third = consumer.fetch_group(5, None);
4594
4595 let mut group = req.accept(None).unwrap();
4597 group
4598 .write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"hi"))
4599 .unwrap();
4600 group.finish().unwrap();
4601
4602 assert_eq!(first.await.unwrap().sequence, 5);
4603 assert_eq!(second.await.unwrap().sequence, 5);
4604 assert_eq!(third.await.unwrap().sequence, 5);
4605 }
4606
4607 #[tokio::test]
4608 async fn fetch_coalesced_reject_fails_all() {
4609 let producer = track_producer("test", None);
4610 let dynamic = producer.dynamic();
4611 let consumer = producer.consume();
4612
4613 let first = consumer.fetch_group(5, None);
4614 let second = consumer.fetch_group(5, None);
4615 let req = dynamic
4616 .requested_group()
4617 .now_or_never()
4618 .expect("should not block")
4619 .unwrap();
4620 req.reject(Error::Cancel);
4621
4622 assert!(matches!(first.await, Err(Error::Cancel)));
4623 assert!(matches!(second.await, Err(Error::Cancel)));
4624
4625 let retry = consumer.fetch_group(5, None);
4627 assert!(kio::Pollable::poll(&*retry, &kio::Waiter::noop()).is_pending());
4628 let req = dynamic
4629 .requested_group()
4630 .now_or_never()
4631 .expect("should not block")
4632 .unwrap();
4633 assert_eq!(req.sequence(), 5);
4634 }
4635
4636 #[tokio::test]
4637 async fn fetch_queued_fails_when_handlers_leave() {
4638 let producer = track_producer("test", None);
4639 let dynamic = producer.dynamic();
4640 let consumer = producer.consume();
4641
4642 let pending = consumer.fetch_group(5, None);
4644 assert!(kio::Pollable::poll(&*pending, &kio::Waiter::noop()).is_pending());
4645 drop(dynamic);
4646 assert!(matches!(pending.await, Err(Error::NotFound)));
4647
4648 let fetch = producer.state.read().fetch.clone();
4650 assert!(fetch.read().is_empty());
4651 }
4652
4653 #[tokio::test]
4654 async fn fetch_miss_no_dynamic_not_found() {
4655 let mut producer = track_producer("test", None);
4658 producer.append_group().unwrap(); let consumer = producer.consume();
4660 assert!(matches!(consumer.fetch_group(5, None).await, Err(Error::NotFound)));
4661 }
4662
4663 #[tokio::test]
4664 async fn fetch_past_final_not_found() {
4665 let mut producer = track_producer("test", None);
4666 producer.append_group().unwrap(); producer.finish().unwrap(); let dynamic = producer.dynamic();
4672 let consumer = producer.consume();
4673 assert!(matches!(consumer.fetch_group(5, None).await, Err(Error::NotFound)));
4674
4675 assert!(dynamic.poll_requested_group(&kio::Waiter::noop()).is_pending());
4677 }
4678
4679 fn pooled_producer(capacity: u64) -> (Producer, cache::Pool) {
4681 let pool = cache::Pool::new(capacity);
4682 let broadcast = broadcast::Info {
4683 origin: crate::origin::Info::default().with_pool(pool.clone()),
4684 ..Default::default()
4685 };
4686 let producer = Producer::new(Arc::new(broadcast), "test", None);
4687 (producer, pool)
4688 }
4689
4690 fn finished_group(producer: &mut Producer, size: usize) -> u64 {
4691 let mut group = producer.append_group().unwrap();
4692 group
4693 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; size]))
4694 .unwrap();
4695 group.finish().unwrap();
4696 group.sequence
4697 }
4698
4699 #[tokio::test]
4702 async fn debt_evicts_oldest_group() {
4703 tokio::time::pause();
4704
4705 let (mut producer, pool) = pooled_producer(10_000);
4707
4708 finished_group(&mut producer, 10_000); finished_group(&mut producer, 10_000); finished_group(&mut producer, 10_000); let consumer = producer.consume();
4713 assert!(consumer.peek_group(0).is_none(), "oldest group is evicted");
4714 assert!(consumer.peek_group(2).is_some(), "latest group survives");
4715 assert!(pool.used() <= 21_000, "usage hovers near capacity: {}", pool.used());
4718
4719 let mut subscriber = producer.subscribe(None);
4721 assert!(subscriber.assert_group().sequence > 0, "evicted group is not delivered");
4722 }
4723
4724 #[tokio::test]
4726 async fn latest_group_never_evicted() {
4727 tokio::time::pause();
4728
4729 let (mut producer, pool) = pooled_producer(100);
4731 finished_group(&mut producer, 1000); assert!(pool.used() > 100, "the latest may exceed the budget");
4733
4734 finished_group(&mut producer, 1000); finished_group(&mut producer, 1000); let consumer = producer.consume();
4739 assert!(consumer.peek_group(0).is_none());
4740 let mut group = consumer.peek_group(2).expect("latest survives");
4741 assert_eq!(group.read_frame().await.unwrap().unwrap().payload.len(), 1000);
4742 }
4743
4744 #[tokio::test]
4748 async fn fetch_refresh_survives_eviction() {
4749 tokio::time::pause();
4750
4751 let (mut producer, _pool) = pooled_producer(10_000);
4752 let consumer = producer.consume();
4753
4754 finished_group(&mut producer, 3_000); tokio::time::advance(Duration::from_secs(1)).await;
4756 finished_group(&mut producer, 3_000); tokio::time::advance(Duration::from_secs(1)).await;
4758 finished_group(&mut producer, 3_000); tokio::time::advance(Duration::from_millis(500)).await;
4760
4761 let mut fetched = consumer.fetch_group(0, None).await.unwrap();
4763 assert_eq!(fetched.read_frame().await.unwrap().unwrap().payload.len(), 3_000);
4764 tokio::time::advance(Duration::from_millis(500)).await;
4765
4766 finished_group(&mut producer, 3_000); tokio::time::advance(Duration::from_secs(1)).await;
4770 finished_group(&mut producer, 3_000); assert!(consumer.peek_group(0).is_some(), "refreshed group survives");
4773 assert!(consumer.peek_group(1).is_none(), "unread group is evicted instead");
4774 }
4775
4776 #[tokio::test]
4779 async fn eviction_aborts_readers() {
4780 tokio::time::pause();
4781
4782 let (mut producer, _pool) = pooled_producer(10_000);
4783 let mut subscriber = producer.subscribe(None);
4784
4785 finished_group(&mut producer, 10_000); let mut group0 = subscriber.assert_group();
4787
4788 finished_group(&mut producer, 10_000); finished_group(&mut producer, 10_000); let read = group0.read_frame().await;
4792 assert!(matches!(read, Err(Error::Evicted)), "expected Evicted, got {read:?}");
4793 }
4794
4795 #[tokio::test]
4799 async fn small_writes_carry_debt() {
4800 tokio::time::pause();
4801
4802 let (mut producer, pool) = pooled_producer(22_000);
4803 let consumer = producer.consume();
4804
4805 finished_group(&mut producer, 20_000); for _ in 0..3 {
4810 finished_group(&mut producer, 1_000);
4811 }
4812 assert!(consumer.peek_group(0).is_some(), "debt smaller than the victim carries");
4813
4814 for _ in 0..20 {
4816 finished_group(&mut producer, 1_000);
4817 }
4818 assert!(
4819 consumer.peek_group(0).is_none(),
4820 "accumulated debt evicts the large group"
4821 );
4822 assert!(pool.used() <= 24_000, "usage hovers near capacity: {}", pool.used());
4825 }
4826
4827 #[tokio::test]
4831 async fn payment_capped_per_write() {
4832 tokio::time::pause();
4833
4834 let (mut producer, pool) = pooled_producer(1 << 40);
4835 for _ in 0..10 {
4836 finished_group(&mut producer, 1_000);
4837 }
4838
4839 pool.resize(100);
4841 let before = pool.used();
4842
4843 finished_group(&mut producer, 1_000);
4845
4846 let consumer = producer.consume();
4847 assert!(consumer.peek_group(0).is_none(), "the oldest groups are evicted");
4848 assert!(consumer.peek_group(1).is_none());
4849 assert!(consumer.peek_group(2).is_some(), "the backlog drains gradually");
4850 assert!(pool.used() > before - 4_000, "one write must not dump the backlog");
4851 }
4852
4853 #[tokio::test]
4857 async fn accept_preserves_write_accounting() {
4858 tokio::time::pause();
4859
4860 let pool = cache::Pool::new(12_000);
4861 let broadcast = broadcast::Info {
4862 origin: crate::origin::Info::default().with_pool(pool.clone()),
4863 ..Default::default()
4864 };
4865 let request = Request::new(Arc::new(broadcast), "test");
4866 let dynamic = request.dynamic();
4867 let consumer = request.consume();
4868
4869 let pending = consumer.fetch_group(0, None);
4871 let req = dynamic
4872 .requested_group()
4873 .now_or_never()
4874 .expect("should not block")
4875 .unwrap();
4876 let mut backfill = req.accept(None).unwrap();
4877 pending.await.unwrap();
4878 backfill
4879 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; 30_000]))
4880 .unwrap();
4881
4882 let mut producer = request.accept(None);
4885 producer.append_group().unwrap().finish().unwrap();
4886 producer.append_group().unwrap().finish().unwrap();
4887
4888 assert!(
4889 producer.consume().peek_group(0).is_none(),
4890 "pre-accept backfill growth is reclaimed after accept"
4891 );
4892 assert!(pool.used() <= 13_000, "usage converges: {}", pool.used());
4893 }
4894
4895 #[tokio::test]
4898 async fn recreated_sequence_bounds_eviction_hints() {
4899 let (mut producer, _pool) = pooled_producer(1 << 40);
4900 producer.create_group(5u64.into()).unwrap().finish().unwrap();
4901
4902 for _ in 0..200 {
4903 let group = producer.create_group(1u64.into()).unwrap();
4904 group.abort(Error::Cancel).unwrap();
4905 }
4906
4907 let state = producer.state.read();
4908 assert!(
4909 state.evict.len() <= 2 * state.lookup.len() + EVICT_SLACK,
4910 "stale hints are compacted: {} entries for {} slots",
4911 state.evict.len(),
4912 state.lookup.len()
4913 );
4914 }
4915
4916 #[tokio::test]
4919 async fn same_tick_write_outranks_inserted() {
4920 tokio::time::pause();
4921
4922 let (mut producer, _pool) = pooled_producer(10_000);
4924
4925 producer.append_group().unwrap().finish().unwrap(); finished_group(&mut producer, 3_000); finished_group(&mut producer, 3_000); finished_group(&mut producer, 3_000); finished_group(&mut producer, 3_000); let consumer = producer.consume();
4932 assert!(consumer.peek_group(0).is_none(), "insert-only content pays first");
4933 assert!(consumer.peek_group(1).is_some(), "same-tick written content survives");
4934 }
4935
4936 #[tokio::test]
4939 async fn frame_only_writer_pays() {
4940 tokio::time::pause();
4941
4942 let (mut producer, pool) = pooled_producer(2_000);
4943 let mut demoted = producer.append_group().unwrap(); producer.append_group().unwrap().finish().unwrap(); demoted
4949 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; 300_000]))
4950 .unwrap();
4951
4952 assert!(
4953 pool.used() <= 5_000,
4954 "the frame write settled the debt: {}",
4955 pool.used()
4956 );
4957 assert!(matches!(demoted.finish(), Err(Error::Evicted)));
4958 }
4959
4960 #[tokio::test]
4963 async fn each_track_owns_its_account() {
4964 let broadcast = Arc::new(broadcast::Info::default());
4965 let info = Info::default();
4966 let a = Producer::new(broadcast.clone(), "a", info.clone());
4967 let b = Producer::new(broadcast, "b", info);
4968
4969 let a = a.state.read().cache.clone();
4970 let b = b.state.read().cache.clone();
4971 assert!(!Arc::ptr_eq(&a, &b), "each track owns its account");
4972 }
4973
4974 #[tokio::test]
4977 async fn a_dynamic_defers_teardown() {
4978 let (mut producer, pool) = pooled_producer(1 << 40);
4979 let dynamic = producer.dynamic();
4980 finished_group(&mut producer, 100);
4981
4982 drop(producer);
4983 assert!(pool.used() > 0, "the handler still serves the cache");
4984
4985 drop(dynamic);
4986 assert_eq!(pool.used(), 0, "the last handle tears it down");
4987 }
4988
4989 #[tokio::test]
4995 async fn finished_track_frees_its_cache() {
4996 let (mut producer, pool) = pooled_producer(1 << 40);
4997 finished_group(&mut producer, 100);
4998 producer.finish().unwrap();
4999
5000 let state = producer.state.downgrade();
5001 drop(producer);
5002
5003 assert!(state.upgrade().is_none(), "the track state is freed");
5004 assert_eq!(pool.used(), 0, "so are its cached bytes");
5005 }
5006
5007 #[tokio::test]
5011 async fn teardown_ignores_a_settling_group() {
5012 let (mut producer, pool) = pooled_producer(1 << 40);
5013 finished_group(&mut producer, 100);
5014
5015 let settling = producer.state.downgrade().upgrade().expect("open");
5017 drop(producer);
5018
5019 assert_eq!(pool.used(), 0, "the abrupt teardown still released the cache");
5020 drop(settling);
5021 }
5022
5023 #[tokio::test]
5026 async fn cached_group_outlives_its_track() {
5027 let (mut producer, pool) = pooled_producer(1 << 40);
5028 let sequence = finished_group(&mut producer, 100);
5029 let group = producer.consume().peek_group(sequence).expect("cached");
5030 producer.finish().unwrap();
5031
5032 let state = producer.state.downgrade();
5033 drop(producer);
5034 assert!(state.upgrade().is_none(), "the track state is freed");
5035 assert!(pool.used() > 0, "the retained group keeps its own bytes");
5036
5037 drop(group);
5038 assert_eq!(pool.used(), 0, "which it releases when dropped");
5039 }
5040
5041 #[tokio::test]
5045 async fn pre_accept_backfill_settles_late_writes() {
5046 tokio::time::pause();
5047
5048 let pool = cache::Pool::new(2_000);
5049 let broadcast = broadcast::Info {
5050 origin: crate::origin::Info::default().with_pool(pool.clone()),
5051 ..Default::default()
5052 };
5053 let request = Request::new(Arc::new(broadcast), "test");
5054 let dynamic = request.dynamic();
5055 let consumer = request.consume();
5056
5057 let pending = consumer.fetch_group(0, None);
5059 let req = dynamic
5060 .requested_group()
5061 .now_or_never()
5062 .expect("should not block")
5063 .unwrap();
5064 let mut backfill = req.accept(None).unwrap();
5065 pending.await.unwrap();
5066
5067 let mut producer = request.accept(None);
5069 producer.append_group().unwrap().finish().unwrap();
5070
5071 backfill
5074 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; 300_000]))
5075 .unwrap();
5076
5077 assert!(
5078 pool.used() <= 5_000,
5079 "the frame write settled the debt: {}",
5080 pool.used()
5081 );
5082 }
5083
5084 #[tokio::test]
5088 async fn write_restarts_retention_clock() {
5089 tokio::time::pause();
5090
5091 let (mut producer, _pool) = pooled_producer(1 << 40);
5092 let mut straggler = producer.append_group().unwrap(); producer.append_group().unwrap().finish().unwrap(); tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
5097 straggler
5098 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; 100]))
5099 .unwrap();
5100 producer.append_group().unwrap().finish().unwrap(); let consumer = producer.consume();
5103 assert!(consumer.peek_group(0).is_some(), "the write restarted the clock");
5104
5105 tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
5107 producer.append_group().unwrap().finish().unwrap(); assert!(consumer.peek_group(0).is_none(), "idle content still expires");
5109 }
5110
5111 #[tokio::test]
5114 async fn refreshed_front_does_not_starve_expiry() {
5115 tokio::time::pause();
5116
5117 let (mut producer, _pool) = pooled_producer(1 << 40);
5118 let dynamic = producer.dynamic();
5119 let consumer = producer.consume();
5120
5121 producer.create_group(10u64.into()).unwrap().finish().unwrap();
5122 for sequence in 1..=5u64 {
5123 let pending = consumer.fetch_group(sequence, None);
5124 let req = dynamic
5125 .requested_group()
5126 .now_or_never()
5127 .expect("should not block")
5128 .unwrap();
5129 let mut group = req.accept(None).unwrap();
5130 group
5131 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; 100]))
5132 .unwrap();
5133 group.finish().unwrap();
5134 pending.await.unwrap();
5135 }
5136
5137 tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
5140 for sequence in 1..=4u64 {
5141 consumer.fetch_group(sequence, None).await.unwrap();
5142 }
5143
5144 for _ in 0..3 {
5146 producer.append_group().unwrap().finish().unwrap();
5147 }
5148 assert!(consumer.peek_group(5).is_none(), "expired backfill is reclaimed");
5149 assert!(consumer.peek_group(1).is_some(), "refreshed backfill survives");
5150 }
5151
5152 #[tokio::test]
5155 async fn recreated_sequence_delivered_once() {
5156 let (mut producer, _pool) = pooled_producer(1 << 40);
5157
5158 producer.create_group(0u64.into()).unwrap().finish().unwrap();
5159 let aborted = producer.create_group(1u64.into()).unwrap();
5160 aborted.abort(Error::Cancel).unwrap();
5161 producer.create_group(2u64.into()).unwrap().finish().unwrap();
5162 producer.create_group(1u64.into()).unwrap().finish().unwrap();
5163
5164 let mut subscriber = producer.subscribe(None);
5165 assert_eq!(subscriber.assert_group().sequence, 0);
5166 assert_eq!(subscriber.assert_group().sequence, 2);
5167 assert_eq!(
5168 subscriber.assert_group().sequence,
5169 1,
5170 "replacement arrives at its own position"
5171 );
5172 subscriber.assert_no_group();
5173 }
5174
5175 #[tokio::test]
5179 async fn datagrams_do_not_block_eviction() {
5180 tokio::time::pause();
5181
5182 let (mut producer, pool) = pooled_producer(1_000);
5183 for _ in 0..10 {
5184 finished_group(&mut producer, 1_000);
5185 producer.append_datagram(Timestamp::ZERO, &b"beat"[..]).unwrap();
5186 }
5187
5188 let consumer = producer.consume();
5189 assert!(consumer.peek_group(0).is_none(), "old groups still evict");
5190 assert!(
5191 pool.used() < 4 * 1_256,
5192 "interleaved datagrams must not bypass the budget: {}",
5193 pool.used()
5194 );
5195 }
5196
5197 #[tokio::test]
5201 async fn aborted_group_leaves_no_ghost_sample() {
5202 tokio::time::pause();
5203
5204 let (mut producer, pool) = pooled_producer(1 << 40);
5205 let group0 = producer.append_group().unwrap();
5206 producer.append_group().unwrap(); assert!(pool.average().is_some(), "demoted group is sampled");
5209 group0.abort(Error::Cancel).unwrap();
5210 assert_eq!(pool.average(), None, "the abort must remove the sample");
5211 }
5212
5213 #[tokio::test]
5216 async fn empty_groups_repay_overhead() {
5217 tokio::time::pause();
5218
5219 let (mut producer, pool) = pooled_producer(1_000);
5220 for _ in 0..100 {
5221 let mut group = producer.append_group().unwrap();
5222 group.finish().unwrap();
5223 }
5224
5225 assert!(
5226 pool.used() <= 3_000,
5227 "empty-group overhead must stay near the budget: {}",
5228 pool.used()
5229 );
5230 }
5231
5232 #[tokio::test]
5235 async fn growth_on_demoted_group_is_billed() {
5236 tokio::time::pause();
5237
5238 let (mut producer, pool) = pooled_producer(2_000);
5239 let mut straggler = producer.append_group().unwrap(); producer.append_group().unwrap().finish().unwrap(); straggler
5244 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; 10_000]))
5245 .unwrap();
5246
5247 producer.append_group().unwrap().finish().unwrap(); let consumer = producer.consume();
5251 assert!(consumer.peek_group(0).is_none(), "the ballooned group is evicted");
5252 assert!(pool.used() <= 3_000, "growth is reclaimed: {}", pool.used());
5253 }
5254
5255 #[tokio::test]
5258 async fn refilled_sequence_stays_out_of_subscriptions() {
5259 let (mut producer, _pool) = pooled_producer(1 << 40);
5260 let dynamic = producer.dynamic();
5261 let consumer = producer.consume();
5262
5263 producer.create_group(0u64.into()).unwrap().finish().unwrap();
5264 let aborted = producer.create_group(1u64.into()).unwrap();
5265 aborted.abort(Error::Cancel).unwrap();
5266 producer.create_group(2u64.into()).unwrap().finish().unwrap();
5267
5268 let pending = consumer.fetch_group(1, None);
5271 let req = dynamic
5272 .requested_group()
5273 .now_or_never()
5274 .expect("should not block")
5275 .unwrap();
5276 let mut group = req.accept(None).unwrap();
5277 group
5278 .write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"backfill"))
5279 .unwrap();
5280 group.finish().unwrap();
5281 pending.await.unwrap();
5282
5283 assert!(consumer.peek_group(1).is_some());
5285 let mut subscriber = producer.subscribe(None);
5286 assert_eq!(subscriber.assert_group().sequence, 0);
5287 assert_eq!(subscriber.assert_group().sequence, 2);
5288 subscriber.assert_no_group();
5289 }
5290
5291 #[tokio::test]
5294 async fn expired_backfill_behind_refreshed_reclaimed() {
5295 tokio::time::pause();
5296
5297 let (mut producer, _pool) = pooled_producer(1 << 40);
5298 let dynamic = producer.dynamic();
5299 let consumer = producer.consume();
5300
5301 producer.create_group(5u64.into()).unwrap().finish().unwrap();
5302 for sequence in [2u64, 3u64] {
5303 let pending = consumer.fetch_group(sequence, None);
5304 let req = dynamic
5305 .requested_group()
5306 .now_or_never()
5307 .expect("should not block")
5308 .unwrap();
5309 let mut group = req.accept(None).unwrap();
5310 group
5311 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; 100]))
5312 .unwrap();
5313 group.finish().unwrap();
5314 pending.await.unwrap();
5315 }
5316
5317 tokio::time::advance(Duration::from_secs(4)).await;
5319 consumer.fetch_group(2, None).await.unwrap();
5320 tokio::time::advance(DEFAULT_LATENCY_MAX - Duration::from_secs(2)).await;
5321 producer.create_group(6u64.into()).unwrap().finish().unwrap();
5322
5323 let consumer = producer.consume();
5324 assert!(consumer.peek_group(2).is_some(), "refreshed backfill survives");
5325 assert!(consumer.peek_group(3).is_none(), "expired backfill is reclaimed");
5326 }
5327
5328 #[tokio::test]
5331 async fn same_tick_fetch_protects() {
5332 tokio::time::pause();
5333
5334 let (mut producer, _pool) = pooled_producer(10_000);
5336 let consumer = producer.consume();
5337
5338 finished_group(&mut producer, 3_000); finished_group(&mut producer, 3_000); finished_group(&mut producer, 3_000); consumer.fetch_group(0, None).await.unwrap();
5343
5344 finished_group(&mut producer, 3_000); finished_group(&mut producer, 3_000); assert!(consumer.peek_group(0).is_some(), "same-tick refresh protects");
5348 assert!(consumer.peek_group(1).is_none(), "the unread group dies instead");
5349 }
5350
5351 #[tokio::test]
5355 async fn refetched_latest_stays_protected() {
5356 tokio::time::pause();
5357
5358 let (mut producer, _pool) = pooled_producer(10_000);
5359 let dynamic = producer.dynamic();
5360 let consumer = producer.consume();
5361
5362 let straggler = producer.append_group().unwrap(); let latest = producer.append_group().unwrap(); latest.abort(Error::Cancel).unwrap();
5367
5368 let pending = consumer.fetch_group(1, None);
5370 let req = dynamic
5371 .requested_group()
5372 .now_or_never()
5373 .expect("should not block")
5374 .unwrap();
5375 let mut group = req.accept(None).unwrap();
5376 group
5377 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; 1000]))
5378 .unwrap();
5379 group.finish().unwrap();
5380 pending.await.unwrap();
5381
5382 {
5385 let state = producer.state.read();
5386 assert!(state.lookup.contains_key(&1), "refetched group is cached");
5387 assert!(
5388 state.evict.iter().all(|(sequence, _)| *sequence != 1),
5389 "the live edge must not be an eviction candidate"
5390 );
5391 }
5392 drop(straggler);
5393 }
5394
5395 #[tokio::test]
5398 async fn eviction_allows_refetch() {
5399 tokio::time::pause();
5400
5401 let (mut producer, _pool) = pooled_producer(10_000);
5402 let dynamic = producer.dynamic();
5403
5404 finished_group(&mut producer, 10_000); finished_group(&mut producer, 10_000); finished_group(&mut producer, 10_000); let consumer = producer.consume();
5409 assert!(consumer.peek_group(0).is_none());
5410 let pending = consumer.fetch_group(0, None);
5411
5412 let req = dynamic
5413 .requested_group()
5414 .now_or_never()
5415 .expect("should not block")
5416 .unwrap();
5417 assert_eq!(req.sequence(), 0);
5418
5419 let mut group = req.accept(None).unwrap();
5420 group
5421 .write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"refetched"))
5422 .unwrap();
5423 group.finish().unwrap();
5424
5425 let mut group = pending.await.unwrap();
5426 assert_eq!(&group.read_frame().await.unwrap().unwrap().payload[..], b"refetched");
5427 }
5428
5429 #[tokio::test]
5432 async fn fetched_backfill_not_subscribed() {
5433 let (mut producer, _pool) = pooled_producer(1 << 40);
5434 let dynamic = producer.dynamic();
5435 let consumer = producer.consume();
5436
5437 producer.create_group(5u64.into()).unwrap().finish().unwrap();
5439 producer.create_group(6u64.into()).unwrap().finish().unwrap();
5440
5441 let pending = consumer.fetch_group(2, None);
5443 let req = dynamic
5444 .requested_group()
5445 .now_or_never()
5446 .expect("should not block")
5447 .unwrap();
5448 let mut group = req.accept(None).unwrap();
5449 group
5450 .write_frame(Timestamp::ZERO, bytes::Bytes::from_static(b"backfill"))
5451 .unwrap();
5452 group.finish().unwrap();
5453 let mut fetched = pending.await.unwrap();
5454 assert_eq!(&fetched.read_frame().await.unwrap().unwrap().payload[..], b"backfill");
5455 assert!(consumer.peek_group(2).is_some(), "backfill is cached for later fetches");
5456
5457 let mut subscriber = producer.subscribe(None);
5459 assert_eq!(subscriber.assert_group().sequence, 5);
5460 assert_eq!(subscriber.assert_group().sequence, 6);
5461 subscriber.assert_no_group();
5462 }
5463
5464 #[tokio::test]
5467 async fn expired_backfill_reclaimed() {
5468 tokio::time::pause();
5469
5470 let (mut producer, pool) = pooled_producer(1 << 40);
5471 let dynamic = producer.dynamic();
5472 let consumer = producer.consume();
5473
5474 producer.create_group(5u64.into()).unwrap().finish().unwrap();
5475
5476 let pending = consumer.fetch_group(2, None);
5478 let req = dynamic
5479 .requested_group()
5480 .now_or_never()
5481 .expect("should not block")
5482 .unwrap();
5483 let mut group = req.accept(None).unwrap();
5484 group
5485 .write_frame(Timestamp::ZERO, bytes::Bytes::from(vec![0u8; 1000]))
5486 .unwrap();
5487 group.finish().unwrap();
5488 pending.await.unwrap();
5489 let used = pool.used();
5490
5491 tokio::time::advance(DEFAULT_LATENCY_MAX + Duration::from_secs(1)).await;
5493 producer.create_group(6u64.into()).unwrap().finish().unwrap();
5494
5495 assert!(consumer.peek_group(2).is_none(), "expired backfill is reclaimed");
5496 assert!(pool.used() < used, "its bytes are released");
5497 }
5498
5499 #[tokio::test]
5500 async fn fetch_aborts_with_track() {
5501 let producer = track_producer("test", None);
5502 let dynamic = producer.dynamic();
5503 let consumer = producer.consume();
5504
5505 let pending = consumer.fetch_group(3, None);
5506 assert!(kio::Pollable::poll(&*pending, &kio::Waiter::noop()).is_pending());
5507
5508 producer.abort(Error::Cancel).unwrap();
5509 assert!(pending.await.is_err());
5510 drop(dynamic);
5511 }
5512}