Skip to main content

liminal/channel/
record.rs

1//! What a recording publish saw (SRV-012, the tap's stations and hops): whom
2//! its fan-out queued it for and when, and when a durable channel's append
3//! completed. Filled by [`ChannelHandle::publish_recording`] into a record
4//! its caller keeps and reuses.
5//!
6//! [`ChannelHandle::publish_recording`]: crate::channel::ChannelHandle::publish_recording
7
8use std::time::Instant;
9
10/// One recipient: the subscriber pid, and the instant its inbox queued the
11/// envelope.
12pub type Recipient = (u64, Instant);
13
14/// What a recording publish saw. Cleared at the start of each recording
15/// publish and its allocation reused, so a caller that keeps one record
16/// allocates only while it grows to the widest fan-out.
17#[derive(Debug, Default)]
18pub struct PublishRecord {
19    pub(crate) recipients: Vec<Recipient>,
20    pub(crate) appended_at: Option<Instant>,
21}
22
23impl PublishRecord {
24    /// An empty record with room for `recipients` before it grows.
25    #[must_use]
26    pub fn with_capacity(recipients: usize) -> Self {
27        Self {
28            recipients: Vec::with_capacity(recipients),
29            appended_at: None,
30        }
31    }
32
33    /// How many recipients it holds before it grows.
34    #[must_use]
35    pub fn capacity(&self) -> usize {
36        self.recipients.capacity()
37    }
38
39    /// Each subscriber whose inbox queued the envelope, with the instant it
40    /// did, in fan-out order: exactly
41    /// [`ChannelDelivery::delivered_count`](crate::channel::ChannelDelivery::delivered_count)
42    /// of them, never one that matched and refused.
43    #[must_use]
44    pub fn recipients(&self) -> &[Recipient] {
45        &self.recipients
46    }
47
48    /// When a durable channel's append of the envelope completed, before its
49    /// fan-out; `None` for a channel that is not durable.
50    #[must_use]
51    pub const fn appended_at(&self) -> Option<Instant> {
52        self.appended_at
53    }
54
55    /// Empties the record, keeping its allocation.
56    pub fn clear(&mut self) {
57        self.recipients.clear();
58        self.appended_at = None;
59    }
60}
61
62/// The instant a recording publish reads: only ever called while a record
63/// is lent, so a publish with none reads no clock. A test build counts the
64/// reads, so a pin can show it.
65pub fn recorded_now() -> Instant {
66    #[cfg(test)]
67    CLOCK_READS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
68    Instant::now()
69}
70
71/// Clock reads [`recorded_now`] has made in this process. Test-only.
72#[cfg(test)]
73pub static CLOCK_READS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);