1use std::collections::HashMap;
16use std::net::SocketAddr;
17use std::sync::atomic::{AtomicU64, Ordering};
18use std::sync::{Arc, OnceLock, Weak};
19
20use parking_lot::Mutex;
21use tokio::net::TcpListener;
22use tokio::task::JoinHandle;
23use tokio::time::{Duration, MissedTickBehavior};
24use tokio_util::sync::CancellationToken;
25
26use crate::cluster::dispatch::{ClusterDispatcher, DispatchPlan};
27use crate::cluster::peer::{Peer, PeerEndpoint, PeerState};
28use crate::cluster::pool::{PoolConfig, ServerPool};
29use crate::conf::{ConfDynSeed, ConfPool, Config};
30use crate::embed::error::EmbedError;
31use crate::embed::events::{CloseReason, ConnRoleTag, EventBus, EventStream, ServerEvent};
32use crate::embed::hooks::{
33 CryptoProvider, Datastore, LoggingMetricsSink, MetricsSink, SeedsProvider,
34};
35use crate::embed::snapshots::{DatacenterSnapshot, PeerSnapshot, RingSnapshot};
36use crate::events::EventManager;
37use crate::hashkit::DynToken;
38use crate::msg::Msg;
39use crate::stats::{
40 describe_stats, MetricSpec, PoolField, PoolStats, ServerField, ServerStats, ServiceInfo,
41 Snapshot, Stats,
42};
43
44#[non_exhaustive]
52pub struct ServerHooks {
53 pub(crate) datastore: Option<Box<dyn Datastore>>,
54 pub(crate) seeds: Option<Box<dyn SeedsProvider>>,
55 pub(crate) crypto: Option<Box<dyn CryptoProvider>>,
56 pub(crate) metrics: Option<Box<dyn MetricsSink>>,
57}
58
59impl ServerHooks {
60 #[must_use]
62 pub fn datastore(&self) -> Option<&dyn Datastore> {
63 self.datastore.as_deref()
64 }
65
66 #[must_use]
68 pub fn seeds(&self) -> Option<&dyn SeedsProvider> {
69 self.seeds.as_deref()
70 }
71
72 #[must_use]
74 pub fn crypto(&self) -> Option<&dyn CryptoProvider> {
75 self.crypto.as_deref()
76 }
77
78 #[must_use]
80 pub fn metrics(&self) -> Option<&dyn MetricsSink> {
81 self.metrics.as_deref()
82 }
83}
84
85impl std::fmt::Debug for ServerHooks {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 f.debug_struct("ServerHooks")
88 .field("datastore", &self.datastore.is_some())
89 .field("seeds", &self.seeds.is_some())
90 .field("crypto", &self.crypto.is_some())
91 .field("metrics", &self.metrics.is_some())
92 .finish()
93 }
94}
95
96fn registry() -> &'static Mutex<HashMap<SocketAddr, Weak<ServerInner>>> {
108 static R: OnceLock<Mutex<HashMap<SocketAddr, Weak<ServerInner>>>> = OnceLock::new();
109 R.get_or_init(|| Mutex::new(HashMap::new()))
110}
111
112fn registry_register(addr: SocketAddr, server: &Arc<ServerInner>) {
113 registry().lock().insert(addr, Arc::downgrade(server));
114}
115
116fn registry_remove(addr: SocketAddr) {
117 registry().lock().remove(&addr);
118}
119
120fn registry_lookup(addr: SocketAddr) -> Option<Arc<ServerInner>> {
121 registry().lock().get(&addr).and_then(Weak::upgrade)
122}
123
124fn next_generation() -> u64 {
129 static G: AtomicU64 = AtomicU64::new(0);
130 G.fetch_add(1, Ordering::Relaxed)
131}
132
133fn next_conn_id() -> u64 {
135 static C: AtomicU64 = AtomicU64::new(0);
136 C.fetch_add(1, Ordering::Relaxed)
137}
138
139pub(crate) struct ServerInner {
141 pool: Arc<ServerPool>,
142 dispatcher: ClusterDispatcher,
143 stats: Arc<Stats>,
144 snapshot_cache: Arc<Mutex<Snapshot>>,
145 bus: EventBus,
146 events: Arc<EventManager>,
147 datastore: Box<dyn Datastore>,
148 seeds: Box<dyn SeedsProvider>,
149 metrics: Box<dyn MetricsSink>,
150 crypto: Option<Box<dyn CryptoProvider>>,
151 listen_addr: Option<SocketAddr>,
152 dyn_listen_addr: Option<SocketAddr>,
153 cancel: CancellationToken,
154 pool_name: String,
155 config: Mutex<ConfPool>,
156 generation: AtomicU64,
157 command_extension: Option<Arc<dyn crate::embed::CommandExtension>>,
158}
159
160impl std::fmt::Debug for ServerInner {
161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 f.debug_struct("ServerInner")
163 .field("pool_name", &self.pool_name)
164 .field("listen", &self.listen_addr)
165 .field("dyn_listen", &self.dyn_listen_addr)
166 .finish_non_exhaustive()
167 }
168}
169
170impl ServerInner {
171 fn dispatch_local(&self, req: Msg) -> super::hooks::BoxFuture<'_, Result<Msg, EmbedError>> {
172 let fut = self.datastore.dispatch(req);
173 Box::pin(async move {
174 let rsp = fut.await.map_err(|e| EmbedError::Inject(e.to_string()))?;
175 Ok(rsp)
176 })
177 }
178}
179
180#[derive(Debug)]
187pub struct Server {
188 pool_name: String,
189 pool: ConfPool,
190 cluster: Arc<ServerPool>,
191 hooks: ServerHooks,
192 stats: Arc<Stats>,
193 command_extension: Option<Arc<dyn crate::embed::CommandExtension>>,
194}
195
196impl Server {
197 pub(crate) fn from_pool(
199 pool_name: String,
200 pool: ConfPool,
201 hooks: ServerHooks,
202 command_extension: Option<Arc<dyn crate::embed::CommandExtension>>,
203 ) -> Self {
204 let pool_cfg = PoolConfig::from_conf(&pool_name, &pool);
205 let local_peer = build_local_peer(&pool, &pool_cfg);
206 let mut peers = vec![local_peer];
207 if let Some(seeds) = pool.dyn_seeds.as_ref() {
208 let start = u32::try_from(peers.len()).unwrap_or(0);
209 peers.extend(peers_from_seeds(&pool_cfg, seeds, start));
210 }
211 let server_pool_arc = Arc::new(ServerPool::new(pool_cfg.clone(), peers));
212 server_pool_arc.preselect_remote_racks();
213
214 let stats = Arc::new(Stats::new(
215 ServiceInfo {
216 source: pool_cfg.name.clone(),
217 version: env!("CARGO_PKG_VERSION").to_string(),
218 rack: pool_cfg.rack.clone(),
219 dc: pool_cfg.dc.clone(),
220 },
221 PoolStats::new(&pool_cfg.name),
222 ServerStats::new("backend"),
223 ));
224
225 Self {
226 pool_name,
227 pool,
228 cluster: server_pool_arc,
229 hooks,
230 stats,
231 command_extension,
232 }
233 }
234
235 #[must_use]
238 pub fn command_extension(&self) -> Option<&Arc<dyn crate::embed::CommandExtension>> {
239 self.command_extension.as_ref()
240 }
241
242 #[must_use]
244 pub fn pool_name(&self) -> &str {
245 &self.pool_name
246 }
247
248 pub async fn start(self) -> Result<ServerHandle, EmbedError> {
289 let Server {
290 pool_name,
291 pool,
292 cluster,
293 mut hooks,
294 stats,
295 command_extension,
296 } = self;
297
298 let mut dispatcher = ClusterDispatcher::new(cluster.clone());
299 if let Some(ext) = command_extension.as_ref() {
300 dispatcher = dispatcher.with_command_extension(ext.clone());
301 }
302 let bus = EventBus::new(64);
303 let events = Arc::new(EventManager::new(64));
304 let cancel = CancellationToken::new();
305 let snapshot_cache = Arc::new(Mutex::new(Snapshot::default()));
306
307 let datastore = hooks
308 .datastore
309 .take()
310 .expect("invariant: builder always populates datastore");
311 let seeds = hooks
312 .seeds
313 .take()
314 .expect("invariant: builder always populates seeds");
315 let metrics = hooks
316 .metrics
317 .take()
318 .unwrap_or_else(|| Box::new(LoggingMetricsSink::new(pool_name.clone())));
319
320 let transport = pool.transport.unwrap_or_default();
321 let (listen_listener, listen_addr) =
322 bind_listener(pool.listen.as_ref(), transport, &pool).await?;
323 let (dyn_listener, dyn_listen_addr) =
324 bind_listener(pool.dyn_listen.as_ref(), transport, &pool).await?;
325
326 let inner = Arc::new(ServerInner {
327 pool: cluster.clone(),
328 dispatcher,
329 stats: stats.clone(),
330 snapshot_cache: snapshot_cache.clone(),
331 bus: bus.clone(),
332 events: events.clone(),
333 datastore,
334 seeds,
335 metrics,
336 crypto: hooks.crypto,
337 listen_addr,
338 dyn_listen_addr,
339 cancel: cancel.clone(),
340 pool_name: pool_name.clone(),
341 config: Mutex::new(pool.clone()),
342 generation: AtomicU64::new(0),
343 command_extension,
344 });
345
346 if let Some(addr) = inner.dyn_listen_addr {
349 registry_register(addr, &inner);
350 }
351
352 let mut tasks: Vec<JoinHandle<()>> = Vec::new();
354 tasks.push(tokio::spawn(stats_loop(inner.clone(), pool.stats_interval)));
355 tasks.push(tokio::spawn(metrics_loop(inner.clone())));
356 if pool.enable_gossip.unwrap_or(false) {
357 tasks.push(tokio::spawn(gossip_loop(
358 inner.clone(),
359 Duration::from_millis(
360 u64::try_from(pool.gos_interval.unwrap_or(1_000)).unwrap_or(1_000),
361 ),
362 )));
363 }
364 if let (Some(listener), Some(addr)) = (listen_listener, listen_addr) {
365 tasks.push(tokio::spawn(accept_loop(
366 inner.clone(),
367 listener,
368 addr,
369 ConnRoleTag::Proxy,
370 )));
371 }
372 if let (Some(listener), Some(addr)) = (dyn_listener, dyn_listen_addr) {
373 tasks.push(tokio::spawn(accept_loop(
374 inner.clone(),
375 listener,
376 addr,
377 ConnRoleTag::DnodeProxy,
378 )));
379 }
380
381 Ok(ServerHandle {
382 inner,
383 tasks: Arc::new(Mutex::new(tasks)),
384 })
385 }
386}
387
388#[derive(Clone)]
395pub struct ServerHandle {
396 inner: Arc<ServerInner>,
397 tasks: Arc<Mutex<Vec<JoinHandle<()>>>>,
398}
399
400impl std::fmt::Debug for ServerHandle {
401 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
402 f.debug_struct("ServerHandle")
403 .field("pool", &self.inner.pool_name)
404 .field("listen", &self.inner.listen_addr)
405 .field("dyn_listen", &self.inner.dyn_listen_addr)
406 .finish_non_exhaustive()
407 }
408}
409
410impl ServerHandle {
411 #[must_use]
414 pub fn crypto_provider(&self) -> Option<&dyn CryptoProvider> {
415 self.inner.crypto.as_deref()
416 }
417
418 #[must_use]
421 pub fn command_extension(&self) -> Option<Arc<dyn crate::embed::CommandExtension>> {
422 self.inner.command_extension.as_ref().map(Arc::clone)
423 }
424
425 #[must_use]
428 pub fn listen_addr(&self) -> Option<SocketAddr> {
429 self.inner.listen_addr
430 }
431
432 #[must_use]
435 pub fn dyn_listen_addr(&self) -> Option<SocketAddr> {
436 self.inner.dyn_listen_addr
437 }
438
439 #[must_use]
460 pub fn subscribe_events(&self) -> EventStream {
461 self.inner.bus.subscribe()
462 }
463
464 #[must_use]
472 pub fn events(&self) -> Arc<EventManager> {
473 Arc::clone(&self.inner.events)
474 }
475
476 #[must_use]
478 pub fn stats(&self) -> Snapshot {
479 self.inner.stats.snapshot()
480 }
481
482 #[must_use]
497 pub fn stats_handle(&self) -> Arc<Stats> {
498 Arc::clone(&self.inner.stats)
499 }
500
501 #[must_use]
503 pub fn describe_stats(&self) -> Vec<MetricSpec> {
504 let _ = describe_stats(); let mut out: Vec<MetricSpec> = Vec::new();
506 out.extend(crate::stats::POOL_CODEC.iter().copied());
507 out.extend(crate::stats::SERVER_CODEC.iter().copied());
508 out
509 }
510
511 #[must_use]
513 pub fn peers(&self) -> Vec<PeerSnapshot> {
514 self.inner
515 .pool
516 .peers()
517 .read()
518 .iter()
519 .map(PeerSnapshot::from)
520 .collect()
521 }
522
523 #[must_use]
525 pub fn datacenters(&self) -> Vec<DatacenterSnapshot> {
526 self.inner
527 .pool
528 .datacenters()
529 .read()
530 .iter()
531 .map(DatacenterSnapshot::from)
532 .collect()
533 }
534
535 #[must_use]
537 pub fn ring(&self) -> RingSnapshot {
538 let dcs = self.inner.pool.datacenters().read();
539 let mut entries: Vec<(DynToken, u32)> = Vec::new();
540 for dc in dcs.iter() {
541 for rack in dc.racks() {
542 for c in rack.continuums() {
543 entries.push((c.token.clone(), c.peer_idx));
544 }
545 }
546 }
547 RingSnapshot {
548 entries,
549 generation: self.inner.generation.load(Ordering::Relaxed),
550 }
551 }
552
553 pub async fn inject_request(&self, req: Msg) -> Result<Msg, EmbedError> {
565 let key: Vec<u8> = req
569 .keys()
570 .first()
571 .map(|kp| kp.tag_bytes().to_vec())
572 .unwrap_or_default();
573 let plan = self.inner.dispatcher.plan(&req, &key);
574 match plan {
575 DispatchPlan::Drop => {
576 let mut rsp = Msg::new(req.id(), crate::msg::MsgType::Unknown, false);
579 rsp.set_parent_id(req.id());
580 Ok(rsp)
581 }
582 DispatchPlan::NoTargets => Err(EmbedError::Inject(
583 "cluster has no quorum-eligible targets".into(),
584 )),
585 DispatchPlan::LocalDatastore => self.inner.dispatch_local(req).await,
586 DispatchPlan::Replicas { targets, .. } => {
587 self.inner.stats.pool_incr_by(PoolField::ForwardError, 0); self.inner.stats.server_incr(ServerField::ReadRequests);
589 let resolved: Vec<(bool, Option<SocketAddr>)> = {
593 let peers = self.inner.pool.peers().read();
594 targets
595 .iter()
596 .map(|t| {
597 let peer = peers.get(t.peer_idx as usize);
598 let is_local = peer.is_some_and(crate::cluster::peer::Peer::is_local);
599 let addr = peer.and_then(|p| p.endpoint().pname().parse().ok());
600 (is_local, addr)
601 })
602 .collect()
603 };
604 let mut last_err: Option<EmbedError> = None;
605 for (is_local, addr) in resolved {
606 if is_local {
607 return self.inner.dispatch_local(req).await;
608 }
609 if let Some(addr) = addr {
610 if let Some(remote) = registry_lookup(addr) {
611 let mut forwarded = Msg::new(req.id(), req.ty(), true);
612 forwarded.set_parent_id(req.parent_id());
613 match remote.datastore.dispatch(forwarded).await {
614 Ok(rsp) => return Ok(rsp),
615 Err(e) => last_err = Some(EmbedError::Inject(e.to_string())),
616 }
617 }
618 }
619 }
620 if let Some(e) = last_err {
621 return Err(e);
622 }
623 self.inner.dispatch_local(req).await
627 }
628 }
629 }
630
631 pub async fn reload(&self, mut cfg: Config) -> Result<(), EmbedError> {
639 cfg.finalize();
640 cfg.validate()?;
641 let new_pool = cfg.pool().clone();
642 *self.inner.config.lock() = new_pool;
647 let gen_id = next_generation();
648 self.inner.generation.store(gen_id, Ordering::Relaxed);
649 self.inner
650 .bus
651 .send(ServerEvent::ConfigReloaded { generation: gen_id });
652 tokio::task::yield_now().await;
655 Ok(())
656 }
657
658 pub async fn shutdown(&self) -> Result<(), EmbedError> {
666 self.inner.cancel.cancel();
667 if let Some(addr) = self.inner.dyn_listen_addr {
668 registry_remove(addr);
669 }
670 let drained: Vec<JoinHandle<()>> = std::mem::take(&mut *self.tasks.lock());
671 for t in drained {
672 let _ = t.await;
674 }
675 Ok(())
676 }
677
678 pub async fn join(&self) {
714 let drained: Vec<JoinHandle<()>> = std::mem::take(&mut *self.tasks.lock());
715 for t in drained {
716 let _ = t.await;
717 }
718 }
719}
720
721fn build_local_peer(pool: &ConfPool, cfg: &PoolConfig) -> Peer {
724 let dyn_listen = pool.dyn_listen.as_ref().map_or_else(
725 || ("127.0.0.1".to_string(), 0u16),
726 |l| (l.name().to_string(), l.port()),
727 );
728 let tokens = pool
729 .tokens
730 .as_ref()
731 .map(|tl| {
732 tl.components()
733 .iter()
734 .map(|c| DynToken::from_u32(c.digits.parse::<u32>().unwrap_or(0)))
735 .collect::<Vec<_>>()
736 })
737 .unwrap_or_default();
738 let mut peer = Peer::new(
739 0,
740 PeerEndpoint::tcp(dyn_listen.0, dyn_listen.1),
741 cfg.rack.clone(),
742 cfg.dc.clone(),
743 if tokens.is_empty() {
744 vec![DynToken::from_u32(0)]
745 } else {
746 tokens
747 },
748 true,
749 true,
750 false,
751 );
752 let now_secs = std::time::SystemTime::now()
753 .duration_since(std::time::UNIX_EPOCH)
754 .map_or(0, |d| d.as_secs());
755 peer.set_state(PeerState::Normal, now_secs);
756 peer
757}
758
759fn peers_from_seeds(cfg: &PoolConfig, seeds: &[ConfDynSeed], start_idx: u32) -> Vec<Peer> {
760 let now_secs = std::time::SystemTime::now()
761 .duration_since(std::time::UNIX_EPOCH)
762 .map_or(0, |d| d.as_secs());
763 seeds
764 .iter()
765 .enumerate()
766 .map(|(i, s)| {
767 let tokens: Vec<DynToken> = s
768 .tokens()
769 .components()
770 .iter()
771 .map(|c| DynToken::from_u32(c.digits.parse::<u32>().unwrap_or(0)))
772 .collect();
773 let idx_off = u32::try_from(i).unwrap_or(0);
774 let mut p = Peer::new(
775 start_idx + idx_off,
776 PeerEndpoint::tcp(s.host().to_string(), s.port()),
777 s.rack().to_string(),
778 s.dc().to_string(),
779 if tokens.is_empty() {
780 vec![DynToken::from_u32(0)]
781 } else {
782 tokens
783 },
784 false,
785 s.dc() == cfg.dc,
786 false,
787 );
788 p.set_state(PeerState::Normal, now_secs);
789 p
790 })
791 .collect()
792}
793
794async fn bind_listener(
797 listen: Option<&crate::conf::ConfListen>,
798 transport: crate::conf::Transport,
799 #[cfg_attr(not(feature = "quic"), allow(unused_variables))] pool: &crate::conf::ConfPool,
800) -> Result<(Option<TcpListener>, Option<SocketAddr>), EmbedError> {
801 let Some(l) = listen else {
802 return Ok((None, None));
803 };
804 let host = l.name();
805 let port = l.port();
806 if host.is_empty() {
807 return Ok((None, None));
808 }
809 let addr_str = format!("{host}:{port}");
810 let Ok(_addr) = addr_str.parse::<SocketAddr>() else {
811 return Ok((None, None));
812 };
813 match transport {
814 crate::conf::Transport::Tcp => {
815 let listener = TcpListener::bind(&addr_str).await?;
816 let local = listener.local_addr()?;
817 Ok((Some(listener), Some(local)))
818 }
819 #[cfg(feature = "quic")]
820 crate::conf::Transport::Quic => {
821 let cert = pool
831 .quic_cert_file
832 .as_ref()
833 .and_then(|p| p.to_str().map(str::to_owned))
834 .ok_or_else(|| {
835 EmbedError::Build(
836 "transport: quic requires quic_cert_file (UTF-8 path)".to_string(),
837 )
838 })?;
839 let key = pool
840 .quic_key_file
841 .as_ref()
842 .and_then(|p| p.to_str().map(str::to_owned))
843 .ok_or_else(|| {
844 EmbedError::Build(
845 "transport: quic requires quic_key_file (UTF-8 path)".to_string(),
846 )
847 })?;
848 let cfg = crate::net::QuicConfig::server_with_cert_paths(cert, key);
849 let parsed: SocketAddr = addr_str.parse().map_err(|e: std::net::AddrParseError| {
850 EmbedError::Build(format!("invalid quic listen address {addr_str}: {e}"))
851 })?;
852 let listener = crate::net::QuicListener::bind(parsed, cfg).await?;
853 let local = listener.local_addr();
854 drop(listener);
861 Ok((None, Some(local)))
862 }
863 #[cfg(not(feature = "quic"))]
864 crate::conf::Transport::Quic => Err(EmbedError::Build(
865 "transport: quic requires the engine's `quic` Cargo feature".to_string(),
866 )),
867 }
868}
869
870async fn accept_loop(
873 inner: Arc<ServerInner>,
874 listener: TcpListener,
875 addr: SocketAddr,
876 role: ConnRoleTag,
877) {
878 loop {
879 tokio::select! {
880 biased;
881 () = inner.cancel.cancelled() => return,
882 res = listener.accept() => {
883 let Ok((sock, peer)) = res else { return };
884 let conn_id = next_conn_id();
885 inner.bus.send(ServerEvent::ConnectionAccepted {
886 conn_id,
887 role,
888 local_addr: Some(addr),
889 });
890 tracing::warn!(
900 listen = %addr,
901 peer = %peer,
902 role = ?role,
903 conn_id,
904 "embedded listen_addr accepted a connection; embedded mode does not \
905 forward to the dispatcher; use ServerHandle::inject_request instead. \
906 Closing connection."
907 );
908 let bus = inner.bus.clone();
909 let cancel = inner.cancel.clone();
910 tokio::spawn(async move {
911 let _ = sock; let close_reason = if cancel.is_cancelled() {
913 CloseReason::LocalClose
914 } else {
915 CloseReason::PeerEof
916 };
917 bus.send(ServerEvent::ConnectionClosed { conn_id, reason: close_reason });
918 });
919 }
920 }
921 }
922}
923
924async fn stats_loop(inner: Arc<ServerInner>, interval_ms: Option<i64>) {
925 let interval =
926 Duration::from_millis(u64::try_from(interval_ms.unwrap_or(1_000)).unwrap_or(1_000));
927 let mut ticker = tokio::time::interval(interval);
928 ticker.set_missed_tick_behavior(MissedTickBehavior::Delay);
929 loop {
930 tokio::select! {
931 biased;
932 () = inner.cancel.cancelled() => return,
933 _ = ticker.tick() => {
934 let snap = inner.stats.snapshot();
935 *inner.snapshot_cache.lock() = snap;
936 }
937 }
938 }
939}
940
941async fn metrics_loop(inner: Arc<ServerInner>) {
942 let interval = inner.metrics.flush_interval();
943 let mut ticker = tokio::time::interval(interval.max(Duration::from_millis(50)));
944 ticker.set_missed_tick_behavior(MissedTickBehavior::Delay);
945 loop {
946 tokio::select! {
947 biased;
948 () = inner.cancel.cancelled() => return,
949 _ = ticker.tick() => {
950 let snap = inner.snapshot_cache.lock().clone();
951 let _ = inner.metrics.emit(&snap).await;
952 }
953 }
954 }
955}
956
957async fn gossip_loop(inner: Arc<ServerInner>, interval: Duration) {
958 let mut ticker = tokio::time::interval(interval.max(Duration::from_millis(20)));
959 ticker.set_missed_tick_behavior(MissedTickBehavior::Delay);
960 let mut round: u64 = 0;
961 let mut known: std::collections::HashSet<(String, u16)> = std::collections::HashSet::new();
962 {
963 let peers = inner.pool.peers().read();
964 for p in peers.iter() {
965 known.insert((p.endpoint().host().to_string(), p.endpoint().port()));
966 }
967 }
968 loop {
969 tokio::select! {
970 biased;
971 () = inner.cancel.cancelled() => return,
972 _ = ticker.tick() => {
973 let round_started = std::time::Instant::now();
974 let round_ts = std::time::SystemTime::now();
975 round += 1;
976 let seeds = inner.seeds.fetch().unwrap_or_default();
977 let mut added: u32 = 0;
978 {
979 let mut peers = inner.pool.peers().write();
980 let now_secs = std::time::SystemTime::now()
981 .duration_since(std::time::UNIX_EPOCH)
982 .map_or(0, |d| d.as_secs());
983 let cfg = inner.pool.config().clone();
984 for seed in &seeds {
985 let key = (seed.host().to_string(), seed.port());
986 if known.contains(&key) {
987 continue;
988 }
989 let next_idx = u32::try_from(peers.len()).unwrap_or(u32::MAX);
990 let tokens: Vec<DynToken> = seed
991 .tokens()
992 .components()
993 .iter()
994 .map(|c| DynToken::from_u32(c.digits.parse::<u32>().unwrap_or(0)))
995 .collect();
996 let mut p = Peer::new(
997 next_idx,
998 PeerEndpoint::tcp(seed.host().to_string(), seed.port()),
999 seed.rack().to_string(),
1000 seed.dc().to_string(),
1001 if tokens.is_empty() {
1002 vec![DynToken::from_u32(0)]
1003 } else {
1004 tokens
1005 },
1006 false,
1007 seed.dc() == cfg.dc,
1008 false,
1009 );
1010 p.set_state(PeerState::Normal, now_secs);
1011 peers.push(p);
1012 known.insert(key);
1013 added += 1;
1014 }
1015 }
1016 if added > 0 {
1017 rebuild_topology(&inner.pool);
1021 let peers_now = inner.pool.peers().read();
1022 for (idx, p) in peers_now.iter().enumerate().rev().take(added as usize) {
1023 let _ = idx;
1024 inner.bus.send(ServerEvent::PeerUp(p.idx()));
1025 inner.events.publish(crate::events::ClusterEvent::PeerUp {
1026 peer_id: p.idx(),
1027 dc: p.dc().to_string(),
1028 ts: round_ts,
1029 });
1030 }
1031 inner.events.publish(crate::events::ClusterEvent::RingChanged {
1032 tag: "seed-discovery".to_string(),
1033 ts: round_ts,
1034 });
1035 }
1036 let count = u32::try_from(inner.pool.peers().read().len()).unwrap_or(u32::MAX);
1037 inner.bus.send(ServerEvent::GossipRound { round, peers: count });
1038 inner.events.publish(crate::events::ClusterEvent::GossipRoundComplete {
1039 duration: round_started.elapsed(),
1040 peers_seen: count as usize,
1041 ts: round_ts,
1042 });
1043 inner.stats.pool_incr(PoolField::StatsCount);
1044 }
1045 }
1046 }
1047}
1048
1049fn rebuild_topology(pool: &Arc<ServerPool>) {
1050 use crate::cluster::Datacenter;
1051 let peers = pool.peers().read();
1052 let mut new_dcs: Vec<Datacenter> = Vec::new();
1053 for p in peers.iter() {
1054 let idx = if let Some(i) = new_dcs.iter().position(|d| d.name() == p.dc()) {
1055 i
1056 } else {
1057 new_dcs.push(Datacenter::new(p.dc().to_string()));
1058 new_dcs.len() - 1
1059 };
1060 new_dcs[idx].upsert_rack(p.rack().to_string());
1061 }
1062 drop(peers);
1063 {
1064 let mut dcs = pool.datacenters().write();
1065 *dcs = new_dcs;
1066 }
1067 pool.rebuild_ring();
1068 pool.preselect_remote_racks();
1069}
1070
1071