Skip to main content

dynomite/seeds/
mod.rs

1//! Pluggable seeds providers.
2//!
3//! A seeds provider hands the gossip task an up-to-date list of
4//! peers in the canonical
5//! `host:port:rack:dc:tokens|host:port:rack:dc:tokens` format.
6//! Three implementations ship with the engine:
7//!
8//! * [`simple::SimpleSeedsProvider`] - returns the seeds parsed
9//!   from the YAML config.
10//! * [`dns::DnsSeedsProvider`] - resolves a configured DNS
11//!   hostname to a list of IPs (mirroring the reference
12//!   `dns_get_seeds`, with the resolver factored behind a trait so
13//!   the unit test can substitute a deterministic implementation).
14//! * [`florida::FloridaSeedsProvider`] - HTTP GET to a Florida
15//!   service, parses the body. Hand-rolled HTTP/1.0 client over
16//!   `tokio::net::TcpStream` to stay within the locked dependency
17//!   set.
18//!
19//! The trait shape is the seam exposed through the embedding API;
20//! the embed wrapper only needs to forward to it.
21//!
22//! # Examples
23//!
24//! ```
25//! use dynomite::seeds::{SeedsProvider, simple::SimpleSeedsProvider};
26//! use dynomite::conf::ConfDynSeed;
27//! let seeds = vec![ConfDynSeed::parse("h1:8101:rA:dc1:1").unwrap()];
28//! let p = SimpleSeedsProvider::new(seeds);
29//! let got = p.get_seeds().unwrap();
30//! assert_eq!(got.len(), 1);
31//! ```
32
33pub mod dns;
34pub mod florida;
35pub mod simple;
36
37use std::io;
38
39use thiserror::Error;
40
41use crate::conf::ConfDynSeed;
42
43/// Error type for seeds providers.
44#[derive(Debug, Error)]
45pub enum SeedsError {
46    /// The provider has no fresh data: the gossip task should
47    /// retry on the next interval (no fresh data this round).
48    #[error("no fresh seeds")]
49    NoFreshSeeds,
50    /// I/O error.
51    #[error("io error: {0}")]
52    Io(#[from] io::Error),
53    /// Parse error.
54    #[error("parse error: {0}")]
55    Parse(String),
56    /// Endpoint returned an HTTP error.
57    #[error("http error: {0}")]
58    Http(String),
59}
60
61/// Pluggable seeds provider.
62///
63/// Implementations may block; the gossip task calls them from a
64/// dedicated tokio task with timeouts wrapped at the call site.
65/// `SeedsProvider` is an async trait emulated via an associated
66/// future type so it can be implemented for both blocking
67/// (`SimpleSeedsProvider`) and async (`FloridaSeedsProvider`)
68/// shapes.
69pub trait SeedsProvider: Send + Sync {
70    /// Return the current list of seeds, or an error explaining
71    /// why no fresh data is available.
72    ///
73    /// Blocking implementations do their work synchronously and
74    /// return immediately; async implementations should run on a
75    /// blocking task spawned by the caller. The binary's runtime
76    /// wiring picks the right path; the trait stays sync
77    /// to keep the surface small for embedders.
78    fn get_seeds(&self) -> Result<Vec<ConfDynSeed>, SeedsError>;
79}
80
81/// Marker trait used to register custom seeds providers through
82/// the embedding API. Implementing [`SeedsProvider`] is sufficient.
83impl<T> SeedsProvider for std::sync::Arc<T>
84where
85    T: SeedsProvider + ?Sized,
86{
87    fn get_seeds(&self) -> Result<Vec<ConfDynSeed>, SeedsError> {
88        (**self).get_seeds()
89    }
90}