1use std::sync::Arc;
16use std::sync::atomic::{AtomicBool, Ordering};
17use std::time::Duration;
18
19use api::v1::HealthCheckRequest;
20use api::v1::flow::flow_client::FlowClient as PbFlowClient;
21use api::v1::health_check_client::HealthCheckClient;
22use api::v1::prometheus_gateway_client::PrometheusGatewayClient;
23use api::v1::region::region_client::RegionClient as PbRegionClient;
24use arrow_flight::flight_service_client::FlightServiceClient;
25use common_grpc::channel_manager::{
26 ChannelConfig, ChannelManager, ClientTlsOption, load_client_tls_config,
27};
28use parking_lot::RwLock;
29use snafu::{OptionExt, ResultExt};
30use tonic::codec::CompressionEncoding;
31use tonic::transport::Channel;
32
33use crate::load_balance::{LoadBalance, Loadbalancer};
34use crate::{Result, error};
35
36const DEFAULT_HEALTH_CHECK_INTERVAL: Duration = Duration::from_secs(30);
37const DEFAULT_HEALTH_CHECK_TIMEOUT: Duration = Duration::from_secs(1);
38
39#[derive(Clone, Debug)]
41pub struct ClientOptions {
42 pub health_check_interval: Duration,
44 pub health_check_timeout: Duration,
46}
47
48impl Default for ClientOptions {
49 fn default() -> Self {
50 Self {
51 health_check_interval: DEFAULT_HEALTH_CHECK_INTERVAL,
52 health_check_timeout: DEFAULT_HEALTH_CHECK_TIMEOUT,
53 }
54 }
55}
56
57pub struct FlightClient {
58 addr: String,
59 client: FlightServiceClient<Channel>,
60}
61
62impl FlightClient {
63 pub fn addr(&self) -> &str {
64 &self.addr
65 }
66
67 pub fn mut_inner(&mut self) -> &mut FlightServiceClient<Channel> {
68 &mut self.client
69 }
70}
71
72#[derive(Clone, Debug, Default)]
73pub struct Client {
74 inner: Arc<Inner>,
75}
76
77#[derive(Debug)]
78struct Inner {
79 query_channel_manager: ChannelManager,
80 control_channel_manager: ChannelManager,
81 peers: RwLock<Peers>,
82 load_balance: Loadbalancer,
83 health_check_interval: Duration,
84 health_check_timeout: Duration,
85 health_check_started: AtomicBool,
86}
87
88impl Default for Inner {
89 fn default() -> Self {
90 Self::with_manager_and_peers(ChannelManager::new(), Vec::new(), ClientOptions::default())
91 }
92}
93
94#[derive(Debug, Default, PartialEq, Eq)]
95struct PeerStates {
96 active: Vec<usize>,
97 inactive: Vec<usize>,
98}
99
100#[derive(Debug, Default)]
101struct Peers {
102 addresses: Vec<String>,
103 states: PeerStates,
104 generation: u64,
105}
106
107impl Inner {
108 fn with_manager_and_peers(
109 channel_manager: ChannelManager,
110 peers: Vec<String>,
111 options: ClientOptions,
112 ) -> Self {
113 Self::with_managers_and_peers(channel_manager.clone(), channel_manager, peers, options)
114 }
115
116 fn with_managers_and_peers(
117 query_channel_manager: ChannelManager,
118 control_channel_manager: ChannelManager,
119 peers: Vec<String>,
120 options: ClientOptions,
121 ) -> Self {
122 let peer_count = peers.len();
123 Self {
124 query_channel_manager,
125 control_channel_manager,
126 peers: RwLock::new(Peers {
127 addresses: peers,
128 states: PeerStates {
129 active: (0..peer_count).collect(),
130 inactive: Vec::new(),
131 },
132 generation: 0,
133 }),
134 load_balance: Loadbalancer::default(),
135 health_check_interval: options.health_check_interval,
136 health_check_timeout: options.health_check_timeout,
137 health_check_started: AtomicBool::new(false),
138 }
139 }
140
141 fn set_peers(&self, addresses: Vec<String>) {
142 let peer_count = addresses.len();
143 let mut peers = self.peers.write();
144 peers.addresses = addresses;
145 peers.states = PeerStates {
146 active: (0..peer_count).collect(),
147 inactive: Vec::new(),
148 };
149 peers.generation = peers.generation.wrapping_add(1);
150 }
151
152 fn peer_count(&self) -> usize {
153 self.peers.read().addresses.len()
154 }
155
156 fn get_peer(&self) -> Option<String> {
157 let peers = self.peers.read();
158 let index = self
159 .load_balance
160 .get_index(&peers.states.active)
161 .or_else(|| self.load_balance.get_index(&peers.states.inactive))?;
162 Some(peers.addresses[*index].clone())
163 }
164
165 async fn refresh_peer_states(&self) {
166 let (generation, peers) = {
167 let peers = self.peers.read();
168 let addresses = peers
169 .states
170 .active
171 .iter()
172 .chain(&peers.states.inactive)
173 .map(|&index| (index, peers.addresses[index].clone()))
174 .collect::<Vec<_>>();
175 (peers.generation, addresses)
176 };
177 let health_checks = peers.into_iter().map(|(index, addr)| async move {
178 let is_active = self.check_peer_health(&addr).await;
179 (index, is_active)
180 });
181 let results = futures::future::join_all(health_checks).await;
182
183 let (active, inactive) = results.into_iter().fold(
184 (Vec::new(), Vec::new()),
185 |(mut active, mut inactive), (index, is_active)| {
186 if is_active {
187 active.push(index);
188 } else {
189 inactive.push(index);
190 }
191 (active, inactive)
192 },
193 );
194
195 let mut peers = self.peers.write();
196 if peers.generation == generation {
197 peers.states = PeerStates { active, inactive };
198 }
199 }
200
201 async fn check_peer_health(&self, addr: &str) -> bool {
202 let Ok(channel) = self.control_channel_manager.get(addr) else {
203 return false;
204 };
205 let mut client = HealthCheckClient::new(channel);
206 tokio::time::timeout(
207 self.health_check_timeout,
208 client.health_check(HealthCheckRequest {}),
209 )
210 .await
211 .is_ok_and(|result| result.is_ok())
212 }
213}
214
215fn random_initial_delay(max_delay: Duration) -> Duration {
216 let max_nanos = max_delay.as_nanos().min(u64::MAX as u128) as u64;
217 if max_nanos == 0 {
218 return Duration::ZERO;
219 }
220
221 Duration::from_nanos(rand::random_range(0..max_nanos))
222}
223
224impl Client {
225 pub fn new() -> Self {
226 Default::default()
227 }
228
229 pub fn with_urls<U, A>(urls: A) -> Self
230 where
231 U: AsRef<str>,
232 A: AsRef<[U]>,
233 {
234 Self::with_urls_and_options(urls, ClientOptions::default())
235 }
236
237 pub fn with_urls_and_options<U, A>(urls: A, options: ClientOptions) -> Self
239 where
240 U: AsRef<str>,
241 A: AsRef<[U]>,
242 {
243 Self::with_manager_and_urls_and_options(ChannelManager::new(), urls, options)
244 }
245
246 pub fn with_tls_and_urls<U, A>(urls: A, client_tls: ClientTlsOption) -> Result<Self>
247 where
248 U: AsRef<str>,
249 A: AsRef<[U]>,
250 {
251 Self::with_tls_and_urls_and_options(urls, client_tls, ClientOptions::default())
252 }
253
254 pub fn with_tls_and_urls_and_options<U, A>(
256 urls: A,
257 client_tls: ClientTlsOption,
258 options: ClientOptions,
259 ) -> Result<Self>
260 where
261 U: AsRef<str>,
262 A: AsRef<[U]>,
263 {
264 let channel_config = ChannelConfig::default().client_tls_config(client_tls.clone());
265 let tls_config =
266 load_client_tls_config(Some(client_tls)).context(error::CreateTlsChannelSnafu)?;
267 let channel_manager = ChannelManager::with_config(channel_config, tls_config);
268 Ok(Self::with_manager_and_urls_and_options(
269 channel_manager,
270 urls,
271 options,
272 ))
273 }
274
275 pub fn with_manager_and_urls<U, A>(channel_manager: ChannelManager, urls: A) -> Self
276 where
277 U: AsRef<str>,
278 A: AsRef<[U]>,
279 {
280 Self::with_manager_and_urls_and_options(channel_manager, urls, ClientOptions::default())
281 }
282
283 pub(crate) fn with_managers_and_urls<U, A>(
284 query_channel_manager: ChannelManager,
285 control_channel_manager: ChannelManager,
286 urls: A,
287 ) -> Self
288 where
289 U: AsRef<str>,
290 A: AsRef<[U]>,
291 {
292 Self::with_managers_and_urls_and_options(
293 query_channel_manager,
294 control_channel_manager,
295 urls,
296 ClientOptions::default(),
297 )
298 }
299
300 fn with_managers_and_urls_and_options<U, A>(
301 query_channel_manager: ChannelManager,
302 control_channel_manager: ChannelManager,
303 urls: A,
304 options: ClientOptions,
305 ) -> Self
306 where
307 U: AsRef<str>,
308 A: AsRef<[U]>,
309 {
310 let urls: Vec<String> = urls
311 .as_ref()
312 .iter()
313 .map(|peer| peer.as_ref().to_string())
314 .collect();
315 Self {
316 inner: Arc::new(Inner::with_managers_and_peers(
317 query_channel_manager,
318 control_channel_manager,
319 urls,
320 options,
321 )),
322 }
323 }
324
325 pub fn with_manager_and_urls_and_options<U, A>(
327 channel_manager: ChannelManager,
328 urls: A,
329 options: ClientOptions,
330 ) -> Self
331 where
332 U: AsRef<str>,
333 A: AsRef<[U]>,
334 {
335 let channel_manager_for_query = channel_manager.clone();
336 Self::with_managers_and_urls_and_options(
337 channel_manager_for_query,
338 channel_manager,
339 urls,
340 options,
341 )
342 }
343
344 pub fn start<U, A>(&self, urls: A)
345 where
346 U: AsRef<str>,
347 A: AsRef<[U]>,
348 {
349 let urls = urls
350 .as_ref()
351 .iter()
352 .map(|peer| peer.as_ref().to_string())
353 .collect();
354 self.inner.set_peers(urls);
355 }
356
357 fn trigger_health_check(&self) {
358 if self.inner.health_check_interval.is_zero() || self.inner.peer_count() <= 1 {
359 return;
360 }
361
362 if self
363 .inner
364 .health_check_started
365 .swap(true, Ordering::Relaxed)
366 {
367 return;
368 }
369
370 let inner = Arc::downgrade(&self.inner);
371 let health_check_interval = self.inner.health_check_interval;
372 common_runtime::spawn_global(async move {
373 tokio::time::sleep(random_initial_delay(health_check_interval)).await;
374 let mut interval = tokio::time::interval(health_check_interval);
375 interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
376
377 loop {
378 interval.tick().await;
379 let Some(inner) = inner.upgrade() else {
380 return;
381 };
382 if inner.peer_count() > 1 {
383 inner.refresh_peer_states().await;
384 }
385 }
386 });
387 }
388
389 pub fn find_channel(&self) -> Result<(String, Channel)> {
390 self.trigger_health_check();
391
392 let addr = self
393 .inner
394 .get_peer()
395 .context(error::IllegalGrpcClientStateSnafu {
396 err_msg: "No available peer found",
397 })?;
398
399 let channel = self
400 .inner
401 .control_channel_manager
402 .get(&addr)
403 .context(error::CreateChannelSnafu { addr: &addr })?;
404 Ok((addr, channel))
405 }
406
407 pub fn max_grpc_recv_message_size(&self) -> usize {
408 self.inner
409 .control_channel_manager
410 .config()
411 .max_recv_message_size
412 .as_bytes() as usize
413 }
414
415 pub fn max_grpc_send_message_size(&self) -> usize {
416 self.inner
417 .control_channel_manager
418 .config()
419 .max_send_message_size
420 .as_bytes() as usize
421 }
422
423 pub fn make_flight_client(
427 &self,
428 send_compression: bool,
429 accept_compression: bool,
430 ) -> Result<FlightClient> {
431 self.make_flight_client_with_manager(
432 &self.inner.query_channel_manager,
433 send_compression,
434 accept_compression,
435 )
436 }
437
438 pub(crate) fn make_control_flight_client(
439 &self,
440 send_compression: bool,
441 accept_compression: bool,
442 ) -> Result<FlightClient> {
443 self.make_flight_client_with_manager(
444 &self.inner.control_channel_manager,
445 send_compression,
446 accept_compression,
447 )
448 }
449
450 fn make_flight_client_with_manager(
451 &self,
452 channel_manager: &ChannelManager,
453 send_compression: bool,
454 accept_compression: bool,
455 ) -> Result<FlightClient> {
456 self.trigger_health_check();
457 let addr = self
458 .inner
459 .get_peer()
460 .context(error::IllegalGrpcClientStateSnafu {
461 err_msg: "No available peer found",
462 })?;
463 let channel = channel_manager
464 .get(&addr)
465 .context(error::CreateChannelSnafu { addr: &addr })?;
466
467 let mut client = FlightServiceClient::new(channel)
468 .max_decoding_message_size(
469 channel_manager.config().max_recv_message_size.as_bytes() as usize
470 )
471 .max_encoding_message_size(
472 channel_manager.config().max_send_message_size.as_bytes() as usize
473 );
474 if send_compression {
476 client = client.send_compressed(CompressionEncoding::Zstd);
477 }
478 if accept_compression {
479 client = client.accept_compressed(CompressionEncoding::Zstd);
480 }
481
482 Ok(FlightClient { addr, client })
483 }
484
485 pub(crate) fn raw_region_client(&self) -> Result<(String, PbRegionClient<Channel>)> {
486 let (addr, channel) = self.find_channel()?;
487 let client = PbRegionClient::new(channel)
488 .max_decoding_message_size(
489 self.inner
490 .control_channel_manager
491 .config()
492 .max_recv_message_size
493 .as_bytes() as usize,
494 )
495 .max_encoding_message_size(
496 self.inner
497 .control_channel_manager
498 .config()
499 .max_send_message_size
500 .as_bytes() as usize,
501 );
502 Ok((addr, client))
503 }
504
505 pub(crate) fn raw_flow_client(&self) -> Result<(String, PbFlowClient<Channel>)> {
506 let (addr, channel) = self.find_channel()?;
507 let client = PbFlowClient::new(channel)
508 .max_decoding_message_size(
509 self.inner
510 .control_channel_manager
511 .config()
512 .max_recv_message_size
513 .as_bytes() as usize,
514 )
515 .max_encoding_message_size(
516 self.inner
517 .control_channel_manager
518 .config()
519 .max_send_message_size
520 .as_bytes() as usize,
521 )
522 .accept_compressed(CompressionEncoding::Zstd)
523 .send_compressed(CompressionEncoding::Zstd);
524 Ok((addr, client))
525 }
526
527 pub fn make_prometheus_gateway_client(&self) -> Result<PrometheusGatewayClient<Channel>> {
528 let (_, channel) = self.find_channel()?;
529 let client = PrometheusGatewayClient::new(channel)
530 .accept_compressed(CompressionEncoding::Gzip)
531 .accept_compressed(CompressionEncoding::Zstd)
532 .send_compressed(CompressionEncoding::Gzip)
533 .send_compressed(CompressionEncoding::Zstd);
534 Ok(client)
535 }
536
537 pub async fn health_check(&self) -> Result<()> {
538 let (_, channel) = self.find_channel()?;
539 let mut client = HealthCheckClient::new(channel);
540 let _ = client.health_check(HealthCheckRequest {}).await?;
541 Ok(())
542 }
543
544 #[cfg(feature = "testing")]
546 pub fn peer_addresses_by_state(&self) -> (Vec<String>, Vec<String>) {
547 let peers = self.inner.peers.read();
548 let addresses = |indices: &[usize]| {
549 indices
550 .iter()
551 .map(|&index| peers.addresses[index].clone())
552 .collect()
553 };
554 (
555 addresses(&peers.states.active),
556 addresses(&peers.states.inactive),
557 )
558 }
559}
560
561#[cfg(test)]
562mod tests {
563 use std::collections::HashSet;
564 use std::sync::Arc;
565 use std::sync::atomic::Ordering;
566 use std::time::Duration;
567
568 use api::v1::health_check_server::{HealthCheck, HealthCheckServer};
569 use api::v1::{HealthCheckRequest, HealthCheckResponse};
570 use common_grpc::channel_manager::ChannelManager;
571 use tokio::net::TcpListener;
572 use tokio::sync::Notify;
573 use tokio::task::JoinHandle;
574 use tokio::time::{interval, timeout};
575 use tokio_stream::wrappers::TcpListenerStream;
576 use tonic::{Request, Response, Status};
577
578 use super::{Client, ClientOptions, Inner, PeerStates};
579 use crate::load_balance::Loadbalancer;
580
581 const HEALTH_REFRESH_INTERVAL: Duration = Duration::from_millis(10);
582 const STATE_REFRESH_TIMEOUT: Duration = Duration::from_secs(1);
583
584 struct HealthyHealthCheck;
585
586 #[tonic::async_trait]
587 impl HealthCheck for HealthyHealthCheck {
588 async fn health_check(
589 &self,
590 _request: Request<HealthCheckRequest>,
591 ) -> Result<Response<HealthCheckResponse>, Status> {
592 Ok(Response::new(HealthCheckResponse {}))
593 }
594 }
595
596 struct UnhealthyHealthCheck;
597
598 #[tonic::async_trait]
599 impl HealthCheck for UnhealthyHealthCheck {
600 async fn health_check(
601 &self,
602 _request: Request<HealthCheckRequest>,
603 ) -> Result<Response<HealthCheckResponse>, Status> {
604 Err(Status::unavailable("peer is unavailable"))
605 }
606 }
607
608 struct PendingHealthCheck {
609 started: Option<Arc<Notify>>,
610 }
611
612 #[tonic::async_trait]
613 impl HealthCheck for PendingHealthCheck {
614 async fn health_check(
615 &self,
616 _request: Request<HealthCheckRequest>,
617 ) -> Result<Response<HealthCheckResponse>, Status> {
618 if let Some(started) = &self.started {
619 started.notify_one();
620 }
621 std::future::pending().await
622 }
623 }
624
625 async fn start_health_check_server<T>(handler: T) -> (String, JoinHandle<()>)
626 where
627 T: HealthCheck + Send + Sync + 'static,
628 {
629 let listener = TcpListener::bind("127.0.0.1:0")
630 .await
631 .expect("bind health check server");
632 let addr = listener
633 .local_addr()
634 .expect("read health check server address")
635 .to_string();
636 let server = tokio::spawn(async move {
637 tonic::transport::Server::builder()
638 .add_service(HealthCheckServer::new(handler))
639 .serve_with_incoming(TcpListenerStream::new(listener))
640 .await
641 .expect("serve health check server");
642 });
643
644 (addr, server)
645 }
646
647 async fn wait_for_peer_states(client: &Client, expected: PeerStates) {
648 let mut poll = interval(HEALTH_REFRESH_INTERVAL);
649 timeout(STATE_REFRESH_TIMEOUT, async {
650 loop {
651 poll.tick().await;
652 if client.inner.peers.read().states == expected {
653 return;
654 }
655 }
656 })
657 .await
658 .expect("health refresh did not reach expected peer states");
659 }
660
661 fn mock_peers() -> Vec<String> {
662 vec![
663 "127.0.0.1:3001".to_string(),
664 "127.0.0.1:3002".to_string(),
665 "127.0.0.1:3003".to_string(),
666 ]
667 }
668
669 #[test]
670 fn test_inner() {
671 let inner = Inner::default();
672
673 assert!(matches!(
674 inner.load_balance,
675 Loadbalancer::Random(crate::load_balance::Random)
676 ));
677 assert!(inner.get_peer().is_none());
678
679 let peers = mock_peers();
680 let all: HashSet<String> = peers.iter().cloned().collect();
681 let inner =
682 Inner::with_manager_and_peers(ChannelManager::new(), peers, ClientOptions::default());
683
684 for _ in 0..20 {
685 assert!(all.contains(&inner.get_peer().unwrap()));
686 }
687 }
688
689 #[test]
690 fn test_inner_prefers_active_peer() {
691 let peers = mock_peers();
692 let inner = Inner::with_manager_and_peers(
693 ChannelManager::new(),
694 peers.clone(),
695 ClientOptions::default(),
696 );
697 inner.peers.write().states = PeerStates {
698 active: vec![0],
699 inactive: vec![1, 2],
700 };
701
702 assert_eq!(Some(peers[0].clone()), inner.get_peer());
703 }
704
705 #[test]
706 fn test_zero_health_check_interval_disables_background_task() {
707 let client = Client::with_urls_and_options(
708 mock_peers(),
709 ClientOptions {
710 health_check_interval: Duration::ZERO,
711 ..Default::default()
712 },
713 );
714
715 assert!(!client.inner.health_check_started.load(Ordering::Relaxed));
716 let peers = client.inner.peers.read();
717 assert_eq!(mock_peers(), peers.addresses);
718 assert_eq!(vec![0, 1, 2], peers.states.active);
719 assert!(peers.states.inactive.is_empty());
720 }
721
722 #[test]
723 fn test_multi_peer_constructor_defers_background_task() {
724 let client = Client::with_urls(mock_peers());
725
726 assert!(!client.inner.health_check_started.load(Ordering::Relaxed));
727 }
728
729 #[tokio::test]
730 async fn test_single_peer_does_not_start_background_task() {
731 let client = Client::with_urls(["127.0.0.1:3001"]);
732
733 client.find_channel().unwrap();
734
735 assert!(!client.inner.health_check_started.load(Ordering::Relaxed));
736 }
737
738 #[test]
739 fn test_start_initializes_new_client_without_starting_background_task() {
740 let client = Client::new();
741 let peers = mock_peers();
742
743 client.start(peers.clone());
744
745 assert!(peers.contains(&client.inner.get_peer().unwrap()));
746 assert!(!client.inner.health_check_started.load(Ordering::Relaxed));
747 }
748
749 #[tokio::test]
750 async fn test_health_refresh_marks_unhealthy_peer_inactive_and_selects_healthy_peer() {
751 let (healthy_addr, healthy_server) = start_health_check_server(HealthyHealthCheck).await;
753 let (unhealthy_addr, unhealthy_server) =
754 start_health_check_server(UnhealthyHealthCheck).await;
755 let client = Client::with_urls_and_options(
756 [healthy_addr.clone(), unhealthy_addr],
757 ClientOptions {
758 health_check_interval: HEALTH_REFRESH_INTERVAL,
759 ..Default::default()
760 },
761 );
762 assert!(!client.inner.health_check_started.load(Ordering::Relaxed));
763
764 client.find_channel().unwrap();
766 assert!(client.inner.health_check_started.load(Ordering::Relaxed));
767 wait_for_peer_states(
768 &client,
769 PeerStates {
770 active: vec![0],
771 inactive: vec![1],
772 },
773 )
774 .await;
775
776 assert_eq!(Some(healthy_addr), client.inner.get_peer());
778
779 healthy_server.abort();
780 unhealthy_server.abort();
781 }
782
783 #[tokio::test]
784 async fn test_health_refresh_times_out_pending_peer() {
785 let (healthy_addr, healthy_server) = start_health_check_server(HealthyHealthCheck).await;
786 let (pending_addr, pending_server) =
787 start_health_check_server(PendingHealthCheck { started: None }).await;
788 let client = Client::with_urls_and_options(
789 [healthy_addr, pending_addr],
790 ClientOptions {
791 health_check_interval: HEALTH_REFRESH_INTERVAL,
792 health_check_timeout: Duration::from_millis(20),
793 },
794 );
795
796 client.find_channel().unwrap();
797 wait_for_peer_states(
798 &client,
799 PeerStates {
800 active: vec![0],
801 inactive: vec![1],
802 },
803 )
804 .await;
805
806 healthy_server.abort();
807 pending_server.abort();
808 }
809
810 #[tokio::test]
811 async fn test_peer_update_ignores_in_flight_health_result() {
812 let started = Arc::new(Notify::new());
813 let (pending_addr, pending_server) = start_health_check_server(PendingHealthCheck {
814 started: Some(started.clone()),
815 })
816 .await;
817 let inner = Arc::new(Inner::with_manager_and_peers(
818 ChannelManager::new(),
819 vec![pending_addr],
820 ClientOptions {
821 health_check_timeout: Duration::from_millis(20),
822 ..Default::default()
823 },
824 ));
825 let refresh_inner = inner.clone();
826 let refresh = tokio::spawn(async move {
827 refresh_inner.refresh_peer_states().await;
828 });
829 timeout(STATE_REFRESH_TIMEOUT, started.notified())
830 .await
831 .expect("pending health check did not start");
832
833 inner.set_peers(vec!["127.0.0.1:3001".to_string()]);
834 refresh.await.unwrap();
835
836 let peers = inner.peers.read();
837 assert_eq!(vec!["127.0.0.1:3001"], peers.addresses);
838 assert_eq!(vec![0], peers.states.active);
839 assert!(peers.states.inactive.is_empty());
840 pending_server.abort();
841 }
842}