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}