Skip to main content

meta_srv/region/
supervisor.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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/// `DatanodeHeartbeat` represents the heartbeat signal sent from a datanode.
62/// It includes identifiers for the cluster and datanode, a list of regions being monitored,
63/// and a timestamp indicating when the heartbeat was sent.
64#[derive(Debug)]
65pub(crate) struct DatanodeHeartbeat {
66    datanode_id: DatanodeId,
67    // TODO(weny): Considers collecting the memtable size in regions.
68    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
82/// `Event` represents various types of events that can be processed by the region supervisor.
83/// These events are crucial for managing state transitions and handling specific scenarios
84/// in the region lifecycle.
85///
86/// Variants:
87/// - `Tick`: This event is used to trigger region failure detection periodically.
88/// - `InitializeAllRegions`: This event is used to initialize all region failure detectors.
89/// - `RegisterFailureDetectors`: This event is used to register failure detectors for regions.
90/// - `ResetFailureDetectors`: This event is used to reset failure detectors for regions.
91/// - `DeregisterFailureDetectors`: This event is used to deregister failure detectors for regions.
92/// - `HeartbeatArrived`: This event presents the metasrv received [`DatanodeHeartbeat`] from the datanodes.
93/// - `Clear`: This event is used to reset the state of the supervisor, typically used
94///   when a system-wide reset or reinitialization is needed.
95/// - `Dump`: (Available only in test) This event triggers a dump of the
96///   current state for debugging purposes. It allows developers to inspect the internal state
97///   of the supervisor during tests.
98pub(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/// A background job to generate [`Event::Tick`] type events.
137#[derive(Debug)]
138pub struct RegionSupervisorTicker {
139    /// The [`Option`] wrapper allows us to abort the job while dropping the [`RegionSupervisor`].
140    tick_handle: Mutex<Option<JoinHandle<()>>>,
141
142    /// The [`Option`] wrapper allows us to abort the job while dropping the [`RegionSupervisor`].
143    initialization_handle: Mutex<Option<JoinHandle<()>>>,
144
145    /// The interval of tick.
146    tick_interval: Duration,
147
148    /// The delay before initializing all region failure detectors.
149    initialization_delay: Duration,
150
151    /// The retry period for initializing all region failure detectors.
152    initialization_retry_period: Duration,
153
154    /// Sends [Event]s.
155    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    /// Starts the ticker.
197    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    /// Stops the ticker.
249    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
271/// The default tick interval.
272pub const DEFAULT_TICK_INTERVAL: Duration = Duration::from_secs(1);
273/// The default initialization retry period.
274pub const DEFAULT_INITIALIZATION_RETRY_PERIOD: Duration = Duration::from_secs(60);
275
276/// Selector for region supervisor.
277pub enum RegionSupervisorSelector {
278    NaiveSelector(SelectorRef),
279    RegionStatAwareSelector(RegionStatAwareSelectorRef),
280}
281
282/// The [`RegionSupervisor`] is used to detect Region failures
283/// and initiate Region failover upon detection, ensuring uninterrupted region service.
284pub struct RegionSupervisor {
285    /// Used to detect the failure of regions.
286    failure_detector: RegionFailureDetector,
287    /// Tracks the number of failovers for each region.
288    failover_counts: HashMap<DetectingRegion, u32>,
289    /// Receives [Event]s.
290    receiver: Receiver<Event>,
291    /// The context of [`SelectorRef`]
292    selector_context: SelectorContext,
293    /// Candidate node selector.
294    selector: RegionSupervisorSelector,
295    /// Resolves the failed node attributes before selection.
296    affinity_resolver: Option<AffinityResolverRef>,
297    /// Region migration manager.
298    region_migration_manager: RegionMigrationManagerRef,
299    /// The maintenance mode manager.
300    runtime_switch_manager: RuntimeSwitchManagerRef,
301    /// Peer resolver
302    peer_resolver: PeerResolverRef,
303    /// The kv backend.
304    kv_backend: KvBackendRef,
305    /// The meta state, used to check if the current metasrv is the leader.
306    state: Option<StateRef>,
307}
308
309/// Controller for managing failure detectors for regions.
310#[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/// [`HeartbeatAcceptor`] forwards heartbeats to [`RegionSupervisor`].
355#[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    /// Accepts heartbeats from datanodes without waiting for queue capacity: a full queue
366    /// drops the heartbeat, counted in [`METRIC_META_HEARTBEAT_DROPPED`]. A dropped
367    /// heartbeat costs the failure detector one sample.
368    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    /// Returns a mpsc channel with a buffer capacity of 1024 for sending and receiving `Event` messages.
394    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            // Failover prioritizes recovery over affinity, independently of DDL policy.
414            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    /// Sets the resolver used to prepare failover affinity requirements.
429    pub(crate) fn with_affinity_resolver(mut self, resolver: AffinityResolverRef) -> Self {
430        self.affinity_resolver = Some(resolver);
431        self
432    }
433
434    /// Sets the meta state.
435    pub(crate) fn with_state(mut self, state: StateRef) -> Self {
436        self.state = Some(state);
437        self
438    }
439
440    /// Runs the main loop.
441    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                        // Ignore the error.
473                        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(&region_route.region.id)
535                        && let Some(leader_peer) = &region_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            // The corresponding region has `acceptable_heartbeat_pause_millis` to send heartbeat from datanode.
560            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(&region);
576            self.failover_counts.remove(&region);
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        // Extracts regions that are migrating(failover), which means they are already being triggered failover.
600        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            // If all detected regions are failover or migrating, just return.
614            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            // We can't use `grouped_regions.keys().cloned().collect::<Vec<_>>()` here
632            // because there may be false positives in failure detection on the datanode.
633            // So we only consider the datanode that reports the failure.
634            let failed_datanodes = [datanode_id];
635            match self
636                .generate_failover_tasks(datanode_id, &regions, &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    /// Selects a target for each of `regions`, excluding `failure_datanodes`, the unschedulable
672    /// datanodes and the region's datanodes in `exclude_peer_ids_by_region`. Regions without a
673    /// candidate are left out.
674    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                // `Selector` takes one exclusion set, so select once per distinct set of excluded
727                // datanodes.
728                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                        // Other groups may still have candidates.
748                        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        // A region's route followers can't take it over.
801        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                // Exclude no followers; the submit still rejects a follower as the target.
811                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    /// Detects the failure of regions.
955    fn detect_region_failure(&self) -> Vec<(DatanodeId, RegionId)> {
956        self.failure_detector
957            .iter()
958            .filter_map(|e| {
959                // Intentionally not place `current_time_millis()` out of the iteration.
960                // The failure detection determination should be happened "just in time",
961                // i.e., failed or not has to be compared with the most recent "now".
962                // Besides, it might reduce the false positive of failure detection,
963                // because during the iteration, heartbeats are coming in as usual,
964                // and the `phi`s are still updating.
965                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    /// Returns all regions that registered in the failure detector.
975    fn regions(&self) -> HashSet<RegionId> {
976        self.failure_detector
977            .iter()
978            .map(|e| e.region_ident().1)
979            .collect::<HashSet<_>>()
980    }
981
982    /// Updates the state of corresponding failure detectors.
983    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                // Preserve the caller's exclusion set without adding the source.
1240                let result = supervisor
1241                    .select_peers(7, &regions, &[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                        // Only the regions with a candidate are placed.
1251                        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        /// Selector that records the excluded peers and returns a fixed peer.
1294        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        // Datanode 7 is marked unschedulable; datanode 8 failed.
1312        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        /// Records each selection, and has no candidate once datanode 3 is excluded.
1352        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        /// A batch of regions with their excluded peers.
1383        type Batch = (Vec<RegionId>, HashMap<RegionId, BTreeSet<u64>>);
1384
1385        /// Records each batch, and places every region on datanode 9, which no region excludes.
1386        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        // A region of a table without a route has no followers.
1450        regions.push(RegionId::new(table_id + 1, 0));
1451        let tasks = supervisor
1452            .generate_failover_tasks(8, &regions, &[8])
1453            .await
1454            .unwrap();
1455
1456        // Regions with the same followers are selected together, excluding those followers.
1457        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        // Only the region whose follower is the last candidate is skipped.
1466        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        // The stat-aware selector gets the whole batch with each region's followers.
1476        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, &regions, &[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        // A route that can't be read excludes no followers.
1500        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, &regions, &[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        // Clear up
1540        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            // Within the "acceptable_heartbeat_pause_millis" period, phi is zero ...
1580            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            // ... then in less than two seconds, phi is above the threshold.
1586            // The same effect can be seen in the diagrams in Akka's document.
1587            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        // It's ok if we start the ticker again.
1606        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                        // Ignore the first event
1642                        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        // Create a physical table metadata
1667        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        // Create a logical table metadata
1686        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        // The sender is dropped, so the receiver will receive an error.
1722        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        // Registers failure detector again
1741        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        // Regression test for #9419: a full queue used to park the caller.
1957        // The supervisor is never run, so the queue stays full.
1958        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        // The full queue drops the heartbeat and counts it.
1978        acceptor.accept(filler_heartbeat());
1979        assert_eq!(dropped(), dropped_before + 1);
1980        assert_eq!(sender.capacity(), 0);
1981
1982        // Free one slot: the next heartbeat is accepted.
1983        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}