This commit is contained in:
2026-09-22 16:38:08 +07:00
parent 6fac58d494
commit 1c5ef58049
11 changed files with 808 additions and 181 deletions
+58 -56
View File
@@ -19,13 +19,13 @@ use crate::sync::connect_relays;
/// How long one relay is given to answer one page of history.
const PAGE_TIMEOUT: Duration = Duration::from_secs(10);
/// How far below a cursor a warm window reaches back.
pub const CURSOR_OVERLAP_MS: u64 = 60_000;
pub const CURSOR_OVERLAP: Duration = Duration::from_secs(60);
/// The region of history to read.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Window {
pub until_ms: Option<u64>,
pub since_ms: Option<u64>,
pub until: Option<Timestamp>,
pub since: Option<Timestamp>,
}
impl Window {
@@ -34,28 +34,28 @@ impl Window {
Self::default()
}
/// The wraps strictly older than `oldest_ms`.
pub fn older_than(oldest_ms: u64) -> Self {
/// The wraps strictly older than `oldest`.
pub fn older_than(oldest: Timestamp) -> Self {
Self {
until_ms: Some(oldest_ms.saturating_sub(1)),
since_ms: None,
until: Some(oldest - 1u64),
since: None,
}
}
/// The region between `since_ms` and `oldest_ms`, both inclusive.
pub fn between(since_ms: u64, oldest_ms: u64) -> Self {
/// The region between `since` and `oldest`, both inclusive.
pub fn between(since: Timestamp, oldest: Timestamp) -> Self {
Self {
until_ms: Some(oldest_ms.saturating_sub(1)),
since_ms: Some(since_ms),
until: Some(oldest - 1u64),
since: Some(since),
}
}
/// The window a channel is opened with.
pub fn opening(cursor: ChannelCursor) -> Self {
match cursor.newest_ms {
Some(newest_ms) => Self {
since_ms: Some(newest_ms.saturating_sub(CURSOR_OVERLAP_MS)),
until_ms: None,
match cursor.newest {
Some(newest) => Self {
since: Some(newest - CURSOR_OVERLAP),
until: None,
},
None => Self::default(),
}
@@ -139,8 +139,8 @@ pub struct WrapPage {
pub raw: usize,
/// Wraps that reached us under a held plane but that no held key could open.
pub unreadable: usize,
pub newest_ms: Option<u64>,
pub oldest_ms: Option<u64>,
pub newest: Option<Timestamp>,
pub oldest: Option<Timestamp>,
pub exhausted: bool,
pub failed: bool,
pub errors: usize,
@@ -235,7 +235,7 @@ fn read_under(
) -> Result<(OpenedStream, ChatRumor)> {
if held
.retired_at
.is_some_and(|retired| wrap.created_at.as_secs() > retired)
.is_some_and(|retired| wrap.created_at > retired)
{
bail!("sealed after the key that reads it was retired");
}
@@ -250,12 +250,12 @@ fn wrap_filter(authors: &[PublicKey], window: Window, limit: usize) -> Filter {
.authors(authors.iter().copied())
.limit(limit);
if let Some(until_ms) = window.until_ms {
filter = filter.until(Timestamp::from_secs(until_ms / 1000));
if let Some(until) = window.until {
filter = filter.until(until);
}
if let Some(since_ms) = window.since_ms {
filter = filter.since(Timestamp::from_secs(since_ms / 1000));
if let Some(since) = window.since {
filter = filter.since(since);
}
filter
@@ -373,12 +373,12 @@ where
#[derive(Debug)]
struct Walk {
relays: Vec<Walker>,
since_ms: Option<u64>,
since: Option<Timestamp>,
/// The inclusive upper bound of the next page.
cursor: Option<u64>,
cursor: Option<Timestamp>,
seen: BTreeSet<EventId>,
newest_ms: Option<u64>,
oldest_ms: Option<u64>,
newest: Option<Timestamp>,
oldest: Option<Timestamp>,
raw: usize,
errors: usize,
/// Wraps the caller could not read under any held key.
@@ -402,11 +402,11 @@ impl Walk {
.cloned()
.map(|url| Walker { url, dead: false })
.collect(),
since_ms: window.since_ms,
cursor: window.until_ms,
since: window.since,
cursor: window.until,
seen: BTreeSet::new(),
newest_ms: None,
oldest_ms: None,
newest: None,
oldest: None,
raw: 0,
errors: 0,
unreadable: 0,
@@ -421,8 +421,8 @@ impl Walk {
/// The region the next page asks for.
fn region(&self) -> Window {
Window {
until_ms: self.cursor,
since_ms: self.since_ms,
until: self.cursor,
since: self.since,
}
}
@@ -444,13 +444,13 @@ impl Walk {
self.bottom = true;
}
let mut oldest: Option<u64> = None;
let mut oldest: Option<Timestamp> = None;
let mut events = Vec::with_capacity(page.len());
for event in page {
let at_ms = event.created_at.as_secs().saturating_mul(1000);
self.newest_ms = Some(self.newest_ms.map_or(at_ms, |newest| newest.max(at_ms)));
oldest = Some(oldest.map_or(at_ms, |oldest| oldest.min(at_ms)));
let at = event.created_at;
self.newest = Some(self.newest.map_or(at, |newest| newest.max(at)));
oldest = Some(oldest.map_or(at, |oldest| oldest.min(at)));
if self.seen.insert(event.id) {
self.raw += 1;
@@ -459,7 +459,7 @@ impl Walk {
}
match oldest {
Some(oldest) if oldest > 0 => self.cursor = Some(oldest - 1),
Some(oldest) if !oldest.is_zero() => self.cursor = Some(oldest - 1u64),
Some(_) => self.bottom = true,
None => {}
}
@@ -474,8 +474,8 @@ impl Walk {
opened,
raw: self.raw,
unreadable: self.unreadable,
newest_ms: self.newest_ms,
oldest_ms: self.oldest_ms,
newest: self.newest,
oldest: self.oldest,
exhausted: swept && self.raw > 0,
failed: self.errors > 0 || (self.bottom && self.raw == 0),
errors: self.errors,
@@ -500,9 +500,8 @@ mod tests {
let mut events: Vec<Event> = database
.iter()
.filter(|event| {
let at_ms = event.created_at.as_secs().saturating_mul(1000);
window.until_ms.is_none_or(|until| at_ms <= until)
&& window.since_ms.is_none_or(|since| at_ms >= since)
window.until.is_none_or(|until| event.created_at <= until)
&& window.since.is_none_or(|since| event.created_at >= since)
})
.cloned()
.collect();
@@ -613,7 +612,7 @@ mod tests {
let held = HeldKey {
epoch: Epoch(0),
key: SECRET,
retired_at: Some(1_000),
retired_at: Some(Timestamp::from_secs(1_000)),
};
let wrap_at = |channel: &ChannelId, at_ms: u64| {
@@ -656,7 +655,7 @@ mod tests {
assert!(page.failed);
assert!(!page.exhausted);
assert_eq!(page.raw, 0);
assert_eq!(page.oldest_ms, None);
assert_eq!(page.oldest, None);
}
#[test]
@@ -681,7 +680,10 @@ mod tests {
#[test]
fn a_page_boundary_is_exclusive() {
let database = BTreeSet::from([event_at(1_700_000_000_000), event_at(1_700_000_001_000)]);
let database = BTreeSet::from([
event_at(Timestamp::from_secs(1_700_000_000)),
event_at(Timestamp::from_secs(1_700_000_001)),
]);
let mut walk = Walk::new(&[relay_url("history")], Window::newest());
let first = walk.accept(serve_page(&database, walk.region(), 1), 1);
@@ -693,16 +695,16 @@ mod tests {
let oldest = second
.iter()
.map(|event| event.created_at.as_secs() * 1000)
.map(|event| event.created_at)
.min()
.expect("one wrap");
assert_eq!(oldest, 1_700_000_000_000);
assert_eq!(oldest, Timestamp::from_secs(1_700_000_000));
}
fn event_at(at_ms: u64) -> Event {
fn event_at(at: Timestamp) -> Event {
let keys = Keys::generate();
EventBuilder::new(Kind::TextNote, "page")
.custom_created_at(Timestamp::from_secs(at_ms / 1000))
.custom_created_at(at)
.finalize(&keys)
.expect("signs")
}
@@ -714,23 +716,23 @@ mod tests {
assert_eq!(Window::opening(ChannelCursor::default()), Window::default());
let warm = Window::opening(ChannelCursor {
newest_ms: Some(2_000_000),
oldest_ms: Some(1_000),
newest: Some(Timestamp::from_secs(2_000_000)),
oldest: Some(Timestamp::from_secs(1_000)),
exhausted: false,
});
assert_eq!(
warm,
Window {
since_ms: Some(2_000_000 - CURSOR_OVERLAP_MS),
until_ms: None,
since: Some(Timestamp::from_secs(2_000_000) - CURSOR_OVERLAP),
until: None,
}
);
assert_eq!(
Window::older_than(1_000),
Window::older_than(Timestamp::from_secs(1_000)),
Window {
since_ms: None,
until_ms: Some(999),
since: None,
until: Some(Timestamp::from_secs(999)),
}
);
}