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::sync::mpsc;
23use tokio::task::JoinHandle;
24use tokio::time::{Duration, MissedTickBehavior};
25use tokio_util::sync::CancellationToken;
26
27use crate::cluster::dispatch::{ClusterDispatcher, DispatchPlan};
28use crate::cluster::peer::{Peer, PeerEndpoint, PeerState};
29use crate::cluster::pool::{PoolConfig, ServerPool};
30use crate::conf::{ConfDynSeed, ConfPool, Config, DataStore};
31use crate::embed::error::EmbedError;
32use crate::embed::events::{CloseReason, ConnRoleTag, EventBus, EventStream, ServerEvent};
33use crate::embed::hooks::{
34 CryptoProvider, Datastore, LoggingMetricsSink, MetricsSink, SeedsProvider,
35};
36use crate::embed::snapshots::{DatacenterSnapshot, PeerSnapshot, RingSnapshot};
37use crate::events::EventManager;
38use crate::hashkit::DynToken;
39use crate::io::reactor::{ConnRole, TcpTransport};
40use crate::msg::Msg;
41use crate::net::client::client_loop;
42use crate::net::{ClientHandler, Conn};
43use crate::stats::{
44 describe_stats, MetricSpec, PoolField, PoolStats, ServerField, ServerStats, ServiceInfo,
45 Snapshot, Stats,
46};
47
48#[non_exhaustive]
56pub struct ServerHooks {
57 pub(crate) datastore: Option<Box<dyn Datastore>>,
58 pub(crate) seeds: Option<Box<dyn SeedsProvider>>,
59 pub(crate) crypto: Option<Box<dyn CryptoProvider>>,
60 pub(crate) metrics: Option<Box<dyn MetricsSink>>,
61}
62
63impl ServerHooks {
64 #[must_use]
66 pub fn datastore(&self) -> Option<&dyn Datastore> {
67 self.datastore.as_deref()
68 }
69
70 #[must_use]
72 pub fn seeds(&self) -> Option<&dyn SeedsProvider> {
73 self.seeds.as_deref()
74 }
75
76 #[must_use]
78 pub fn crypto(&self) -> Option<&dyn CryptoProvider> {
79 self.crypto.as_deref()
80 }
81
82 #[must_use]
84 pub fn metrics(&self) -> Option<&dyn MetricsSink> {
85 self.metrics.as_deref()
86 }
87}
88
89impl std::fmt::Debug for ServerHooks {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 f.debug_struct("ServerHooks")
92 .field("datastore", &self.datastore.is_some())
93 .field("seeds", &self.seeds.is_some())
94 .field("crypto", &self.crypto.is_some())
95 .field("metrics", &self.metrics.is_some())
96 .finish()
97 }
98}
99
100fn registry() -> &'static Mutex<HashMap<SocketAddr, Weak<ServerInner>>> {
112 static R: OnceLock<Mutex<HashMap<SocketAddr, Weak<ServerInner>>>> = OnceLock::new();
113 R.get_or_init(|| Mutex::new(HashMap::new()))
114}
115
116fn registry_register(addr: SocketAddr, server: &Arc<ServerInner>) {
117 registry().lock().insert(addr, Arc::downgrade(server));
118}
119
120fn registry_remove(addr: SocketAddr) {
121 registry().lock().remove(&addr);
122}
123
124fn registry_lookup(addr: SocketAddr) -> Option<Arc<ServerInner>> {
125 registry().lock().get(&addr).and_then(Weak::upgrade)
126}
127
128fn next_generation() -> u64 {
133 static G: AtomicU64 = AtomicU64::new(0);
134 G.fetch_add(1, Ordering::Relaxed)
135}
136
137fn next_conn_id() -> u64 {
139 static C: AtomicU64 = AtomicU64::new(0);
140 C.fetch_add(1, Ordering::Relaxed)
141}
142
143pub(crate) struct ServerInner {
145 pool: Arc<ServerPool>,
146 dispatcher: Arc<ClusterDispatcher>,
147 client_proto: DataStore,
148 stats: Arc<Stats>,
149 snapshot_cache: Arc<Mutex<Snapshot>>,
150 bus: EventBus,
151 events: Arc<EventManager>,
152 datastore: Arc<dyn Datastore>,
153 seeds: Box<dyn SeedsProvider>,
154 metrics: Box<dyn MetricsSink>,
155 crypto: Option<Box<dyn CryptoProvider>>,
156 listen_addr: Option<SocketAddr>,
157 dyn_listen_addr: Option<SocketAddr>,
158 cancel: CancellationToken,
159 pool_name: String,
160 config: Mutex<ConfPool>,
161 generation: AtomicU64,
162 command_extension: Option<Arc<dyn crate::embed::CommandExtension>>,
163}
164
165impl std::fmt::Debug for ServerInner {
166 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167 f.debug_struct("ServerInner")
168 .field("pool_name", &self.pool_name)
169 .field("listen", &self.listen_addr)
170 .field("dyn_listen", &self.dyn_listen_addr)
171 .finish_non_exhaustive()
172 }
173}
174
175impl ServerInner {
176 fn dispatch_local(&self, req: Msg) -> super::hooks::BoxFuture<'_, Result<Msg, EmbedError>> {
177 let fut = self.datastore.dispatch(req);
178 Box::pin(async move {
179 let rsp = fut.await.map_err(|e| EmbedError::Inject(e.to_string()))?;
180 Ok(rsp)
181 })
182 }
183}
184
185#[derive(Debug)]
192pub struct Server {
193 pool_name: String,
194 pool: ConfPool,
195 cluster: Arc<ServerPool>,
196 hooks: ServerHooks,
197 stats: Arc<Stats>,
198 command_extension: Option<Arc<dyn crate::embed::CommandExtension>>,
199}
200
201impl Server {
202 pub(crate) fn from_pool(
204 pool_name: String,
205 pool: ConfPool,
206 hooks: ServerHooks,
207 command_extension: Option<Arc<dyn crate::embed::CommandExtension>>,
208 ) -> Self {
209 let pool_cfg = PoolConfig::from_conf(&pool_name, &pool);
210 let local_peer = build_local_peer(&pool, &pool_cfg);
211 let mut peers = vec![local_peer];
212 if let Some(seeds) = pool.dyn_seeds.as_ref() {
213 let start = u32::try_from(peers.len()).unwrap_or(0);
214 peers.extend(peers_from_seeds(&pool_cfg, seeds, start));
215 }
216 let server_pool_arc = Arc::new(ServerPool::new(pool_cfg.clone(), peers));
217 server_pool_arc.preselect_remote_racks();
218
219 let stats = Arc::new(Stats::new(
220 ServiceInfo {
221 source: pool_cfg.name.clone(),
222 version: env!("CARGO_PKG_VERSION").to_string(),
223 rack: pool_cfg.rack.clone(),
224 dc: pool_cfg.dc.clone(),
225 },
226 PoolStats::new(&pool_cfg.name),
227 ServerStats::new("backend"),
228 ));
229
230 Self {
231 pool_name,
232 pool,
233 cluster: server_pool_arc,
234 hooks,
235 stats,
236 command_extension,
237 }
238 }
239
240 #[must_use]
243 pub fn command_extension(&self) -> Option<&Arc<dyn crate::embed::CommandExtension>> {
244 self.command_extension.as_ref()
245 }
246
247 #[must_use]
249 pub fn pool_name(&self) -> &str {
250 &self.pool_name
251 }
252
253 pub async fn start(self) -> Result<ServerHandle, EmbedError> {
297 let Server {
298 pool_name,
299 pool,
300 cluster,
301 mut hooks,
302 stats,
303 command_extension,
304 } = self;
305
306 let datastore: Arc<dyn Datastore> = hooks
307 .datastore
308 .take()
309 .expect("invariant: builder always populates datastore")
310 .into();
311
312 let mut dispatcher = ClusterDispatcher::new(cluster.clone());
313 if let Some(ext) = command_extension.as_ref() {
314 dispatcher = dispatcher.with_command_extension(ext.clone());
315 }
316 dispatcher = dispatcher.with_local_datastore(Arc::clone(&datastore));
321 let dispatcher = Arc::new(dispatcher);
322 let client_proto = pool
327 .data_store
328 .and_then(|n| DataStore::from_int(n).ok())
329 .unwrap_or(DataStore::Valkey);
330 let bus = EventBus::new(64);
331 let events = Arc::new(EventManager::new(64));
332 let cancel = CancellationToken::new();
333 let snapshot_cache = Arc::new(Mutex::new(Snapshot::default()));
334
335 let seeds = hooks
336 .seeds
337 .take()
338 .expect("invariant: builder always populates seeds");
339 let metrics = hooks
340 .metrics
341 .take()
342 .unwrap_or_else(|| Box::new(LoggingMetricsSink::new(pool_name.clone())));
343
344 let transport = pool.transport.unwrap_or_default();
345 let (listen_listener, listen_addr) =
346 bind_listener(pool.listen.as_ref(), transport, &pool).await?;
347 let (dyn_listener, dyn_listen_addr) =
348 bind_listener(pool.dyn_listen.as_ref(), transport, &pool).await?;
349
350 let inner = Arc::new(ServerInner {
351 pool: cluster.clone(),
352 dispatcher,
353 client_proto,
354 stats: stats.clone(),
355 snapshot_cache: snapshot_cache.clone(),
356 bus: bus.clone(),
357 events: events.clone(),
358 datastore,
359 seeds,
360 metrics,
361 crypto: hooks.crypto,
362 listen_addr,
363 dyn_listen_addr,
364 cancel: cancel.clone(),
365 pool_name: pool_name.clone(),
366 config: Mutex::new(pool.clone()),
367 generation: AtomicU64::new(0),
368 command_extension,
369 });
370
371 if let Some(addr) = inner.dyn_listen_addr {
374 registry_register(addr, &inner);
375 }
376
377 let mut tasks: Vec<JoinHandle<()>> = Vec::new();
379 tasks.push(tokio::spawn(stats_loop(inner.clone(), pool.stats_interval)));
380 tasks.push(tokio::spawn(metrics_loop(inner.clone())));
381 if pool.enable_gossip.unwrap_or(false) {
382 tasks.push(tokio::spawn(gossip_loop(
383 inner.clone(),
384 Duration::from_millis(
385 u64::try_from(pool.gos_interval.unwrap_or(1_000)).unwrap_or(1_000),
386 ),
387 )));
388 }
389 if let (Some(listener), Some(addr)) = (listen_listener, listen_addr) {
390 tasks.push(tokio::spawn(accept_loop(
391 inner.clone(),
392 listener,
393 addr,
394 ConnRoleTag::Proxy,
395 )));
396 }
397 if let (Some(listener), Some(addr)) = (dyn_listener, dyn_listen_addr) {
398 tasks.push(tokio::spawn(accept_loop(
399 inner.clone(),
400 listener,
401 addr,
402 ConnRoleTag::DnodeProxy,
403 )));
404 }
405
406 Ok(ServerHandle {
407 inner,
408 tasks: Arc::new(Mutex::new(tasks)),
409 })
410 }
411}
412
413#[derive(Clone)]
420pub struct ServerHandle {
421 inner: Arc<ServerInner>,
422 tasks: Arc<Mutex<Vec<JoinHandle<()>>>>,
423}
424
425impl std::fmt::Debug for ServerHandle {
426 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
427 f.debug_struct("ServerHandle")
428 .field("pool", &self.inner.pool_name)
429 .field("listen", &self.inner.listen_addr)
430 .field("dyn_listen", &self.inner.dyn_listen_addr)
431 .finish_non_exhaustive()
432 }
433}
434
435impl ServerHandle {
436 #[must_use]
439 pub fn crypto_provider(&self) -> Option<&dyn CryptoProvider> {
440 self.inner.crypto.as_deref()
441 }
442
443 #[must_use]
446 pub fn command_extension(&self) -> Option<Arc<dyn crate::embed::CommandExtension>> {
447 self.inner.command_extension.as_ref().map(Arc::clone)
448 }
449
450 #[must_use]
453 pub fn listen_addr(&self) -> Option<SocketAddr> {
454 self.inner.listen_addr
455 }
456
457 #[must_use]
460 pub fn dyn_listen_addr(&self) -> Option<SocketAddr> {
461 self.inner.dyn_listen_addr
462 }
463
464 #[must_use]
485 pub fn subscribe_events(&self) -> EventStream {
486 self.inner.bus.subscribe()
487 }
488
489 #[must_use]
497 pub fn events(&self) -> Arc<EventManager> {
498 Arc::clone(&self.inner.events)
499 }
500
501 #[must_use]
503 pub fn stats(&self) -> Snapshot {
504 self.inner.stats.snapshot()
505 }
506
507 #[must_use]
522 pub fn stats_handle(&self) -> Arc<Stats> {
523 Arc::clone(&self.inner.stats)
524 }
525
526 #[must_use]
528 pub fn describe_stats(&self) -> Vec<MetricSpec> {
529 let _ = describe_stats(); let mut out: Vec<MetricSpec> = Vec::new();
531 out.extend(crate::stats::POOL_CODEC.iter().copied());
532 out.extend(crate::stats::SERVER_CODEC.iter().copied());
533 out
534 }
535
536 #[must_use]
538 pub fn peers(&self) -> Vec<PeerSnapshot> {
539 self.inner
540 .pool
541 .peers()
542 .read()
543 .iter()
544 .map(PeerSnapshot::from)
545 .collect()
546 }
547
548 #[must_use]
550 pub fn datacenters(&self) -> Vec<DatacenterSnapshot> {
551 self.inner
552 .pool
553 .datacenters()
554 .read()
555 .iter()
556 .map(DatacenterSnapshot::from)
557 .collect()
558 }
559
560 #[must_use]
562 pub fn ring(&self) -> RingSnapshot {
563 let dcs = self.inner.pool.datacenters().read();
564 let mut entries: Vec<(DynToken, u32)> = Vec::new();
565 for dc in dcs.iter() {
566 for rack in dc.racks() {
567 for c in rack.continuums() {
568 entries.push((c.token.clone(), c.peer_idx));
569 }
570 }
571 }
572 RingSnapshot {
573 entries,
574 generation: self.inner.generation.load(Ordering::Relaxed),
575 }
576 }
577
578 pub async fn inject_request(&self, req: Msg) -> Result<Msg, EmbedError> {
590 let key: Vec<u8> = req
594 .keys()
595 .first()
596 .map(|kp| kp.tag_bytes().to_vec())
597 .unwrap_or_default();
598 let plan = self.inner.dispatcher.plan(&req, &key);
599 match plan {
600 DispatchPlan::Drop => {
601 let mut rsp = Msg::new(req.id(), crate::msg::MsgType::Unknown, false);
604 rsp.set_parent_id(req.id());
605 Ok(rsp)
606 }
607 DispatchPlan::NoTargets => Err(EmbedError::Inject(
608 "cluster has no quorum-eligible targets".into(),
609 )),
610 DispatchPlan::LocalDatastore => self.inner.dispatch_local(req).await,
611 DispatchPlan::Replicas { targets, .. } => {
612 self.inner.stats.pool_incr_by(PoolField::ForwardError, 0); self.inner.stats.server_incr(ServerField::ReadRequests);
614 let resolved: Vec<(bool, Option<SocketAddr>)> = {
618 let peers = self.inner.pool.peers().read();
619 targets
620 .iter()
621 .map(|t| {
622 let peer = peers.get(t.peer_idx as usize);
623 let is_local = peer.is_some_and(crate::cluster::peer::Peer::is_local);
624 let addr = peer.and_then(|p| p.endpoint().pname().parse().ok());
625 (is_local, addr)
626 })
627 .collect()
628 };
629 let mut last_err: Option<EmbedError> = None;
630 for (is_local, addr) in resolved {
631 if is_local {
632 return self.inner.dispatch_local(req).await;
633 }
634 if let Some(addr) = addr {
635 if let Some(remote) = registry_lookup(addr) {
636 let mut forwarded = Msg::new(req.id(), req.ty(), true);
637 forwarded.set_parent_id(req.parent_id());
638 match remote.datastore.dispatch(forwarded).await {
639 Ok(rsp) => return Ok(rsp),
640 Err(e) => last_err = Some(EmbedError::Inject(e.to_string())),
641 }
642 }
643 }
644 }
645 if let Some(e) = last_err {
646 return Err(e);
647 }
648 self.inner.dispatch_local(req).await
652 }
653 }
654 }
655
656 pub async fn reload(&self, mut cfg: Config) -> Result<(), EmbedError> {
664 cfg.finalize();
665 cfg.validate()?;
666 let new_pool = cfg.pool().clone();
667 *self.inner.config.lock() = new_pool;
672 let gen_id = next_generation();
673 self.inner.generation.store(gen_id, Ordering::Relaxed);
674 self.inner
675 .bus
676 .send(ServerEvent::ConfigReloaded { generation: gen_id });
677 tokio::task::yield_now().await;
680 Ok(())
681 }
682
683 pub async fn shutdown(&self) -> Result<(), EmbedError> {
691 self.inner.cancel.cancel();
692 if let Some(addr) = self.inner.dyn_listen_addr {
693 registry_remove(addr);
694 }
695 let drained: Vec<JoinHandle<()>> = std::mem::take(&mut *self.tasks.lock());
696 for t in drained {
697 let _ = t.await;
699 }
700 Ok(())
701 }
702
703 pub async fn join(&self) {
739 let drained: Vec<JoinHandle<()>> = std::mem::take(&mut *self.tasks.lock());
740 for t in drained {
741 let _ = t.await;
742 }
743 }
744}
745
746fn build_local_peer(pool: &ConfPool, cfg: &PoolConfig) -> Peer {
749 let dyn_listen = pool.dyn_listen.as_ref().map_or_else(
750 || ("127.0.0.1".to_string(), 0u16),
751 |l| (l.name().to_string(), l.port()),
752 );
753 let tokens = pool
754 .tokens
755 .as_ref()
756 .map(|tl| {
757 tl.components()
758 .iter()
759 .map(|c| DynToken::from_u32(c.digits.parse::<u32>().unwrap_or(0)))
760 .collect::<Vec<_>>()
761 })
762 .unwrap_or_default();
763 let mut peer = Peer::new(
764 0,
765 PeerEndpoint::tcp(dyn_listen.0, dyn_listen.1),
766 cfg.rack.clone(),
767 cfg.dc.clone(),
768 if tokens.is_empty() {
769 vec![DynToken::from_u32(0)]
770 } else {
771 tokens
772 },
773 true,
774 true,
775 false,
776 );
777 let now_secs = std::time::SystemTime::now()
778 .duration_since(std::time::UNIX_EPOCH)
779 .map_or(0, |d| d.as_secs());
780 peer.set_state(PeerState::Normal, now_secs);
781 peer
782}
783
784fn peers_from_seeds(cfg: &PoolConfig, seeds: &[ConfDynSeed], start_idx: u32) -> Vec<Peer> {
785 let now_secs = std::time::SystemTime::now()
786 .duration_since(std::time::UNIX_EPOCH)
787 .map_or(0, |d| d.as_secs());
788 seeds
789 .iter()
790 .enumerate()
791 .map(|(i, s)| {
792 let tokens: Vec<DynToken> = s
793 .tokens()
794 .components()
795 .iter()
796 .map(|c| DynToken::from_u32(c.digits.parse::<u32>().unwrap_or(0)))
797 .collect();
798 let idx_off = u32::try_from(i).unwrap_or(0);
799 let mut p = Peer::new(
800 start_idx + idx_off,
801 PeerEndpoint::tcp(s.host().to_string(), s.port()),
802 s.rack().to_string(),
803 s.dc().to_string(),
804 if tokens.is_empty() {
805 vec![DynToken::from_u32(0)]
806 } else {
807 tokens
808 },
809 false,
810 s.dc() == cfg.dc,
811 false,
812 );
813 p.set_state(PeerState::Normal, now_secs);
814 p
815 })
816 .collect()
817}
818
819async fn bind_listener(
822 listen: Option<&crate::conf::ConfListen>,
823 transport: crate::conf::Transport,
824 #[cfg_attr(not(feature = "quic"), allow(unused_variables))] pool: &crate::conf::ConfPool,
825) -> Result<(Option<TcpListener>, Option<SocketAddr>), EmbedError> {
826 let Some(l) = listen else {
827 return Ok((None, None));
828 };
829 let host = l.name();
830 let port = l.port();
831 if host.is_empty() {
832 return Ok((None, None));
833 }
834 let addr_str = format!("{host}:{port}");
835 let Ok(_addr) = addr_str.parse::<SocketAddr>() else {
836 return Ok((None, None));
837 };
838 match transport {
839 crate::conf::Transport::Tcp => {
840 let listener = TcpListener::bind(&addr_str).await?;
841 let local = listener.local_addr()?;
842 Ok((Some(listener), Some(local)))
843 }
844 #[cfg(feature = "quic")]
845 crate::conf::Transport::Quic => {
846 let cert = pool
856 .quic_cert_file
857 .as_ref()
858 .and_then(|p| p.to_str().map(str::to_owned))
859 .ok_or_else(|| {
860 EmbedError::Build(
861 "transport: quic requires quic_cert_file (UTF-8 path)".to_string(),
862 )
863 })?;
864 let key = pool
865 .quic_key_file
866 .as_ref()
867 .and_then(|p| p.to_str().map(str::to_owned))
868 .ok_or_else(|| {
869 EmbedError::Build(
870 "transport: quic requires quic_key_file (UTF-8 path)".to_string(),
871 )
872 })?;
873 let cfg = crate::net::QuicConfig::server_with_cert_paths(cert, key);
874 let parsed: SocketAddr = addr_str.parse().map_err(|e: std::net::AddrParseError| {
875 EmbedError::Build(format!("invalid quic listen address {addr_str}: {e}"))
876 })?;
877 let listener = crate::net::QuicListener::bind(parsed, cfg).await?;
878 let local = listener.local_addr();
879 drop(listener);
886 Ok((None, Some(local)))
887 }
888 #[cfg(not(feature = "quic"))]
889 crate::conf::Transport::Quic => Err(EmbedError::Build(
890 "transport: quic requires the engine's `quic` Cargo feature".to_string(),
891 )),
892 }
893}
894
895async fn accept_loop(
898 inner: Arc<ServerInner>,
899 listener: TcpListener,
900 addr: SocketAddr,
901 role: ConnRoleTag,
902) {
903 loop {
904 tokio::select! {
905 biased;
906 () = inner.cancel.cancelled() => return,
907 res = listener.accept() => {
908 let Ok((sock, peer)) = res else { return };
909 let conn_id = next_conn_id();
910 inner.bus.send(ServerEvent::ConnectionAccepted {
911 conn_id,
912 role,
913 local_addr: Some(addr),
914 });
915 if matches!(role, ConnRoleTag::Proxy) {
916 let _ = sock.set_nodelay(true);
927 let dispatcher: Arc<dyn crate::net::dispatcher::Dispatcher> =
928 inner.dispatcher.clone();
929 let client_proto = inner.client_proto;
930 let bus = inner.bus.clone();
931 tokio::spawn(async move {
932 let transport = Box::new(TcpTransport::new(sock, ConnRole::Client));
933 let conn = Conn::new(transport, ConnRole::Client);
934 let (tx, rx) = mpsc::channel(64);
935 let handler = ClientHandler::new(dispatcher, tx, client_proto);
936 let outcome = client_loop(conn, handler, rx).await;
937 let close_reason = match outcome {
938 Ok(()) => CloseReason::PeerEof,
939 Err(_) => CloseReason::IoError,
940 };
941 bus.send(ServerEvent::ConnectionClosed {
942 conn_id,
943 reason: close_reason,
944 });
945 });
946 } else {
947 tracing::debug!(
956 listen = %addr,
957 peer = %peer,
958 role = ?role,
959 conn_id,
960 "embedded dyn_listen accepted a cross-process peer; embedded \
961 multi-node forwarding uses the in-process registry. Closing \
962 connection."
963 );
964 let bus = inner.bus.clone();
965 let cancel = inner.cancel.clone();
966 tokio::spawn(async move {
967 let _ = sock; let close_reason = if cancel.is_cancelled() {
969 CloseReason::LocalClose
970 } else {
971 CloseReason::PeerEof
972 };
973 bus.send(ServerEvent::ConnectionClosed {
974 conn_id,
975 reason: close_reason,
976 });
977 });
978 }
979 }
980 }
981 }
982}
983
984async fn stats_loop(inner: Arc<ServerInner>, interval_ms: Option<i64>) {
985 let interval =
986 Duration::from_millis(u64::try_from(interval_ms.unwrap_or(1_000)).unwrap_or(1_000));
987 let mut ticker = tokio::time::interval(interval);
988 ticker.set_missed_tick_behavior(MissedTickBehavior::Delay);
989 loop {
990 tokio::select! {
991 biased;
992 () = inner.cancel.cancelled() => return,
993 _ = ticker.tick() => {
994 let snap = inner.stats.snapshot();
995 *inner.snapshot_cache.lock() = snap;
996 }
997 }
998 }
999}
1000
1001async fn metrics_loop(inner: Arc<ServerInner>) {
1002 let interval = inner.metrics.flush_interval();
1003 let mut ticker = tokio::time::interval(interval.max(Duration::from_millis(50)));
1004 ticker.set_missed_tick_behavior(MissedTickBehavior::Delay);
1005 loop {
1006 tokio::select! {
1007 biased;
1008 () = inner.cancel.cancelled() => return,
1009 _ = ticker.tick() => {
1010 let snap = inner.snapshot_cache.lock().clone();
1011 let _ = inner.metrics.emit(&snap).await;
1012 }
1013 }
1014 }
1015}
1016
1017async fn gossip_loop(inner: Arc<ServerInner>, interval: Duration) {
1018 let mut ticker = tokio::time::interval(interval.max(Duration::from_millis(20)));
1019 ticker.set_missed_tick_behavior(MissedTickBehavior::Delay);
1020 let mut round: u64 = 0;
1021 let mut known: std::collections::HashSet<(String, u16)> = std::collections::HashSet::new();
1022 {
1023 let peers = inner.pool.peers().read();
1024 for p in peers.iter() {
1025 known.insert((p.endpoint().host().to_string(), p.endpoint().port()));
1026 }
1027 }
1028 loop {
1029 tokio::select! {
1030 biased;
1031 () = inner.cancel.cancelled() => return,
1032 _ = ticker.tick() => {
1033 let round_started = std::time::Instant::now();
1034 let round_ts = std::time::SystemTime::now();
1035 round += 1;
1036 let seeds = inner.seeds.fetch().unwrap_or_default();
1037 let mut added: u32 = 0;
1038 {
1039 let mut peers = inner.pool.peers().write();
1040 let now_secs = std::time::SystemTime::now()
1041 .duration_since(std::time::UNIX_EPOCH)
1042 .map_or(0, |d| d.as_secs());
1043 let cfg = inner.pool.config().clone();
1044 for seed in &seeds {
1045 let key = (seed.host().to_string(), seed.port());
1046 if known.contains(&key) {
1047 continue;
1048 }
1049 let next_idx = u32::try_from(peers.len()).unwrap_or(u32::MAX);
1050 let tokens: Vec<DynToken> = seed
1051 .tokens()
1052 .components()
1053 .iter()
1054 .map(|c| DynToken::from_u32(c.digits.parse::<u32>().unwrap_or(0)))
1055 .collect();
1056 let mut p = Peer::new(
1057 next_idx,
1058 PeerEndpoint::tcp(seed.host().to_string(), seed.port()),
1059 seed.rack().to_string(),
1060 seed.dc().to_string(),
1061 if tokens.is_empty() {
1062 vec![DynToken::from_u32(0)]
1063 } else {
1064 tokens
1065 },
1066 false,
1067 seed.dc() == cfg.dc,
1068 false,
1069 );
1070 p.set_state(PeerState::Normal, now_secs);
1071 peers.push(p);
1072 known.insert(key);
1073 added += 1;
1074 }
1075 }
1076 if added > 0 {
1077 rebuild_topology(&inner.pool);
1081 let peers_now = inner.pool.peers().read();
1082 for (idx, p) in peers_now.iter().enumerate().rev().take(added as usize) {
1083 let _ = idx;
1084 inner.bus.send(ServerEvent::PeerUp(p.idx()));
1085 inner.events.publish(crate::events::ClusterEvent::PeerUp {
1086 peer_id: p.idx(),
1087 dc: p.dc().to_string(),
1088 ts: round_ts,
1089 });
1090 }
1091 inner.events.publish(crate::events::ClusterEvent::RingChanged {
1092 tag: "seed-discovery".to_string(),
1093 ts: round_ts,
1094 });
1095 }
1096 let count = u32::try_from(inner.pool.peers().read().len()).unwrap_or(u32::MAX);
1097 inner.bus.send(ServerEvent::GossipRound { round, peers: count });
1098 inner.events.publish(crate::events::ClusterEvent::GossipRoundComplete {
1099 duration: round_started.elapsed(),
1100 peers_seen: count as usize,
1101 ts: round_ts,
1102 });
1103 inner.stats.pool_incr(PoolField::StatsCount);
1104 }
1105 }
1106 }
1107}
1108
1109fn rebuild_topology(pool: &Arc<ServerPool>) {
1110 use crate::cluster::Datacenter;
1111 let peers = pool.peers().read();
1112 let mut new_dcs: Vec<Datacenter> = Vec::new();
1113 for p in peers.iter() {
1114 let idx = if let Some(i) = new_dcs.iter().position(|d| d.name() == p.dc()) {
1115 i
1116 } else {
1117 new_dcs.push(Datacenter::new(p.dc().to_string()));
1118 new_dcs.len() - 1
1119 };
1120 new_dcs[idx].upsert_rack(p.rack().to_string());
1121 }
1122 drop(peers);
1123 {
1124 let mut dcs = pool.datacenters().write();
1125 *dcs = new_dcs;
1126 }
1127 pool.rebuild_ring();
1128 pool.preselect_remote_racks();
1129}
1130
1131