1use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
16use std::fmt::Debug;
17use std::sync::{Arc, Mutex};
18use std::time::{Duration, Instant};
19
20use async_trait::async_trait;
21use common_meta::DatanodeId;
22use common_meta::affinity::{AffinityFallback, AffinityReference, AffinityResolverRef};
23use common_meta::datanode::Stat;
24use common_meta::ddl::{DetectingRegion, RegionFailureDetectorController};
25use common_meta::key::runtime_switch::RuntimeSwitchManagerRef;
26use common_meta::key::table_route::{TableRouteKey, TableRouteStorage, TableRouteValue};
27use common_meta::key::{MetadataKey, MetadataValue};
28use common_meta::kv_backend::KvBackendRef;
29use common_meta::leadership_notifier::LeadershipChangeListener;
30use common_meta::peer::{Peer, PeerResolverRef};
31use common_meta::range_stream::{DEFAULT_PAGE_SIZE, PaginationStream};
32use common_meta::rpc::store::RangeRequest;
33use common_runtime::JoinHandle;
34use common_telemetry::{debug, error, info, warn};
35use common_time::util::current_time_millis;
36use futures::{StreamExt, TryStreamExt};
37use itertools::Itertools;
38use snafu::{ResultExt, ensure};
39use store_api::storage::RegionId;
40use tokio::sync::mpsc::error::TrySendError;
41use tokio::sync::mpsc::{Receiver, Sender};
42use tokio::sync::oneshot;
43use tokio::time::{MissedTickBehavior, interval, interval_at};
44
45use crate::discovery::utils::accept_ingest_workload;
46use crate::error::{self, Result};
47use crate::failure_detector::PhiAccrualFailureDetectorOptions;
48use crate::metasrv::{RegionStatAwareSelectorRef, SelectorContext, SelectorRef};
49use crate::metrics::METRIC_META_HEARTBEAT_DROPPED;
50use crate::procedure::region_migration::manager::{
51 RegionMigrationManagerRef, RegionMigrationTriggerReason, SubmitRegionMigrationTaskResult,
52};
53use crate::procedure::region_migration::utils::RegionMigrationTaskBatch;
54use crate::procedure::region_migration::{
55 DEFAULT_REGION_MIGRATION_TIMEOUT, RegionMigrationProcedureTask,
56};
57use crate::region::failure_detector::RegionFailureDetector;
58use crate::selector::SelectorOptions;
59use crate::state::StateRef;
60
61#[derive(Debug)]
65pub(crate) struct DatanodeHeartbeat {
66 datanode_id: DatanodeId,
67 regions: Vec<RegionId>,
69 timestamp: i64,
70}
71
72impl From<&Stat> for DatanodeHeartbeat {
73 fn from(value: &Stat) -> Self {
74 DatanodeHeartbeat {
75 datanode_id: value.id,
76 regions: value.region_stats.iter().map(|x| x.id).collect(),
77 timestamp: value.timestamp_millis,
78 }
79 }
80}
81
82pub(crate) enum Event {
99 Tick,
100 InitializeAllRegions(tokio::sync::oneshot::Sender<()>),
101 RegisterFailureDetectors(Vec<DetectingRegion>),
102 DeregisterFailureDetectors(Vec<DetectingRegion>),
103 ResetFailureDetectors(Vec<DetectingRegion>),
104 HeartbeatArrived(DatanodeHeartbeat),
105 Clear,
106 #[cfg(test)]
107 Dump(tokio::sync::oneshot::Sender<RegionFailureDetector>),
108}
109
110impl Debug for Event {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 match self {
113 Self::Tick => write!(f, "Tick"),
114 Self::HeartbeatArrived(arg0) => f.debug_tuple("HeartbeatArrived").field(arg0).finish(),
115 Self::Clear => write!(f, "Clear"),
116 Self::InitializeAllRegions(_) => write!(f, "InspectAndRegisterRegions"),
117 Self::RegisterFailureDetectors(arg0) => f
118 .debug_tuple("RegisterFailureDetectors")
119 .field(arg0)
120 .finish(),
121 Self::ResetFailureDetectors(arg0) => {
122 f.debug_tuple("ResetFailureDetectors").field(arg0).finish()
123 }
124 Self::DeregisterFailureDetectors(arg0) => f
125 .debug_tuple("DeregisterFailureDetectors")
126 .field(arg0)
127 .finish(),
128 #[cfg(test)]
129 Self::Dump(_) => f.debug_struct("Dump").finish(),
130 }
131 }
132}
133
134pub type RegionSupervisorTickerRef = Arc<RegionSupervisorTicker>;
135
136#[derive(Debug)]
138pub struct RegionSupervisorTicker {
139 tick_handle: Mutex<Option<JoinHandle<()>>>,
141
142 initialization_handle: Mutex<Option<JoinHandle<()>>>,
144
145 tick_interval: Duration,
147
148 initialization_delay: Duration,
150
151 initialization_retry_period: Duration,
153
154 sender: Sender<Event>,
156}
157
158#[async_trait]
159impl LeadershipChangeListener for RegionSupervisorTicker {
160 fn name(&self) -> &'static str {
161 "RegionSupervisorTicker"
162 }
163
164 async fn on_leader_start(&self) -> common_meta::error::Result<()> {
165 self.start();
166 Ok(())
167 }
168
169 async fn on_leader_stop(&self) -> common_meta::error::Result<()> {
170 self.stop();
171 Ok(())
172 }
173}
174
175impl RegionSupervisorTicker {
176 pub(crate) fn new(
177 tick_interval: Duration,
178 initialization_delay: Duration,
179 initialization_retry_period: Duration,
180 sender: Sender<Event>,
181 ) -> Self {
182 info!(
183 "RegionSupervisorTicker is created, tick_interval: {:?}, initialization_delay: {:?}, initialization_retry_period: {:?}",
184 tick_interval, initialization_delay, initialization_retry_period
185 );
186 Self {
187 tick_handle: Mutex::new(None),
188 initialization_handle: Mutex::new(None),
189 tick_interval,
190 initialization_delay,
191 initialization_retry_period,
192 sender,
193 }
194 }
195
196 pub fn start(&self) {
198 let mut handle = self.tick_handle.lock().unwrap();
199 if handle.is_none() {
200 let sender = self.sender.clone();
201 let tick_interval = self.tick_interval;
202 let initialization_delay = self.initialization_delay;
203
204 let mut initialization_interval = interval_at(
205 tokio::time::Instant::now() + initialization_delay,
206 self.initialization_retry_period,
207 );
208 initialization_interval.set_missed_tick_behavior(MissedTickBehavior::Skip);
209 let initialization_handler = common_runtime::spawn_global(async move {
210 loop {
211 initialization_interval.tick().await;
212 let (tx, rx) = oneshot::channel();
213 if sender.send(Event::InitializeAllRegions(tx)).await.is_err() {
214 info!(
215 "EventReceiver is dropped, region failure detectors initialization loop is stopped"
216 );
217 break;
218 }
219 if rx.await.is_ok() {
220 info!("All region failure detectors are initialized.");
221 break;
222 }
223 }
224 });
225 *self.initialization_handle.lock().unwrap() = Some(initialization_handler);
226
227 let sender = self.sender.clone();
228 let ticker_loop = tokio::spawn(async move {
229 let mut tick_interval = interval(tick_interval);
230 tick_interval.set_missed_tick_behavior(MissedTickBehavior::Skip);
231
232 if let Err(err) = sender.send(Event::Clear).await {
233 warn!(err; "EventReceiver is dropped, failed to send Event::Clear");
234 return;
235 }
236 loop {
237 tick_interval.tick().await;
238 if sender.send(Event::Tick).await.is_err() {
239 info!("EventReceiver is dropped, tick loop is stopped");
240 break;
241 }
242 }
243 });
244 *handle = Some(ticker_loop);
245 }
246 }
247
248 pub fn stop(&self) {
250 let handle = self.tick_handle.lock().unwrap().take();
251 if let Some(handle) = handle {
252 handle.abort();
253 info!("The tick loop is stopped.");
254 }
255 let initialization_handler = self.initialization_handle.lock().unwrap().take();
256 if let Some(initialization_handler) = initialization_handler {
257 initialization_handler.abort();
258 info!("The initialization loop is stopped.");
259 }
260 }
261}
262
263impl Drop for RegionSupervisorTicker {
264 fn drop(&mut self) {
265 self.stop();
266 }
267}
268
269pub type RegionSupervisorRef = Arc<RegionSupervisor>;
270
271pub const DEFAULT_TICK_INTERVAL: Duration = Duration::from_secs(1);
273pub const DEFAULT_INITIALIZATION_RETRY_PERIOD: Duration = Duration::from_secs(60);
275
276pub enum RegionSupervisorSelector {
278 NaiveSelector(SelectorRef),
279 RegionStatAwareSelector(RegionStatAwareSelectorRef),
280}
281
282pub struct RegionSupervisor {
285 failure_detector: RegionFailureDetector,
287 failover_counts: HashMap<DetectingRegion, u32>,
289 receiver: Receiver<Event>,
291 selector_context: SelectorContext,
293 selector: RegionSupervisorSelector,
295 affinity_resolver: Option<AffinityResolverRef>,
297 region_migration_manager: RegionMigrationManagerRef,
299 runtime_switch_manager: RuntimeSwitchManagerRef,
301 peer_resolver: PeerResolverRef,
303 kv_backend: KvBackendRef,
305 state: Option<StateRef>,
307}
308
309#[derive(Debug, Clone)]
311pub struct RegionFailureDetectorControl {
312 sender: Sender<Event>,
313}
314
315impl RegionFailureDetectorControl {
316 pub(crate) fn new(sender: Sender<Event>) -> Self {
317 Self { sender }
318 }
319}
320
321#[async_trait::async_trait]
322impl RegionFailureDetectorController for RegionFailureDetectorControl {
323 async fn register_failure_detectors(&self, detecting_regions: Vec<DetectingRegion>) {
324 if let Err(err) = self
325 .sender
326 .send(Event::RegisterFailureDetectors(detecting_regions))
327 .await
328 {
329 error!(err; "RegionSupervisor has stop receiving heartbeat.");
330 }
331 }
332
333 async fn reset_failure_detectors(&self, detecting_regions: Vec<DetectingRegion>) {
334 if let Err(err) = self
335 .sender
336 .send(Event::ResetFailureDetectors(detecting_regions))
337 .await
338 {
339 error!(err; "RegionSupervisor has stop receiving heartbeat.");
340 }
341 }
342
343 async fn deregister_failure_detectors(&self, detecting_regions: Vec<DetectingRegion>) {
344 if let Err(err) = self
345 .sender
346 .send(Event::DeregisterFailureDetectors(detecting_regions))
347 .await
348 {
349 error!(err; "RegionSupervisor has stop receiving heartbeat.");
350 }
351 }
352}
353
354#[derive(Clone)]
356pub(crate) struct HeartbeatAcceptor {
357 sender: Sender<Event>,
358}
359
360impl HeartbeatAcceptor {
361 pub(crate) fn new(sender: Sender<Event>) -> Self {
362 Self { sender }
363 }
364
365 pub(crate) fn accept(&self, heartbeat: DatanodeHeartbeat) {
369 let datanode_id = heartbeat.datanode_id;
370 match self.sender.try_send(Event::HeartbeatArrived(heartbeat)) {
371 Ok(()) => {}
372 Err(TrySendError::Full(_)) => {
373 METRIC_META_HEARTBEAT_DROPPED
374 .with_label_values(&["full"])
375 .inc();
376 warn!(
377 "Dropping heartbeat because the region supervisor event queue is full, datanode_id: {}, queue_capacity: {}",
378 datanode_id,
379 self.sender.max_capacity()
380 );
381 }
382 Err(TrySendError::Closed(err)) => {
383 METRIC_META_HEARTBEAT_DROPPED
384 .with_label_values(&["closed"])
385 .inc();
386 error!(err; "RegionSupervisor has stop receiving heartbeat.");
387 }
388 }
389 }
390}
391
392impl RegionSupervisor {
393 pub(crate) fn channel() -> (Sender<Event>, Receiver<Event>) {
395 tokio::sync::mpsc::channel(1024)
396 }
397
398 #[allow(clippy::too_many_arguments)]
399 pub(crate) fn new(
400 event_receiver: Receiver<Event>,
401 options: PhiAccrualFailureDetectorOptions,
402 selector_context: SelectorContext,
403 selector: RegionSupervisorSelector,
404 region_migration_manager: RegionMigrationManagerRef,
405 runtime_switch_manager: RuntimeSwitchManagerRef,
406 peer_resolver: PeerResolverRef,
407 kv_backend: KvBackendRef,
408 ) -> Self {
409 Self {
410 failure_detector: RegionFailureDetector::new(options),
411 failover_counts: HashMap::new(),
412 receiver: event_receiver,
413 selector_context: SelectorContext {
415 affinity_fallback: AffinityFallback::Unconstrained,
416 ..selector_context
417 },
418 selector,
419 affinity_resolver: None,
420 region_migration_manager,
421 runtime_switch_manager,
422 peer_resolver,
423 kv_backend,
424 state: None,
425 }
426 }
427
428 pub(crate) fn with_affinity_resolver(mut self, resolver: AffinityResolverRef) -> Self {
430 self.affinity_resolver = Some(resolver);
431 self
432 }
433
434 pub(crate) fn with_state(mut self, state: StateRef) -> Self {
436 self.state = Some(state);
437 self
438 }
439
440 pub(crate) async fn run(&mut self) {
442 while let Some(event) = self.receiver.recv().await {
443 if let Some(state) = self.state.as_ref()
444 && !state.read().unwrap().is_leader()
445 {
446 warn!(
447 "The current metasrv is not the leader, ignore {:?} event",
448 event
449 );
450 continue;
451 }
452
453 match event {
454 Event::InitializeAllRegions(sender) => {
455 match self.is_maintenance_mode_enabled().await {
456 Ok(false) => {}
457 Ok(true) => {
458 warn!(
459 "Skipping initialize all regions since maintenance mode is enabled."
460 );
461 continue;
462 }
463 Err(err) => {
464 error!(err; "Failed to check maintenance mode during initialize all regions.");
465 continue;
466 }
467 }
468
469 if let Err(err) = self.initialize_all().await {
470 error!(err; "Failed to initialize all regions.");
471 } else {
472 let _ = sender.send(());
474 }
475 }
476 Event::Tick => {
477 let regions = self.detect_region_failure();
478 self.handle_region_failures(regions).await;
479 }
480 Event::RegisterFailureDetectors(detecting_regions) => {
481 self.register_failure_detectors(detecting_regions).await
482 }
483 Event::ResetFailureDetectors(detecting_regions) => {
484 self.reset_failure_detectors(detecting_regions).await
485 }
486 Event::DeregisterFailureDetectors(detecting_regions) => {
487 self.deregister_failure_detectors(detecting_regions).await
488 }
489 Event::HeartbeatArrived(heartbeat) => self.on_heartbeat_arrived(heartbeat),
490 Event::Clear => {
491 self.clear();
492 info!("Region supervisor is initialized.");
493 }
494 #[cfg(test)]
495 Event::Dump(sender) => {
496 let _ = sender.send(self.failure_detector.dump());
497 }
498 }
499 }
500 info!("RegionSupervisor is stopped!");
501 }
502
503 async fn initialize_all(&self) -> Result<()> {
504 let now = Instant::now();
505 let regions = self.regions();
506 let req = RangeRequest::new().with_prefix(TableRouteKey::range_prefix());
507 let stream = PaginationStream::new(self.kv_backend.clone(), req, DEFAULT_PAGE_SIZE, |kv| {
508 TableRouteKey::from_bytes(&kv.key).map(|v| (v.table_id, kv.value))
509 })
510 .into_stream();
511
512 let mut stream = stream
513 .map_ok(|(_, value)| {
514 TableRouteValue::try_from_raw_value(&value)
515 .context(error::TableMetadataManagerSnafu)
516 })
517 .boxed();
518 let mut detecting_regions = Vec::new();
519 while let Some(route) = stream
520 .try_next()
521 .await
522 .context(error::TableMetadataManagerSnafu)?
523 {
524 let route = route?;
525 if !route.is_physical() {
526 continue;
527 }
528
529 let physical_table_route = route.into_physical_table_route();
530 physical_table_route
531 .region_routes
532 .iter()
533 .for_each(|region_route| {
534 if !regions.contains(®ion_route.region.id)
535 && let Some(leader_peer) = ®ion_route.leader_peer
536 {
537 detecting_regions.push((leader_peer.id, region_route.region.id));
538 }
539 });
540 }
541
542 let num_detecting_regions = detecting_regions.len();
543 if !detecting_regions.is_empty() {
544 self.register_failure_detectors(detecting_regions).await;
545 }
546
547 info!(
548 "Initialize {} region failure detectors, elapsed: {:?}",
549 num_detecting_regions,
550 now.elapsed()
551 );
552
553 Ok(())
554 }
555
556 async fn register_failure_detectors(&self, detecting_regions: Vec<DetectingRegion>) {
557 let ts_millis = current_time_millis();
558 for region in detecting_regions {
559 self.failure_detector
561 .maybe_init_region_failure_detector(region, ts_millis);
562 }
563 }
564
565 async fn reset_failure_detectors(&mut self, detecting_regions: Vec<DetectingRegion>) {
566 let ts_millis = current_time_millis();
567 for region in detecting_regions {
568 self.failure_detector
569 .reset_region_failure_detector(region, ts_millis);
570 }
571 }
572
573 async fn deregister_failure_detectors(&mut self, detecting_regions: Vec<DetectingRegion>) {
574 for region in detecting_regions {
575 self.failure_detector.remove(®ion);
576 self.failover_counts.remove(®ion);
577 }
578 }
579
580 async fn handle_region_failures(&mut self, mut regions: Vec<(DatanodeId, RegionId)>) {
581 if regions.is_empty() {
582 return;
583 }
584 match self.is_maintenance_mode_enabled().await {
585 Ok(false) => {}
586 Ok(true) => {
587 warn!(
588 "Skipping failover since maintenance mode is enabled. Detected region failures: {:?}",
589 regions
590 );
591 return;
592 }
593 Err(err) => {
594 error!(err; "Failed to check maintenance mode");
595 return;
596 }
597 }
598
599 let migrating_regions = regions
601 .extract_if(.., |(_, region_id)| {
602 self.region_migration_manager.tracker().contains(*region_id)
603 })
604 .collect::<Vec<_>>();
605
606 for (datanode_id, region_id) in migrating_regions {
607 debug!(
608 "Removed region failover for region: {region_id}, datanode: {datanode_id} because it's migrating"
609 );
610 }
611
612 if regions.is_empty() {
613 return;
615 }
616
617 let mut grouped_regions: HashMap<u64, Vec<RegionId>> =
618 HashMap::with_capacity(regions.len());
619 for (datanode_id, region_id) in regions {
620 grouped_regions
621 .entry(datanode_id)
622 .or_default()
623 .push(region_id);
624 }
625
626 for (datanode_id, regions) in grouped_regions {
627 warn!(
628 "Detects region failures on datanode: {}, regions: {:?}",
629 datanode_id, regions
630 );
631 let failed_datanodes = [datanode_id];
635 match self
636 .generate_failover_tasks(datanode_id, ®ions, &failed_datanodes)
637 .await
638 {
639 Ok(tasks) => {
640 let mut grouped_tasks: HashMap<(u64, u64), Vec<_>> = HashMap::new();
641 for (task, count) in tasks {
642 grouped_tasks
643 .entry((task.from_peer.id, task.to_peer.id))
644 .or_default()
645 .push((task, count));
646 }
647
648 for ((from_peer_id, to_peer_id), tasks) in grouped_tasks {
649 if tasks.is_empty() {
650 continue;
651 }
652 let task = RegionMigrationTaskBatch::from_tasks(tasks);
653 let region_ids = task.region_ids.clone();
654 if let Err(err) = self.do_failover_tasks(task).await {
655 error!(err; "Failed to execute region failover for regions: {:?}, from_peer: {}, to_peer: {}", region_ids, from_peer_id, to_peer_id);
656 }
657 }
658 }
659 Err(err) => error!(err; "Failed to generate failover tasks"),
660 }
661 }
662 }
663
664 pub(crate) async fn is_maintenance_mode_enabled(&self) -> Result<bool> {
665 self.runtime_switch_manager
666 .maintenance_mode()
667 .await
668 .context(error::RuntimeSwitchManagerSnafu)
669 }
670
671 async fn select_peers(
675 &self,
676 from_peer_id: DatanodeId,
677 regions: &[RegionId],
678 failure_datanodes: &[DatanodeId],
679 exclude_peer_ids_by_region: &HashMap<RegionId, BTreeSet<DatanodeId>>,
680 ) -> Result<Vec<(RegionId, Peer)>> {
681 let affinity = if let Some(resolver) = &self.affinity_resolver {
682 match resolver
683 .resolve(&[AffinityReference::Datanode(from_peer_id)])
684 .await
685 {
686 Ok(mut resolved) => {
687 ensure!(
688 resolved.len() == 1,
689 error::UnexpectedSnafu {
690 violated: "affinity resolution must preserve the number and order of references",
691 }
692 );
693 let affinity = resolved.pop().flatten();
694 if affinity.is_none() {
695 warn!(
696 "Source affinity unavailable for datanode {}; selecting failover peers without affinity",
697 from_peer_id
698 );
699 }
700 affinity
701 }
702 Err(error) => {
703 warn!(error; "Failed to resolve failover affinity for datanode {}; selecting without affinity", from_peer_id);
704 None
705 }
706 }
707 } else {
708 None
709 };
710 let mut exclude_peer_ids: HashSet<u64> =
711 HashSet::from_iter(failure_datanodes.iter().copied());
712 let unschedulable_datanodes = self
713 .selector_context
714 .peer_discovery
715 .active_datanodes(None)
716 .await
717 .map_err(common_error::ext::BoxedError::new)
718 .context(error::OtherSnafu)?
719 .into_iter()
720 .filter(|datanode| !datanode.is_schedulable())
721 .map(|datanode| datanode.peer.id);
722 exclude_peer_ids.extend(unschedulable_datanodes);
723
724 let region_peers = match &self.selector {
725 RegionSupervisorSelector::NaiveSelector(selector) => {
726 let empty = BTreeSet::new();
729 let mut groups = BTreeMap::<_, Vec<_>>::new();
730 for region_id in regions {
731 let excluded = exclude_peer_ids_by_region.get(region_id).unwrap_or(&empty);
732 groups.entry(excluded).or_default().push(*region_id);
733 }
734 let mut region_peers = Vec::with_capacity(regions.len());
735 for (excluded, regions) in groups {
736 let mut exclude_peer_ids = exclude_peer_ids.clone();
737 exclude_peer_ids.extend(excluded);
738 let opts = SelectorOptions {
739 affinity: affinity.as_ref(),
740 min_required_items: regions.len(),
741 allow_duplication: true,
742 exclude_peer_ids,
743 workload_filter: Some(accept_ingest_workload),
744 };
745 match selector.select(&self.selector_context, opts).await {
746 Ok(peers) => region_peers.extend(regions.into_iter().zip(peers)),
747 Err(error::Error::NoEnoughAvailableNode { .. }) => {}
749 Err(err) => return Err(err),
750 }
751 }
752 region_peers
753 }
754 RegionSupervisorSelector::RegionStatAwareSelector(selector) => {
755 let selected = selector
756 .select(
757 &self.selector_context,
758 from_peer_id,
759 regions,
760 exclude_peer_ids,
761 exclude_peer_ids_by_region,
762 affinity.as_ref(),
763 )
764 .await;
765 match selected {
766 Err(error::Error::NoEnoughAvailableNode { .. }) => vec![],
767 selected => selected?,
768 }
769 }
770 };
771 let placed = region_peers
772 .iter()
773 .map(|(region_id, _)| *region_id)
774 .collect::<HashSet<_>>();
775 let unplaced = regions
776 .iter()
777 .filter(|region_id| !placed.contains(region_id))
778 .collect::<Vec<_>>();
779 if !unplaced.is_empty() {
780 error!("No failover target for regions: {:?}", unplaced);
781 }
782 Ok(region_peers)
783 }
784
785 async fn generate_failover_tasks(
786 &mut self,
787 from_peer_id: DatanodeId,
788 regions: &[RegionId],
789 failed_datanodes: &[DatanodeId],
790 ) -> Result<Vec<(RegionMigrationProcedureTask, u32)>> {
791 let mut tasks = Vec::with_capacity(regions.len());
792 let from_peer = self
793 .peer_resolver
794 .datanode(from_peer_id)
795 .await
796 .ok()
797 .flatten()
798 .unwrap_or_else(|| Peer::empty(from_peer_id));
799
800 let table_ids = regions
802 .iter()
803 .map(|region_id| region_id.table_id())
804 .unique()
805 .collect::<Vec<_>>();
806 let table_routes = TableRouteStorage::new(self.kv_backend.clone())
807 .batch_get(&table_ids)
808 .await
809 .unwrap_or_else(|err| {
810 warn!(err; "Failed to read the routes of failover regions: {:?}", regions);
812 vec![]
813 });
814 let exclude_peer_ids_by_region = table_routes
815 .iter()
816 .flatten()
817 .filter_map(|table_route| table_route.region_routes().ok())
818 .flatten()
819 .map(|route| {
820 let peers = route.follower_peers.iter().map(|peer| peer.id);
821 (route.region.id, peers.collect())
822 })
823 .collect();
824 let region_peers = self
825 .select_peers(
826 from_peer_id,
827 regions,
828 failed_datanodes,
829 &exclude_peer_ids_by_region,
830 )
831 .await?;
832
833 for (region_id, peer) in region_peers {
834 let count = *self
835 .failover_counts
836 .entry((from_peer_id, region_id))
837 .and_modify(|count| *count += 1)
838 .or_insert(1);
839 let task = RegionMigrationProcedureTask {
840 region_id,
841 from_peer: from_peer.clone(),
842 to_peer: peer,
843 timeout: DEFAULT_REGION_MIGRATION_TIMEOUT * count,
844 trigger_reason: RegionMigrationTriggerReason::Failover,
845 };
846 tasks.push((task, count));
847 }
848
849 Ok(tasks)
850 }
851
852 async fn do_failover_tasks(&mut self, task: RegionMigrationTaskBatch) -> Result<()> {
853 let from_peer_id = task.from_peer.id;
854 let to_peer_id = task.to_peer.id;
855 let timeout = task.timeout;
856 let trigger_reason = task.trigger_reason;
857 let result = self
858 .region_migration_manager
859 .submit_region_migration_task(task)
860 .await?;
861 self.handle_submit_region_migration_task_result(
862 from_peer_id,
863 to_peer_id,
864 timeout,
865 trigger_reason,
866 result,
867 )
868 .await
869 }
870
871 async fn handle_submit_region_migration_task_result(
872 &mut self,
873 from_peer_id: DatanodeId,
874 to_peer_id: DatanodeId,
875 timeout: Duration,
876 trigger_reason: RegionMigrationTriggerReason,
877 result: SubmitRegionMigrationTaskResult,
878 ) -> Result<()> {
879 if !result.migrated.is_empty() {
880 let detecting_regions = result
881 .migrated
882 .iter()
883 .map(|region_id| (from_peer_id, *region_id))
884 .collect::<Vec<_>>();
885 self.deregister_failure_detectors(detecting_regions).await;
886 info!(
887 "Region has been migrated to target peer: {}, removed failover detectors for regions: {:?}",
888 to_peer_id, result.migrated,
889 )
890 }
891 if !result.migrating.is_empty() {
892 info!(
893 "Region is still migrating, skipping failover for regions: {:?}",
894 result.migrating
895 );
896 }
897 if !result.region_not_found.is_empty() {
898 let detecting_regions = result
899 .region_not_found
900 .iter()
901 .map(|region_id| (from_peer_id, *region_id))
902 .collect::<Vec<_>>();
903 self.deregister_failure_detectors(detecting_regions).await;
904 info!(
905 "Region route not found, removed failover detectors for regions: {:?}",
906 result.region_not_found
907 );
908 }
909 if !result.table_not_found.is_empty() {
910 let detecting_regions = result
911 .table_not_found
912 .iter()
913 .map(|region_id| (from_peer_id, *region_id))
914 .collect::<Vec<_>>();
915 self.deregister_failure_detectors(detecting_regions).await;
916 info!(
917 "Table is not found, removed failover detectors for regions: {:?}",
918 result.table_not_found
919 );
920 }
921 if !result.leader_changed.is_empty() {
922 let detecting_regions = result
923 .leader_changed
924 .iter()
925 .map(|region_id| (from_peer_id, *region_id))
926 .collect::<Vec<_>>();
927 self.deregister_failure_detectors(detecting_regions).await;
928 info!(
929 "Region's leader peer changed, removed failover detectors for regions: {:?}",
930 result.leader_changed
931 );
932 }
933 if !result.peer_conflict.is_empty() {
934 info!(
935 "Region has peer conflict, ignore failover for regions: {:?}",
936 result.peer_conflict
937 );
938 }
939 if !result.submitted.is_empty() {
940 info!(
941 "Failover for regions: {:?}, from_peer: {}, to_peer: {}, procedure_id: {:?}, timeout: {:?}, trigger_reason: {:?}",
942 result.submitted,
943 from_peer_id,
944 to_peer_id,
945 result.procedure_id,
946 timeout,
947 trigger_reason,
948 );
949 }
950
951 Ok(())
952 }
953
954 fn detect_region_failure(&self) -> Vec<(DatanodeId, RegionId)> {
956 self.failure_detector
957 .iter()
958 .filter_map(|e| {
959 if !e.failure_detector().is_available(current_time_millis()) {
966 Some(*e.region_ident())
967 } else {
968 None
969 }
970 })
971 .collect::<Vec<_>>()
972 }
973
974 fn regions(&self) -> HashSet<RegionId> {
976 self.failure_detector
977 .iter()
978 .map(|e| e.region_ident().1)
979 .collect::<HashSet<_>>()
980 }
981
982 fn on_heartbeat_arrived(&self, heartbeat: DatanodeHeartbeat) {
984 for region_id in heartbeat.regions {
985 let detecting_region = (heartbeat.datanode_id, region_id);
986 let mut detector = self
987 .failure_detector
988 .region_failure_detector(detecting_region);
989 detector.heartbeat(heartbeat.timestamp);
990 }
991 }
992
993 fn clear(&self) {
994 self.failure_detector.clear();
995 }
996}
997
998#[cfg(test)]
999pub(crate) mod tests {
1000 use std::assert_matches;
1001 use std::collections::HashMap;
1002 use std::sync::{Arc, Mutex};
1003 use std::time::Duration;
1004
1005 use common_meta::affinity::NoopAffinity;
1006 use common_meta::ddl::RegionFailureDetectorController;
1007 use common_meta::ddl::test_util::{
1008 test_create_logical_table_task, test_create_physical_table_task,
1009 };
1010 use common_meta::key::table_route::{
1011 LogicalTableRouteValue, PhysicalTableRouteValue, TableRouteValue,
1012 };
1013 use common_meta::key::{TableMetadataManager, runtime_switch};
1014 use common_meta::peer::Peer;
1015 use common_meta::rpc::router::{Region, RegionRoute};
1016 use common_meta::test_util::NoopPeerResolver;
1017 use common_telemetry::info;
1018 use common_time::util::current_time_millis;
1019 use rand::Rng;
1020 use store_api::storage::RegionId;
1021 use tokio::sync::mpsc::Sender;
1022 use tokio::sync::oneshot;
1023 use tokio::time::sleep;
1024
1025 use super::RegionSupervisorSelector;
1026 use crate::metasrv::SelectorContext;
1027 use crate::metrics::METRIC_META_HEARTBEAT_DROPPED;
1028 use crate::procedure::region_migration::RegionMigrationTriggerReason;
1029 use crate::procedure::region_migration::manager::{
1030 RegionMigrationManager, SubmitRegionMigrationTaskResult,
1031 };
1032 use crate::procedure::region_migration::test_util::TestingEnv;
1033 use crate::region::supervisor::{
1034 DatanodeHeartbeat, Event, HeartbeatAcceptor, RegionFailureDetectorControl,
1035 RegionSupervisor, RegionSupervisorTicker,
1036 };
1037 use crate::selector::test_utils::{RandomNodeSelector, new_test_selector_context};
1038
1039 pub(crate) fn new_test_supervisor() -> (RegionSupervisor, Sender<Event>) {
1040 new_test_supervisor_with(
1041 new_test_selector_context(),
1042 RegionSupervisorSelector::NaiveSelector(Arc::new(RandomNodeSelector::new(vec![
1043 Peer::empty(1),
1044 ]))),
1045 )
1046 }
1047
1048 pub(crate) fn new_test_supervisor_with(
1049 selector_context: SelectorContext,
1050 selector: RegionSupervisorSelector,
1051 ) -> (RegionSupervisor, Sender<Event>) {
1052 let env = TestingEnv::new();
1053 let context_factory = env.context_factory();
1054 let region_migration_manager = Arc::new(RegionMigrationManager::new(
1055 env.procedure_manager().clone(),
1056 context_factory,
1057 ));
1058 let runtime_switch_manager =
1059 Arc::new(runtime_switch::RuntimeSwitchManager::new(env.kv_backend()));
1060 let peer_resolver = Arc::new(NoopPeerResolver);
1061 let (tx, rx) = RegionSupervisor::channel();
1062 let kv_backend = env.kv_backend();
1063
1064 (
1065 RegionSupervisor::new(
1066 rx,
1067 Default::default(),
1068 selector_context,
1069 selector,
1070 region_migration_manager,
1071 runtime_switch_manager,
1072 peer_resolver,
1073 kv_backend,
1074 ),
1075 tx,
1076 )
1077 }
1078
1079 #[tokio::test]
1080 async fn test_failover_affinity_fallback_and_selector_errors() {
1081 use std::collections::{BTreeSet, HashSet};
1082 use std::sync::atomic::{AtomicUsize, Ordering};
1083
1084 use common_meta::affinity::{
1085 AffinityFallback, AffinityReference, AffinityRequirements, AffinityResolver,
1086 };
1087 use common_meta::error::{self as meta_error, Result as MetaResult};
1088
1089 use crate::error::{self, Result};
1090 use crate::metasrv::{SelectTarget, SelectorContext};
1091 use crate::selector::{RegionStatAwareSelector, Selector, SelectorOptions};
1092
1093 #[derive(Clone, Copy, PartialEq)]
1094 enum Resolution {
1095 Disabled,
1096 Available,
1097 Missing,
1098 Error,
1099 }
1100
1101 struct Resolver {
1102 result: Resolution,
1103 calls: AtomicUsize,
1104 }
1105
1106 #[async_trait::async_trait]
1107 impl AffinityResolver for Resolver {
1108 async fn resolve(
1109 &self,
1110 references: &[AffinityReference],
1111 ) -> MetaResult<Vec<Option<AffinityRequirements>>> {
1112 assert_eq!(references, &[AffinityReference::Datanode(7)]);
1113 self.calls.fetch_add(1, Ordering::Relaxed);
1114 match self.result {
1115 Resolution::Available => {
1116 Ok(vec![AffinityRequirements::new(HashMap::from([(
1117 "pool".into(),
1118 "a".into(),
1119 )]))])
1120 }
1121 Resolution::Disabled => unreachable!(),
1122 Resolution::Missing => Ok(vec![None]),
1123 Resolution::Error => meta_error::UnexpectedSnafu {
1124 err_msg: "source lookup failed",
1125 }
1126 .fail(),
1127 }
1128 }
1129 }
1130
1131 #[derive(Clone, Copy)]
1132 enum Outcome {
1133 Success,
1134 Short,
1135 Exhausted,
1136 Error,
1137 }
1138 struct FailoverSelector {
1139 outcome: Outcome,
1140 expected_fallback: AffinityFallback,
1141 calls: Mutex<Vec<Option<AffinityRequirements>>>,
1142 }
1143 impl FailoverSelector {
1144 fn select_peers(&self, affinity: Option<&AffinityRequirements>) -> Result<Vec<Peer>> {
1145 self.calls.lock().unwrap().push(affinity.cloned());
1146 if affinity.is_some() {
1147 match self.outcome {
1148 Outcome::Success => {}
1149 Outcome::Short => return Ok(vec![Peer::empty(9)]),
1150 Outcome::Exhausted => {
1151 return error::NoEnoughAvailableNodeSnafu {
1152 required: 2usize,
1153 available: 0usize,
1154 select_target: SelectTarget::Datanode,
1155 }
1156 .fail();
1157 }
1158 Outcome::Error => {
1159 return error::UnexpectedSnafu {
1160 violated: "selection backend failed",
1161 }
1162 .fail();
1163 }
1164 }
1165 }
1166 Ok(vec![Peer::empty(9); 2])
1167 }
1168 }
1169 #[async_trait::async_trait]
1170 impl Selector for FailoverSelector {
1171 type Context = SelectorContext;
1172 type Output = Vec<Peer>;
1173 async fn select(
1174 &self,
1175 ctx: &SelectorContext,
1176 opts: SelectorOptions<'_>,
1177 ) -> Result<Self::Output> {
1178 assert_eq!(ctx.affinity_fallback, self.expected_fallback);
1179 assert_eq!(opts.exclude_peer_ids, HashSet::from([8]));
1180 assert!(opts.workload_filter.is_some());
1181 assert!(opts.allow_duplication);
1182 assert_eq!(opts.min_required_items, 2);
1183 self.select_peers(opts.affinity)
1184 }
1185 }
1186 #[async_trait::async_trait]
1187 impl RegionStatAwareSelector for FailoverSelector {
1188 type Context = SelectorContext;
1189 type Output = Vec<(RegionId, Peer)>;
1190 async fn select(
1191 &self,
1192 ctx: &SelectorContext,
1193 source: u64,
1194 regions: &[RegionId],
1195 excluded: HashSet<u64>,
1196 exclude_peer_ids_by_region: &HashMap<RegionId, BTreeSet<u64>>,
1197 affinity: Option<&AffinityRequirements>,
1198 ) -> Result<Self::Output> {
1199 assert_eq!(ctx.affinity_fallback, self.expected_fallback);
1200 assert_eq!(source, 7);
1201 assert_eq!(excluded, HashSet::from([8]));
1202 assert!(exclude_peer_ids_by_region.is_empty());
1203 Ok(regions
1204 .iter()
1205 .copied()
1206 .zip(self.select_peers(affinity)?)
1207 .collect())
1208 }
1209 }
1210 for stat_aware in [false, true] {
1211 for (resolution, outcome, succeeds) in [
1212 (Resolution::Available, Outcome::Success, true),
1213 (Resolution::Available, Outcome::Short, false),
1214 (Resolution::Available, Outcome::Exhausted, false),
1215 (Resolution::Available, Outcome::Error, false),
1216 (Resolution::Missing, Outcome::Success, true),
1217 (Resolution::Error, Outcome::Success, true),
1218 (Resolution::Disabled, Outcome::Success, true),
1219 ] {
1220 let resolver = Arc::new(Resolver {
1221 result: resolution,
1222 calls: AtomicUsize::new(0),
1223 });
1224 let selector = Arc::new(FailoverSelector {
1225 outcome,
1226 expected_fallback: AffinityFallback::Unconstrained,
1227 calls: Mutex::new(vec![]),
1228 });
1229 let (mut supervisor, _sender) = new_test_supervisor();
1230 if resolution != Resolution::Disabled {
1231 supervisor = supervisor.with_affinity_resolver(resolver.clone());
1232 }
1233 supervisor.selector = if stat_aware {
1234 RegionSupervisorSelector::RegionStatAwareSelector(selector.clone())
1235 } else {
1236 RegionSupervisorSelector::NaiveSelector(selector.clone())
1237 };
1238 let regions = [RegionId::new(1, 1), RegionId::new(1, 2)];
1239 let result = supervisor
1241 .select_peers(7, ®ions, &[8], &HashMap::new())
1242 .await;
1243 if succeeds {
1244 assert_eq!(
1245 result.unwrap(),
1246 vec![(regions[0], Peer::empty(9)), (regions[1], Peer::empty(9))]
1247 );
1248 } else {
1249 match outcome {
1250 Outcome::Short => {
1252 assert_eq!(result.unwrap(), vec![(regions[0], Peer::empty(9))])
1253 }
1254 Outcome::Exhausted => assert!(result.unwrap().is_empty()),
1255 Outcome::Error => assert!(matches!(
1256 result.unwrap_err(),
1257 error::Error::Unexpected { .. }
1258 )),
1259 Outcome::Success => unreachable!(),
1260 }
1261 }
1262 assert_eq!(
1263 resolver.calls.load(Ordering::Relaxed),
1264 usize::from(resolution != Resolution::Disabled)
1265 );
1266 let calls = selector.calls.lock().unwrap();
1267 assert_eq!(calls.len(), 1);
1268 if let Some(affinity) = calls.first() {
1269 assert_eq!(affinity.is_some(), resolution == Resolution::Available);
1270 }
1271 }
1272 }
1273 }
1274
1275 fn filler_heartbeat() -> DatanodeHeartbeat {
1276 DatanodeHeartbeat {
1277 datanode_id: 1,
1278 regions: vec![],
1279 timestamp: 0,
1280 }
1281 }
1282
1283 #[tokio::test]
1284 async fn test_select_peers_excludes_unschedulable_datanodes() {
1285 use std::collections::HashSet as StdHashSet;
1286
1287 use crate::key::NodeSchedulingRole;
1288 use crate::selector::{Selector, SelectorOptions};
1289 use crate::test_util::{
1290 create_meta_peer_client, new_node_scheduling_discovery, put_datanodes,
1291 };
1292
1293 struct ExcludeAssertSelector;
1295
1296 #[async_trait::async_trait]
1297 impl Selector for ExcludeAssertSelector {
1298 type Context = SelectorContext;
1299 type Output = Vec<Peer>;
1300
1301 async fn select(
1302 &self,
1303 _ctx: &Self::Context,
1304 opts: SelectorOptions<'_>,
1305 ) -> crate::error::Result<Self::Output> {
1306 assert_eq!(StdHashSet::from([7, 8]), opts.exclude_peer_ids);
1307 Ok(vec![Peer::empty(9)])
1308 }
1309 }
1310
1311 let client = create_meta_peer_client();
1313 put_datanodes(&client, vec![Peer::empty(7)]).await;
1314 let (manager, discovery) = new_node_scheduling_discovery(&client);
1315 manager
1316 .mark_unschedulable(NodeSchedulingRole::Datanode, 7)
1317 .await
1318 .unwrap();
1319
1320 let selector_context = SelectorContext {
1321 affinity_fallback: Default::default(),
1322 affinity: Arc::new(NoopAffinity),
1323 peer_discovery: Arc::new(discovery),
1324 };
1325 let (supervisor, _tx) = new_test_supervisor_with(
1326 selector_context,
1327 RegionSupervisorSelector::NaiveSelector(Arc::new(ExcludeAssertSelector)),
1328 );
1329
1330 let region_peers = supervisor
1331 .select_peers(7, &[RegionId::new(1, 1)], &[8], &HashMap::new())
1332 .await
1333 .unwrap();
1334 assert_eq!(1, region_peers.len());
1335 assert_eq!(Peer::empty(9), region_peers[0].1);
1336 }
1337
1338 #[tokio::test]
1339 async fn test_failover_excludes_route_followers() {
1340 use std::collections::{BTreeSet, HashSet as StdHashSet};
1341
1342 use common_meta::affinity::AffinityRequirements;
1343 use common_meta::key::MetadataKey;
1344 use common_meta::key::table_route::TableRouteKey;
1345 use common_meta::rpc::store::PutRequest;
1346
1347 use crate::error::{self, Result};
1348 use crate::metasrv::SelectTarget;
1349 use crate::selector::{RegionStatAwareSelector, Selector, SelectorOptions};
1350
1351 struct RecordingSelector(Mutex<Vec<(StdHashSet<u64>, usize)>>);
1353
1354 #[async_trait::async_trait]
1355 impl Selector for RecordingSelector {
1356 type Context = SelectorContext;
1357 type Output = Vec<Peer>;
1358
1359 async fn select(
1360 &self,
1361 _ctx: &Self::Context,
1362 opts: SelectorOptions<'_>,
1363 ) -> Result<Self::Output> {
1364 let required = opts.min_required_items;
1365 let no_candidate = opts.exclude_peer_ids.contains(&3);
1366 self.0
1367 .lock()
1368 .unwrap()
1369 .push((opts.exclude_peer_ids, required));
1370 if no_candidate {
1371 return error::NoEnoughAvailableNodeSnafu {
1372 required,
1373 available: 0usize,
1374 select_target: SelectTarget::Datanode,
1375 }
1376 .fail();
1377 }
1378 Ok(vec![Peer::empty(3); required])
1379 }
1380 }
1381
1382 type Batch = (Vec<RegionId>, HashMap<RegionId, BTreeSet<u64>>);
1384
1385 struct StatAwareRecorder(Mutex<Vec<Batch>>);
1387
1388 #[async_trait::async_trait]
1389 impl RegionStatAwareSelector for StatAwareRecorder {
1390 type Context = SelectorContext;
1391 type Output = Vec<(RegionId, Peer)>;
1392
1393 async fn select(
1394 &self,
1395 _ctx: &Self::Context,
1396 _from_peer_id: u64,
1397 region_ids: &[RegionId],
1398 exclude_peer_ids: StdHashSet<u64>,
1399 exclude_peer_ids_by_region: &HashMap<RegionId, BTreeSet<u64>>,
1400 _affinity: Option<&AffinityRequirements>,
1401 ) -> Result<Self::Output> {
1402 assert_eq!(exclude_peer_ids, StdHashSet::from([8]));
1403 self.0
1404 .lock()
1405 .unwrap()
1406 .push((region_ids.to_vec(), exclude_peer_ids_by_region.clone()));
1407 Ok(region_ids
1408 .iter()
1409 .map(|region_id| (*region_id, Peer::empty(9)))
1410 .collect())
1411 }
1412 }
1413
1414 let selector = Arc::new(RecordingSelector(Mutex::new(vec![])));
1415 let (mut supervisor, _tx) = new_test_supervisor_with(
1416 new_test_selector_context(),
1417 RegionSupervisorSelector::NaiveSelector(selector.clone()),
1418 );
1419 let table_id = 1024;
1420 let region_route = |region_number, followers: &[u64]| RegionRoute {
1421 region: Region {
1422 id: RegionId::new(table_id, region_number),
1423 ..Default::default()
1424 },
1425 leader_peer: Some(Peer::empty(8)),
1426 follower_peers: followers.iter().map(|id| Peer::empty(*id)).collect(),
1427 ..Default::default()
1428 };
1429 let table_route = PhysicalTableRouteValue::new(vec![
1430 region_route(0, &[]),
1431 region_route(1, &[3]),
1432 region_route(2, &[4]),
1433 region_route(3, &[4]),
1434 ]);
1435 let mut task = test_create_physical_table_task("my_physical_table");
1436 task.set_table_id(table_id);
1437 TableMetadataManager::new(supervisor.kv_backend.clone())
1438 .create_table_metadata(
1439 task.table_info,
1440 TableRouteValue::Physical(table_route),
1441 HashMap::new(),
1442 )
1443 .await
1444 .unwrap();
1445
1446 let mut regions = (0..4)
1447 .map(|number| RegionId::new(table_id, number))
1448 .collect::<Vec<_>>();
1449 regions.push(RegionId::new(table_id + 1, 0));
1451 let tasks = supervisor
1452 .generate_failover_tasks(8, ®ions, &[8])
1453 .await
1454 .unwrap();
1455
1456 assert_eq!(
1458 *selector.0.lock().unwrap(),
1459 vec![
1460 (StdHashSet::from([8]), 2),
1461 (StdHashSet::from([8, 3]), 1),
1462 (StdHashSet::from([8, 4]), 2),
1463 ]
1464 );
1465 let failed_over = tasks
1467 .iter()
1468 .map(|(task, _)| task.region_id)
1469 .collect::<Vec<_>>();
1470 assert_eq!(
1471 failed_over,
1472 [regions[0], regions[4], regions[2], regions[3]]
1473 );
1474
1475 let stat_aware = Arc::new(StatAwareRecorder(Mutex::new(vec![])));
1477 supervisor.selector = RegionSupervisorSelector::RegionStatAwareSelector(stat_aware.clone());
1478 let tasks = supervisor
1479 .generate_failover_tasks(8, ®ions, &[8])
1480 .await
1481 .unwrap();
1482 let followers = HashMap::from([
1483 (regions[0], BTreeSet::new()),
1484 (regions[1], BTreeSet::from([3])),
1485 (regions[2], BTreeSet::from([4])),
1486 (regions[3], BTreeSet::from([4])),
1487 ]);
1488 assert_eq!(
1489 *stat_aware.0.lock().unwrap(),
1490 vec![(regions.clone(), followers)]
1491 );
1492 let failed_over = tasks
1493 .iter()
1494 .map(|(task, _)| task.region_id)
1495 .collect::<Vec<_>>();
1496 assert_eq!(failed_over, regions);
1497 supervisor.selector = RegionSupervisorSelector::NaiveSelector(selector.clone());
1498
1499 supervisor
1501 .kv_backend
1502 .put(
1503 PutRequest::new()
1504 .with_key(TableRouteKey::new(table_id + 1).to_bytes())
1505 .with_value(b"corrupted".to_vec()),
1506 )
1507 .await
1508 .unwrap();
1509 selector.0.lock().unwrap().clear();
1510 let tasks = supervisor
1511 .generate_failover_tasks(8, ®ions, &[8])
1512 .await
1513 .unwrap();
1514 assert_eq!(
1515 *selector.0.lock().unwrap(),
1516 vec![(StdHashSet::from([8]), 5)]
1517 );
1518 assert_eq!(tasks.len(), 5);
1519 }
1520
1521 #[tokio::test]
1522 async fn test_heartbeat() {
1523 let (mut supervisor, sender) = new_test_supervisor();
1524 tokio::spawn(async move { supervisor.run().await });
1525
1526 sender
1527 .send(Event::HeartbeatArrived(DatanodeHeartbeat {
1528 datanode_id: 0,
1529 regions: vec![RegionId::new(1, 1)],
1530 timestamp: 100,
1531 }))
1532 .await
1533 .unwrap();
1534 let (tx, rx) = oneshot::channel();
1535 sender.send(Event::Dump(tx)).await.unwrap();
1536 let detector = rx.await.unwrap();
1537 assert!(detector.contains(&(0, RegionId::new(1, 1))));
1538
1539 sender.send(Event::Clear).await.unwrap();
1541 let (tx, rx) = oneshot::channel();
1542 sender.send(Event::Dump(tx)).await.unwrap();
1543 assert!(rx.await.unwrap().is_empty());
1544
1545 fn generate_heartbeats(datanode_id: u64, region_ids: Vec<u32>) -> Vec<DatanodeHeartbeat> {
1546 let mut rng = rand::rng();
1547 let start = current_time_millis();
1548 (0..2000)
1549 .map(|i| DatanodeHeartbeat {
1550 timestamp: start + i * 1000 + rng.random_range(0..100),
1551 datanode_id,
1552 regions: region_ids
1553 .iter()
1554 .map(|number| RegionId::new(0, *number))
1555 .collect(),
1556 })
1557 .collect::<Vec<_>>()
1558 }
1559
1560 let heartbeats = generate_heartbeats(100, vec![1, 2, 3]);
1561 let last_heartbeat_time = heartbeats.last().unwrap().timestamp;
1562 for heartbeat in heartbeats {
1563 sender
1564 .send(Event::HeartbeatArrived(heartbeat))
1565 .await
1566 .unwrap();
1567 }
1568
1569 let (tx, rx) = oneshot::channel();
1570 sender.send(Event::Dump(tx)).await.unwrap();
1571 let detector = rx.await.unwrap();
1572 assert_eq!(detector.len(), 3);
1573
1574 for e in detector.iter() {
1575 let fd = e.failure_detector();
1576 let acceptable_heartbeat_pause_millis = fd.acceptable_heartbeat_pause_millis() as i64;
1577 let start = last_heartbeat_time;
1578
1579 for i in 1..=acceptable_heartbeat_pause_millis / 1000 {
1581 let now = start + i * 1000;
1582 assert_eq!(fd.phi(now), 0.0);
1583 }
1584
1585 let now = start + acceptable_heartbeat_pause_millis + 1000;
1588 assert!(fd.phi(now) < fd.threshold() as _);
1589 let now = start + acceptable_heartbeat_pause_millis + 2000;
1590 assert!(fd.phi(now) > fd.threshold() as _);
1591 }
1592 }
1593
1594 #[tokio::test]
1595 async fn test_supervisor_ticker() {
1596 let (tx, mut rx) = tokio::sync::mpsc::channel(128);
1597 let ticker = RegionSupervisorTicker {
1598 tick_handle: Mutex::new(None),
1599 initialization_handle: Mutex::new(None),
1600 tick_interval: Duration::from_millis(10),
1601 initialization_delay: Duration::from_millis(100),
1602 initialization_retry_period: Duration::from_millis(100),
1603 sender: tx,
1604 };
1605 for _ in 0..2 {
1607 ticker.start();
1608 sleep(Duration::from_millis(100)).await;
1609 ticker.stop();
1610 assert!(!rx.is_empty());
1611 while let Ok(event) = rx.try_recv() {
1612 assert_matches!(
1613 event,
1614 Event::Tick | Event::Clear | Event::InitializeAllRegions(_)
1615 );
1616 }
1617 assert!(ticker.initialization_handle.lock().unwrap().is_none());
1618 assert!(ticker.tick_handle.lock().unwrap().is_none());
1619 }
1620 }
1621
1622 #[tokio::test]
1623 async fn test_initialize_all_regions_event_handling() {
1624 common_telemetry::init_default_ut_logging();
1625 let (tx, mut rx) = tokio::sync::mpsc::channel(128);
1626 let ticker = RegionSupervisorTicker {
1627 tick_handle: Mutex::new(None),
1628 initialization_handle: Mutex::new(None),
1629 tick_interval: Duration::from_millis(1000),
1630 initialization_delay: Duration::from_millis(50),
1631 initialization_retry_period: Duration::from_millis(50),
1632 sender: tx,
1633 };
1634 ticker.start();
1635 sleep(Duration::from_millis(60)).await;
1636 let handle = tokio::spawn(async move {
1637 let mut counter = 0;
1638 while let Some(event) = rx.recv().await {
1639 if let Event::InitializeAllRegions(tx) = event {
1640 if counter == 0 {
1641 counter += 1;
1643 continue;
1644 }
1645 tx.send(()).unwrap();
1646 info!("Responded initialize all regions event");
1647 break;
1648 }
1649 }
1650 rx
1651 });
1652
1653 let rx = handle.await.unwrap();
1654 for _ in 0..3 {
1655 sleep(Duration::from_millis(100)).await;
1656 assert!(rx.is_empty());
1657 }
1658 }
1659
1660 #[tokio::test]
1661 async fn test_initialize_all_regions() {
1662 common_telemetry::init_default_ut_logging();
1663 let (mut supervisor, sender) = new_test_supervisor();
1664 let table_metadata_manager = TableMetadataManager::new(supervisor.kv_backend.clone());
1665
1666 let table_id = 1024;
1668 let mut create_physical_table_task = test_create_physical_table_task("my_physical_table");
1669 create_physical_table_task.set_table_id(table_id);
1670 let table_info = create_physical_table_task.table_info;
1671 let table_route = PhysicalTableRouteValue::new(vec![RegionRoute {
1672 region: Region {
1673 id: RegionId::new(table_id, 0),
1674 ..Default::default()
1675 },
1676 leader_peer: Some(Peer::empty(1)),
1677 ..Default::default()
1678 }]);
1679 let table_route_value = TableRouteValue::Physical(table_route);
1680 table_metadata_manager
1681 .create_table_metadata(table_info, table_route_value, HashMap::new())
1682 .await
1683 .unwrap();
1684
1685 let logical_table_id = 1025;
1687 let mut test_create_logical_table_task = test_create_logical_table_task("my_logical_table");
1688 test_create_logical_table_task.set_table_id(logical_table_id);
1689 let table_info = test_create_logical_table_task.table_info;
1690 let table_route = LogicalTableRouteValue::new(1024);
1691 let table_route_value = TableRouteValue::Logical(table_route);
1692 table_metadata_manager
1693 .create_table_metadata(table_info, table_route_value, HashMap::new())
1694 .await
1695 .unwrap();
1696 tokio::spawn(async move { supervisor.run().await });
1697 let (tx, rx) = oneshot::channel();
1698 sender.send(Event::InitializeAllRegions(tx)).await.unwrap();
1699 assert!(rx.await.is_ok());
1700
1701 let (tx, rx) = oneshot::channel();
1702 sender.send(Event::Dump(tx)).await.unwrap();
1703 let detector = rx.await.unwrap();
1704 assert_eq!(detector.len(), 1);
1705 assert!(detector.contains(&(1, RegionId::new(1024, 0))));
1706 }
1707
1708 #[tokio::test]
1709 async fn test_initialize_all_regions_with_maintenance_mode() {
1710 common_telemetry::init_default_ut_logging();
1711 let (mut supervisor, sender) = new_test_supervisor();
1712
1713 supervisor
1714 .runtime_switch_manager
1715 .set_maintenance_mode()
1716 .await
1717 .unwrap();
1718 tokio::spawn(async move { supervisor.run().await });
1719 let (tx, rx) = oneshot::channel();
1720 sender.send(Event::InitializeAllRegions(tx)).await.unwrap();
1721 assert!(rx.await.is_err());
1723 }
1724
1725 #[tokio::test]
1726 async fn test_region_failure_detector_controller() {
1727 let (mut supervisor, sender) = new_test_supervisor();
1728 let controller = RegionFailureDetectorControl::new(sender.clone());
1729 tokio::spawn(async move { supervisor.run().await });
1730 let detecting_region = (1, RegionId::new(1, 1));
1731 controller
1732 .register_failure_detectors(vec![detecting_region])
1733 .await;
1734
1735 let (tx, rx) = oneshot::channel();
1736 sender.send(Event::Dump(tx)).await.unwrap();
1737 let detector = rx.await.unwrap();
1738 let region_detector = detector.region_failure_detector(detecting_region).clone();
1739
1740 controller
1742 .register_failure_detectors(vec![detecting_region])
1743 .await;
1744 let (tx, rx) = oneshot::channel();
1745 sender.send(Event::Dump(tx)).await.unwrap();
1746 let detector = rx.await.unwrap();
1747 let got = detector.region_failure_detector(detecting_region).clone();
1748 assert_eq!(region_detector, got);
1749
1750 controller
1751 .deregister_failure_detectors(vec![detecting_region])
1752 .await;
1753 let (tx, rx) = oneshot::channel();
1754 sender.send(Event::Dump(tx)).await.unwrap();
1755 assert!(rx.await.unwrap().is_empty());
1756 }
1757
1758 #[tokio::test]
1759 async fn test_handle_submit_region_migration_task_result_migrated() {
1760 common_telemetry::init_default_ut_logging();
1761 let (mut supervisor, _) = new_test_supervisor();
1762 let region_id = RegionId::new(1, 1);
1763 let detecting_region = (1, region_id);
1764 supervisor
1765 .register_failure_detectors(vec![detecting_region])
1766 .await;
1767 supervisor.failover_counts.insert(detecting_region, 1);
1768 let result = SubmitRegionMigrationTaskResult {
1769 migrated: vec![region_id],
1770 ..Default::default()
1771 };
1772 supervisor
1773 .handle_submit_region_migration_task_result(
1774 1,
1775 2,
1776 Duration::from_millis(1000),
1777 RegionMigrationTriggerReason::Manual,
1778 result,
1779 )
1780 .await
1781 .unwrap();
1782 assert!(!supervisor.failure_detector.contains(&detecting_region));
1783 assert!(supervisor.failover_counts.is_empty());
1784 }
1785
1786 #[tokio::test]
1787 async fn test_handle_submit_region_migration_task_result_migrating() {
1788 common_telemetry::init_default_ut_logging();
1789 let (mut supervisor, _) = new_test_supervisor();
1790 let region_id = RegionId::new(1, 1);
1791 let detecting_region = (1, region_id);
1792 supervisor
1793 .register_failure_detectors(vec![detecting_region])
1794 .await;
1795 supervisor.failover_counts.insert(detecting_region, 1);
1796 let result = SubmitRegionMigrationTaskResult {
1797 migrating: vec![region_id],
1798 ..Default::default()
1799 };
1800 supervisor
1801 .handle_submit_region_migration_task_result(
1802 1,
1803 2,
1804 Duration::from_millis(1000),
1805 RegionMigrationTriggerReason::Manual,
1806 result,
1807 )
1808 .await
1809 .unwrap();
1810 assert!(supervisor.failure_detector.contains(&detecting_region));
1811 assert!(supervisor.failover_counts.contains_key(&detecting_region));
1812 }
1813
1814 #[tokio::test]
1815 async fn test_handle_submit_region_migration_task_result_table_not_found() {
1816 common_telemetry::init_default_ut_logging();
1817 let (mut supervisor, _) = new_test_supervisor();
1818 let region_id = RegionId::new(1, 1);
1819 let detecting_region = (1, region_id);
1820 supervisor
1821 .register_failure_detectors(vec![detecting_region])
1822 .await;
1823 supervisor.failover_counts.insert(detecting_region, 1);
1824 let result = SubmitRegionMigrationTaskResult {
1825 table_not_found: vec![region_id],
1826 ..Default::default()
1827 };
1828 supervisor
1829 .handle_submit_region_migration_task_result(
1830 1,
1831 2,
1832 Duration::from_millis(1000),
1833 RegionMigrationTriggerReason::Manual,
1834 result,
1835 )
1836 .await
1837 .unwrap();
1838 assert!(!supervisor.failure_detector.contains(&detecting_region));
1839 assert!(supervisor.failover_counts.is_empty());
1840 }
1841
1842 #[tokio::test]
1843 async fn test_handle_submit_region_migration_task_result_region_not_found() {
1844 common_telemetry::init_default_ut_logging();
1845 let (mut supervisor, _) = new_test_supervisor();
1846 let region_id = RegionId::new(1, 1);
1847 let detecting_region = (1, region_id);
1848 supervisor
1849 .register_failure_detectors(vec![detecting_region])
1850 .await;
1851 supervisor.failover_counts.insert(detecting_region, 1);
1852 let result = SubmitRegionMigrationTaskResult {
1853 region_not_found: vec![region_id],
1854 ..Default::default()
1855 };
1856 supervisor
1857 .handle_submit_region_migration_task_result(
1858 1,
1859 2,
1860 Duration::from_millis(1000),
1861 RegionMigrationTriggerReason::Manual,
1862 result,
1863 )
1864 .await
1865 .unwrap();
1866 assert!(!supervisor.failure_detector.contains(&detecting_region));
1867 assert!(supervisor.failover_counts.is_empty());
1868 }
1869
1870 #[tokio::test]
1871 async fn test_handle_submit_region_migration_task_result_leader_changed() {
1872 common_telemetry::init_default_ut_logging();
1873 let (mut supervisor, _) = new_test_supervisor();
1874 let region_id = RegionId::new(1, 1);
1875 let detecting_region = (1, region_id);
1876 supervisor
1877 .register_failure_detectors(vec![detecting_region])
1878 .await;
1879 supervisor.failover_counts.insert(detecting_region, 1);
1880 let result = SubmitRegionMigrationTaskResult {
1881 leader_changed: vec![region_id],
1882 ..Default::default()
1883 };
1884 supervisor
1885 .handle_submit_region_migration_task_result(
1886 1,
1887 2,
1888 Duration::from_millis(1000),
1889 RegionMigrationTriggerReason::Manual,
1890 result,
1891 )
1892 .await
1893 .unwrap();
1894 assert!(!supervisor.failure_detector.contains(&detecting_region));
1895 assert!(supervisor.failover_counts.is_empty());
1896 }
1897
1898 #[tokio::test]
1899 async fn test_handle_submit_region_migration_task_result_peer_conflict() {
1900 common_telemetry::init_default_ut_logging();
1901 let (mut supervisor, _) = new_test_supervisor();
1902 let region_id = RegionId::new(1, 1);
1903 let detecting_region = (1, region_id);
1904 supervisor
1905 .register_failure_detectors(vec![detecting_region])
1906 .await;
1907 supervisor.failover_counts.insert(detecting_region, 1);
1908 let result = SubmitRegionMigrationTaskResult {
1909 peer_conflict: vec![region_id],
1910 ..Default::default()
1911 };
1912 supervisor
1913 .handle_submit_region_migration_task_result(
1914 1,
1915 2,
1916 Duration::from_millis(1000),
1917 RegionMigrationTriggerReason::Manual,
1918 result,
1919 )
1920 .await
1921 .unwrap();
1922 assert!(supervisor.failure_detector.contains(&detecting_region));
1923 assert!(supervisor.failover_counts.contains_key(&detecting_region));
1924 }
1925
1926 #[tokio::test]
1927 async fn test_handle_submit_region_migration_task_result_submitted() {
1928 common_telemetry::init_default_ut_logging();
1929 let (mut supervisor, _) = new_test_supervisor();
1930 let region_id = RegionId::new(1, 1);
1931 let detecting_region = (1, region_id);
1932 supervisor
1933 .register_failure_detectors(vec![detecting_region])
1934 .await;
1935 supervisor.failover_counts.insert(detecting_region, 1);
1936 let result = SubmitRegionMigrationTaskResult {
1937 submitted: vec![region_id],
1938 ..Default::default()
1939 };
1940 supervisor
1941 .handle_submit_region_migration_task_result(
1942 1,
1943 2,
1944 Duration::from_millis(1000),
1945 RegionMigrationTriggerReason::Manual,
1946 result,
1947 )
1948 .await
1949 .unwrap();
1950 assert!(supervisor.failure_detector.contains(&detecting_region));
1951 assert!(supervisor.failover_counts.contains_key(&detecting_region));
1952 }
1953
1954 #[tokio::test]
1955 async fn test_heartbeat_accept_does_not_block_on_full_queue() {
1956 let (mut supervisor, sender) = new_test_supervisor();
1959
1960 let mut queued = 0;
1961 while sender
1962 .try_send(Event::HeartbeatArrived(filler_heartbeat()))
1963 .is_ok()
1964 {
1965 queued += 1;
1966 }
1967 assert_eq!(queued, sender.max_capacity());
1968 assert_eq!(sender.capacity(), 0);
1969
1970 let acceptor = HeartbeatAcceptor::new(sender.clone());
1971 let dropped = || {
1972 METRIC_META_HEARTBEAT_DROPPED
1973 .with_label_values(&["full"])
1974 .get()
1975 };
1976 let dropped_before = dropped();
1977 acceptor.accept(filler_heartbeat());
1979 assert_eq!(dropped(), dropped_before + 1);
1980 assert_eq!(sender.capacity(), 0);
1981
1982 supervisor.receiver.try_recv().unwrap();
1984 acceptor.accept(filler_heartbeat());
1985 assert_eq!(dropped(), dropped_before + 1);
1986 assert_eq!(sender.capacity(), 0);
1987 }
1988}