use std::collections::VecDeque;
use std::time::{Duration, Instant};
use transmux::ll_hls::PartInfo;
use crate::ids::{PartIndex, SegmentSeq};
use crate::trunk::{PartEntry, SegmentEntry, SegmentWriter, TryPublishSegmentError};
pub const PENDING_PUBLISH_QUEUE_CAP: usize = 8;
pub const PENDING_PUBLISH_MAX_WAIT: Duration = Duration::from_secs(30);
#[non_exhaustive]
pub enum PendingPublish {
Part(PartEntry),
Segment(SegmentEntry),
}
pub fn part_entry(part: PartInfo) -> PartEntry {
PartEntry::new(
part.bytes,
SegmentSeq::new(part.segment_seq),
PartIndex::new(part.part_index),
Duration::from_secs_f64(part.duration),
part.independent,
)
}
pub fn drain_publish_queue(
segment_writer: &mut SegmentWriter,
pending: &mut VecDeque<PendingPublish>,
pending_since: &mut Option<Instant>,
) {
loop {
let Some(item) = pending.pop_front() else {
*pending_since = None;
return;
};
match item {
PendingPublish::Part(part) => {
segment_writer.publish_part(part);
*pending_since = None;
}
PendingPublish::Segment(entry) => {
match segment_writer.try_publish_segment(entry) {
Ok(()) => {
*pending_since = None;
}
Err(TryPublishSegmentError::NonMonotonic(e)) => {
tracing::error!(
error = %e,
"dropping a segment with a non-monotonic sequence_number"
);
*pending_since = None;
}
Err(TryPublishSegmentError::WouldStall(entry)) => {
pending.push_front(PendingPublish::Segment(entry));
let now = Instant::now();
let since = *pending_since.get_or_insert(now);
let waited_too_long =
now.saturating_duration_since(since) >= PENDING_PUBLISH_MAX_WAIT;
let queued_segments = pending
.iter()
.filter(|item| matches!(item, PendingPublish::Segment(_)))
.count();
let queue_too_deep = queued_segments >= PENDING_PUBLISH_QUEUE_CAP;
if (waited_too_long || queue_too_deep)
&& segment_writer.expire_stalled_pins() > 0
{
continue;
}
return;
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::trunk::{Trunk, TrunkConfig};
use broadcast_common::Timestamp;
use core::num::NonZeroUsize;
use transmux::SegmentMeta;
fn nz(n: usize) -> NonZeroUsize {
NonZeroUsize::new(n).expect("non-zero")
}
fn segment(seq: u32) -> SegmentEntry {
SegmentEntry::new(
vec![0u8; 4],
SegmentSeq::new(seq),
Duration::from_secs(1),
Timestamp::from_nanos(0),
SegmentMeta {
discontinuous: false,
},
)
}
#[test]
fn a_drained_queue_is_empty_with_no_stall_clock() {
let trunk = Trunk::new(
TrunkConfig::default()
.with_timed_capacity(nz(8))
.with_sparse_capacity(nz(8))
.with_segment_capacity(nz(8))
.with_event_capacity(nz(8))
.with_part_capacity(nz(8)),
);
let mut writer = trunk.segment_writer().expect("segment writer");
let mut pending = VecDeque::from([PendingPublish::Segment(segment(1))]);
let mut since = Some(Instant::now());
drain_publish_queue(&mut writer, &mut pending, &mut since);
assert!(pending.is_empty());
assert!(since.is_none());
}
}