pub enum FeedKind {
Rss,
Publication,
Unsupported,
}Expand description
What the poller does with a feed row.
A column, not a predicate. “Can this be fetched?” used to be
substr(url, 1, 5) = 'at://' spliced into four statements, with a fifth
reader that had already drifted from them. Deciding it once, in Rust, at
insert — and storing the answer — means SQL cannot disagree with the
fetcher, and wiring the standard.site reader becomes a change to this
function plus a dispatch, rather than an edit to every statement that
mentions a URL.
Variants§
Rss
An RSS/Atom/JSON feed document fetched over http(s).
Publication
An at://…/site.standard.publication/… record pair in somebody’s PDS,
read by standard_site and pollable since 0.4.0.
Unsupported
Any other at:// row: another collection, or a spelling the storage
guard would refuse. Rows like this predate that guard. Never
pollable — handing one to the standard.site reader would fail its
collection check every tick and publish it as an unreachable publisher —
and counted as unpollable so the capacity it holds stays visible.
Implementations§
Source§impl FeedKind
impl FeedKind
Sourcepub const POLLABLE: &'static [FeedKind]
pub const POLLABLE: &'static [FeedKind]
The kinds the scheduler may select: RSS, and since 0.4.0 standard.site
publications. FeedKind::Unsupported is the kind it never selects.
This is the canonical home of the exclusion; the other sites point
here. It used to be a SQL string predicate in store, carrying
its own copy of the rule — which is how one reader (count_feeds) came
to drift from it unnoticed.
Why an unpollable kind is skipped rather than failed: an Unsupported
row names nothing either reader can fetch — another collection, a handle,
a non-canonical spelling — so polling it could only fail.
Handing such a row to the poller does not leave the feature dormant — it
manufactures one permanent failure per row, which the public cause
histogram then reports as an unreachable publisher. Unsupported is not
broken, and telling those apart is the entire reason a failure cause is
recorded. (Rows like this exist: subscriptions written by other clients
before this reader refused the scheme.)
store::count_feeds is deliberately NOT filtered by this. The
global ceiling bounds storage on a small box, and an unpollable row
occupies a row, so it counts against the cap. An earlier version of this
note listed the readers without naming the exception, which read as
completeness it did not have: a review found the ceiling consuming
capacity that appeared on no surface, since /stats measures the poller
and excludes these rows. /admin/metrics renders
store::unpollable_feeds for exactly that reason.
Adding a kind here makes a population of rows due all at once.
store::due_feeds orders next_poll IS NOT NULL, next_poll ASC, so a
row with no scheduled poll sorts ahead of every dated one. Rows that
were never pollable have no schedule, so the boot that reclassifies
them hands the poller a block of N rows that outrank every regular
feed until they drain — ceil(N / batch) ticks, measured, during which
/stats shows a climbing backlog and nothing logs why. Bounded and
harmless at ninety feeds; not at ten thousand. Whoever wires the next
kind should seed or stagger next_poll for the rows it admits.
Sourcepub const AGED: &'static [FeedKind]
pub const AGED: &'static [FeedKind]
The kinds whose entries the retention window applies to.
Age is the wrong retention policy for an archive, and that is a
measurement, not a preference. Three real publications, read through
standard_site::fetch on 2026-09-27: Standard.site’s newest document was
131 days old, Annotated’s 109 (with its oldest at 373), and minus
listens’ 241. Against the instance default window of 14 days, every
one of them stored zero rows — a green poll, an empty feed, and an
info log as the only trace. Long-form publishing is not news-paced.
So a publication is bounded by COUNT instead: max_entries_per_feed in
insert_entries, which caps a feed at the newest N plus up to N starred.
That is a real bound — it is what keeps this from being “retention off” —
and it is the one that suits a source whose value is its archive.
A generous absolute ceiling still applies (publication_retention_days),
because “not aged out” must not mean “immortal”: rows belonging to a feed
nobody polls any more would otherwise never be reaped at all, and the
per-feed trim only runs when a poll stores something.
Sourcepub fn parse(raw: &str) -> Option<Self>
pub fn parse(raw: &str) -> Option<Self>
A closed vocabulary on the way back in: a kind written by a newer build is not silently read as one this build knows.
Sourcepub fn of(url: &str) -> Self
pub fn of(url: &str) -> Self
What a URL will be stored as. The only place the question is asked.
store::feeds.kind is a cache of this function, re-derived from the URL
on every start and on every upsert, so a change here needs no migration
and SQL cannot hold an opinion of its own about what a row is.
Case-insensitive on purpose. An earlier version was case-sensitive,
with a test pinning that a mixed-case At:// row IS handed to the
poller — reasoning that if Rust does not call it an at-URI, neither
should anything else. That was wrong in the direction that matters: URL
schemes are case-insensitive, so Url::parse folds At:// to scheme
at and net::check_scheme refuses it (the DID form does not parse at
all). The row could only fail, every tick, forever, and be published in
the fetch bucket as an unreachable publisher. Storing a non-canonical
spelling is separately refused, because feeds.url is UNIQUE.
Trait Implementations§
impl Copy for FeedKind
impl Eq for FeedKind
impl StructuralPartialEq for FeedKind
Auto Trait Implementations§
impl Freeze for FeedKind
impl RefUnwindSafe for FeedKind
impl Send for FeedKind
impl Sync for FeedKind
impl Unpin for FeedKind
impl UnsafeUnpin for FeedKind
impl UnwindSafe for FeedKind
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more