Skip to main content

datanode/
alive_keeper.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::HashMap;
16use std::sync::Arc;
17use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
18
19use api::v1::meta::GrantedRegion;
20use async_trait::async_trait;
21use common_error::ext::ErrorExt;
22use common_error::status_code::StatusCode;
23use common_meta::error::InvalidProtoMsgSnafu;
24use common_meta::heartbeat::handler::{
25    HandleControl, HeartbeatResponseHandler, HeartbeatResponseHandlerContext,
26};
27use common_telemetry::{debug, error, info, trace, warn};
28use snafu::OptionExt;
29use store_api::region_engine::{RegionRole, SettableRegionRoleState};
30use store_api::region_request::{RegionCloseRequest, RegionRequest};
31use store_api::storage::RegionId;
32#[cfg(test)]
33use tokio::sync::oneshot;
34use tokio::sync::{Mutex, mpsc};
35use tokio::task::JoinHandle;
36use tokio::time::{Duration, Instant};
37
38use crate::error::{self, Result};
39use crate::event_listener::{RegionServerEvent, RegionServerEventReceiver};
40use crate::region_server::RegionServer;
41
42/// [RegionAliveKeeper] manages all [CountdownTaskHandle]s.
43///
44/// [RegionAliveKeeper] starts a [CountdownTask] for each region. When the deadline is reached,
45/// the status of region be set to "readonly", ensures there is no side-effect in the entity system.
46///
47/// The deadline is controlled by the meta server. Datanode will send its opened regions info to meta sever
48/// via heartbeat. If the meta server decides some region could be resided in this Datanode,
49/// it will renew the lease of region, a deadline of [CountdownTask] will be reset.
50pub struct RegionAliveKeeper {
51    region_server: RegionServer,
52    tasks: Arc<Mutex<HashMap<RegionId, Arc<CountdownTaskHandle>>>>,
53    heartbeat_interval_millis: Arc<AtomicU64>,
54    started: Arc<AtomicBool>,
55
56    /// The epoch when [RegionAliveKeeper] is created. It's used to get a monotonically non-decreasing
57    /// elapsed time when submitting heartbeats to the meta server (because [Instant] is monotonically
58    /// non-decreasing). The heartbeat requests will carry the duration since this epoch, and the
59    /// duration acts like an "invariant point" for region's keep alive lease.
60    epoch: Instant,
61
62    countdown_task_handler_ext: Option<CountdownTaskHandlerExtRef>,
63}
64
65impl RegionAliveKeeper {
66    /// Returns an empty [RegionAliveKeeper].
67    pub fn new(
68        region_server: RegionServer,
69        countdown_task_handler_ext: Option<CountdownTaskHandlerExtRef>,
70        heartbeat_interval: Duration,
71    ) -> Self {
72        Self {
73            region_server,
74            tasks: Arc::new(Mutex::new(HashMap::new())),
75            heartbeat_interval_millis: Arc::new(AtomicU64::new(
76                heartbeat_interval.as_millis() as u64
77            )),
78            started: Arc::new(AtomicBool::new(false)),
79            epoch: Instant::now(),
80            countdown_task_handler_ext,
81        }
82    }
83
84    /// Update the heartbeat interval with the value received from Metasrv.
85    pub fn update_heartbeat_interval(&self, heartbeat_interval_millis: u64) {
86        self.heartbeat_interval_millis
87            .store(heartbeat_interval_millis, Ordering::Relaxed);
88    }
89
90    async fn find_handle(&self, region_id: RegionId) -> Option<Arc<CountdownTaskHandle>> {
91        self.tasks.lock().await.get(&region_id).cloned()
92    }
93
94    /// Add the countdown task for a specific region.
95    /// It will be ignored if the task exists.
96    pub async fn register_region(&self, region_id: RegionId) {
97        let handle = Arc::new(CountdownTaskHandle::new(
98            self.region_server.clone(),
99            self.countdown_task_handler_ext.clone(),
100            region_id,
101        ));
102
103        let should_start = {
104            let mut handles = self.tasks.lock().await;
105
106            // Check if already exists, return early if so
107            if handles.contains_key(&region_id) {
108                return;
109            }
110
111            // Insert new handle
112            handles.insert(region_id, handle.clone());
113
114            // Return whether we should start (check state inside lock)
115            self.started.load(Ordering::Relaxed)
116        };
117
118        if should_start {
119            handle
120                .start(self.heartbeat_interval_millis.load(Ordering::Relaxed))
121                .await;
122            info!("Region alive countdown for region {region_id} is started!");
123        } else {
124            info!(
125                "Region alive countdown for region {region_id} is registered but not started yet!"
126            );
127        }
128    }
129
130    /// Removes the countdown task for a specific region.
131    pub async fn deregister_region(&self, region_id: RegionId) {
132        if self.tasks.lock().await.remove(&region_id).is_some() {
133            info!("Deregister alive countdown for region {region_id}")
134        }
135    }
136
137    /// Renews the lease of regions to `deadline`, granted for a heartbeat sent at `sent_at`.
138    async fn renew_region_leases(
139        &self,
140        regions: &[GrantedRegion],
141        sent_at: Instant,
142        deadline: Instant,
143    ) {
144        for region in regions {
145            let (role, region_id) = (region.role().into(), RegionId::from(region.region_id));
146            if let Some(handle) = self.find_handle(region_id).await {
147                handle
148                    .reset_deadline(role, sent_at, deadline, region.extensions.clone())
149                    .await;
150            } else {
151                warn!(
152                    "Trying to renew the lease for region {region_id}, the keeper handler is not found!"
153                );
154                // Else the region alive keeper might be triggered by lagging messages, we can safely ignore it.
155            }
156        }
157    }
158
159    async fn close_staled_region(&self, region_id: RegionId) {
160        info!("Closing staled region: {region_id}");
161        let request = RegionRequest::Close(RegionCloseRequest::default());
162        if let Err(e) = self.region_server.handle_request(region_id, request).await
163            && e.status_code() != StatusCode::RegionNotFound
164        {
165            let _ = self
166                .region_server
167                .set_region_role(region_id, RegionRole::Follower);
168            error!(e; "Failed to close staled region {}, convert region to follower.", region_id);
169        }
170    }
171
172    /// Closes staled regions.
173    async fn close_staled_regions(&self, regions: &[u64]) {
174        for region_id in regions {
175            self.close_staled_region(RegionId::from_u64(*region_id))
176                .await;
177        }
178    }
179
180    #[cfg(test)]
181    async fn deadline(&self, region_id: RegionId) -> Option<Instant> {
182        let mut deadline = None;
183        if let Some(handle) = self.find_handle(region_id).await {
184            let (s, r) = oneshot::channel();
185            if handle.tx.send(CountdownCommand::Deadline(s)).await.is_ok() {
186                deadline = r.await.ok()
187            }
188        }
189        deadline
190    }
191
192    pub async fn start(
193        self: &Arc<Self>,
194        event_receiver: Option<RegionServerEventReceiver>,
195    ) -> Result<()> {
196        self.started.store(true, Ordering::Relaxed);
197
198        if let Some(mut event_receiver) = event_receiver {
199            let keeper = self.clone();
200            // Initializers region alive keeper.
201            // It makes sure all opened regions are registered to `RegionAliveKeeper.`
202            loop {
203                match event_receiver.0.try_recv() {
204                    Ok(RegionServerEvent::Registered(region_id)) => {
205                        keeper.register_region(region_id).await;
206                    }
207                    Ok(RegionServerEvent::Deregistered(region_id)) => {
208                        keeper.deregister_region(region_id).await;
209                    }
210                    Err(mpsc::error::TryRecvError::Disconnected) => {
211                        return error::UnexpectedSnafu {
212                            violated: "RegionServerEventSender closed",
213                        }
214                        .fail();
215                    }
216                    Err(mpsc::error::TryRecvError::Empty) => {
217                        break;
218                    }
219                }
220            }
221            let running = self.started.clone();
222
223            // Watches changes
224            common_runtime::spawn_global(async move {
225                loop {
226                    if !running.load(Ordering::Relaxed) {
227                        info!("RegionAliveKeeper stopped! Quits the watch loop!");
228                        break;
229                    }
230
231                    match event_receiver.0.recv().await {
232                        Some(RegionServerEvent::Registered(region_id)) => {
233                            keeper.register_region(region_id).await;
234                        }
235                        Some(RegionServerEvent::Deregistered(region_id)) => {
236                            keeper.deregister_region(region_id).await;
237                        }
238                        None => {
239                            info!("RegionServerEventSender closed! Quits the watch loop!");
240                            break;
241                        }
242                    }
243                }
244            });
245        }
246
247        let tasks = self.tasks.lock().await;
248        let interval = self.heartbeat_interval_millis.load(Ordering::Relaxed);
249        for task in tasks.values() {
250            task.start(interval).await;
251        }
252
253        info!(
254            "RegionAliveKeeper is started with region {:?}",
255            tasks.keys().map(|x| x.to_string()).collect::<Vec<_>>(),
256        );
257
258        Ok(())
259    }
260
261    pub fn epoch(&self) -> Instant {
262        self.epoch
263    }
264}
265
266#[async_trait]
267impl HeartbeatResponseHandler for RegionAliveKeeper {
268    fn is_acceptable(&self, ctx: &HeartbeatResponseHandlerContext) -> bool {
269        ctx.response.region_lease.is_some()
270    }
271
272    async fn handle(
273        &self,
274        ctx: &mut HeartbeatResponseHandlerContext,
275    ) -> common_meta::error::Result<HandleControl> {
276        let region_lease = ctx
277            .response
278            .region_lease
279            .as_ref()
280            .context(InvalidProtoMsgSnafu {
281                err_msg: "'region_lease' is missing in heartbeat response",
282            })?;
283        let start_instant = self.epoch + Duration::from_millis(region_lease.duration_since_epoch);
284        let deadline = start_instant + Duration::from_secs(region_lease.lease_seconds);
285
286        if let Some(mito) = self.region_server.mito_engine() {
287            mito.renew_node_lease(deadline.into_std());
288        }
289        self.renew_region_leases(&region_lease.regions, start_instant, deadline)
290            .await;
291        self.close_staled_regions(&region_lease.closeable_region_ids)
292            .await;
293
294        Ok(HandleControl::Continue)
295    }
296}
297
298#[derive(Debug)]
299enum CountdownCommand {
300    /// Start this countdown task. The first deadline will be set to
301    /// 4 * `heartbeat_interval_millis`
302    Start(u64),
303    /// Reset countdown deadline to the given instance.
304    /// (NextRole, HeartbeatSentAt, Deadline, ExtensionInfo)
305    Reset((RegionRole, Instant, Instant, HashMap<String, Vec<u8>>)),
306    /// Returns the current deadline of the countdown task.
307    #[cfg(test)]
308    Deadline(oneshot::Sender<Instant>),
309}
310
311pub type CountdownTaskHandlerExtRef = Arc<dyn CountdownTaskHandlerExt>;
312
313/// Extension trait for [CountdownTaskHandlerExt] to reset deadline of a region.
314#[async_trait]
315pub trait CountdownTaskHandlerExt: Send + Sync {
316    async fn reset_deadline(
317        &self,
318        region_server: &RegionServer,
319        region_id: RegionId,
320        role: RegionRole,
321        deadline: Instant,
322        extension_info: HashMap<String, Vec<u8>>,
323    );
324}
325
326struct CountdownTaskHandle {
327    tx: mpsc::Sender<CountdownCommand>,
328    handler: JoinHandle<()>,
329    region_id: RegionId,
330}
331
332impl CountdownTaskHandle {
333    /// Creates a new [CountdownTaskHandle] and starts the countdown task.
334    fn new(
335        region_server: RegionServer,
336        handler_ext: Option<CountdownTaskHandlerExtRef>,
337        region_id: RegionId,
338    ) -> Self {
339        let (tx, rx) = mpsc::channel(1024);
340
341        let mut countdown_task = CountdownTask {
342            region_server,
343            handler_ext,
344            region_id,
345            rx,
346        };
347        let handler = common_runtime::spawn_hb(async move {
348            countdown_task.run().await;
349        });
350
351        Self {
352            tx,
353            handler,
354            region_id,
355        }
356    }
357
358    /// Starts the [CountdownTask],
359    /// it will be ignored if the task started.
360    async fn start(&self, heartbeat_interval_millis: u64) {
361        if let Err(e) = self
362            .tx
363            .send(CountdownCommand::Start(heartbeat_interval_millis))
364            .await
365        {
366            warn!(
367                "Failed to start region alive keeper countdown: {e}. \
368                Maybe the task is stopped due to region been closed."
369            );
370        }
371    }
372
373    #[cfg(test)]
374    async fn deadline(&self) -> Option<Instant> {
375        let (tx, rx) = oneshot::channel();
376        if self.tx.send(CountdownCommand::Deadline(tx)).await.is_ok() {
377            return rx.await.ok();
378        }
379        None
380    }
381
382    async fn reset_deadline(
383        &self,
384        role: RegionRole,
385        sent_at: Instant,
386        deadline: Instant,
387        extension_info: HashMap<String, Vec<u8>>,
388    ) {
389        if let Err(e) = self
390            .tx
391            .send(CountdownCommand::Reset((
392                role,
393                sent_at,
394                deadline,
395                extension_info,
396            )))
397            .await
398        {
399            warn!(
400                "Failed to reset region alive keeper deadline: {e}. \
401                Maybe the task is stopped due to region been closed."
402            );
403        }
404    }
405}
406
407impl Drop for CountdownTaskHandle {
408    fn drop(&mut self) {
409        debug!(
410            "Aborting region alive countdown task for region {}",
411            self.region_id
412        );
413        self.handler.abort();
414    }
415}
416
417struct CountdownTask {
418    region_server: RegionServer,
419    region_id: RegionId,
420    handler_ext: Option<CountdownTaskHandlerExtRef>,
421    rx: mpsc::Receiver<CountdownCommand>,
422}
423
424impl CountdownTask {
425    async fn run(&mut self) {
426        // 30 years. See `Instant::far_future`.
427        let far_future = Instant::now() + Duration::from_secs(86400 * 365 * 30);
428
429        // Make sure the alive countdown is not gonna happen before heartbeat task is started (the
430        // "start countdown" command will be sent from heartbeat task).
431        let countdown = tokio::time::sleep_until(far_future);
432        tokio::pin!(countdown);
433        let region_id = self.region_id;
434
435        let mut started = false;
436        loop {
437            tokio::select! {
438                command = self.rx.recv() => {
439                    match command {
440                        Some(CountdownCommand::Start(heartbeat_interval_millis)) => {
441                            if !started {
442                                // Set first deadline in 4 heartbeats (roughly after 12 seconds from now if heartbeat
443                                // interval is set to default 3 seconds), to make Datanode and Metasrv more tolerable to
444                                // network or other jitters during startup.
445                                let first_deadline = Instant::now() + Duration::from_millis(heartbeat_interval_millis) * 4;
446                                countdown.set(tokio::time::sleep_until(first_deadline));
447                                started = true;
448                            }
449                        },
450                        Some(CountdownCommand::Reset((role, sent_at, deadline, extension_info))) => {
451                            let prev_role_leader = self.region_server.is_region_leader(self.region_id).unwrap_or(false);
452                            let applied = match self.region_server.set_region_role_with_lease(self.region_id, role, sent_at.into_std(), deadline.into_std()) {
453                                Ok(applied) => applied,
454                                Err(err) => {
455                                    if err.status_code() == StatusCode::RegionNotFound {
456                                        // Table metadata in metasrv is deleted after its regions are dropped.
457                                        // The datanode may still receive lease renewal responses that depend on the metadata
458                                        // during the short period before it is removed.
459                                        warn!(err; "Failed to set region role to {role} for region {region_id}");
460                                    } else {
461                                        error!(err; "Failed to set region role to {role} for region {region_id}");
462                                    }
463                                    false
464                                }
465                            };
466
467                            // Finalize leadership if the grant was applied: persist backfilled metadata.
468                            if applied && !prev_role_leader && role == RegionRole::Leader
469                                && let Err(err) = self
470                                    .region_server
471                                    .set_region_role_state_gracefully(self.region_id, SettableRegionRoleState::Leader)
472                                    .await
473                            {
474                                error!(err; "Failed to set region role state gracefully to {role} for region {region_id}");
475                            }
476
477                            if let Some(ext_handler) = self.handler_ext.as_ref() {
478                                ext_handler.reset_deadline(
479                                    &self.region_server,
480                                    self.region_id,
481                                    role,
482                                    deadline,
483                                    extension_info,
484                                ).await;
485                            }
486                            trace!(
487                                "Reset deadline of region {region_id} to approximately {} seconds later.",
488                                (deadline - Instant::now()).as_secs_f32(),
489                            );
490                            countdown.set(tokio::time::sleep_until(deadline));
491                        },
492                        None => {
493                            info!(
494                                "The handle of countdown task for region {region_id}\
495                                is dropped, RegionAliveKeeper out."
496                            );
497                            break;
498                        },
499                        #[cfg(test)]
500                        Some(CountdownCommand::Deadline(tx)) => {
501                            let _ = tx.send(countdown.deadline());
502                        }
503                    }
504                }
505                () = &mut countdown => {
506                    warn!("The region {region_id} lease is expired, convert region to follower.");
507                    if let Err(err) = self.region_server.set_region_role(self.region_id, RegionRole::Follower) {
508                        error!(err; "Failed to set region role to follower for region {region_id}");
509                    }
510                    // resets the countdown.
511                    let far_future = Instant::now() + Duration::from_secs(86400 * 365 * 30);
512                    countdown.as_mut().reset(far_future);
513                }
514            }
515        }
516    }
517}
518
519#[cfg(test)]
520mod test {
521
522    use std::sync::atomic::AtomicUsize;
523
524    use api::v1::Rows;
525    use api::v1::meta::{HeartbeatResponse, RegionLease};
526    use common_meta::heartbeat::mailbox::HeartbeatMailbox;
527    use mito2::config::MitoConfig;
528    use mito2::engine::MITO_ENGINE_NAME;
529    use mito2::test_util::{CreateRequestBuilder, TestEnv, build_rows, rows_schema};
530    use store_api::region_engine::{
531        RegionEngine, SetRegionRoleStateResponse, SetRegionRoleStateSuccess,
532    };
533    use store_api::region_request::{
534        EnterStagingRequest, RegionPutRequest, StagingPartitionDirective,
535    };
536
537    use super::*;
538    use crate::tests::{MockRegionEngine, mock_region_server};
539
540    /// Handles a heartbeat response that grants `regions` for a heartbeat sent now.
541    async fn handle_lease_response(alive_keeper: &RegionAliveKeeper, regions: Vec<GrantedRegion>) {
542        let sent_at = alive_keeper.epoch().elapsed();
543        handle_lease_response_sent_at(alive_keeper, sent_at, regions).await;
544    }
545
546    /// Handles a heartbeat response that grants `regions` for a heartbeat sent at `sent_at`
547    /// after the epoch.
548    async fn handle_lease_response_sent_at(
549        alive_keeper: &RegionAliveKeeper,
550        sent_at: Duration,
551        regions: Vec<GrantedRegion>,
552    ) {
553        let (tx, _rx) = mpsc::channel(1);
554        let response = HeartbeatResponse {
555            region_lease: Some(RegionLease {
556                regions,
557                duration_since_epoch: sent_at.as_millis() as u64,
558                // The countdown of a granted region does not expire during the test.
559                lease_seconds: 86400,
560                closeable_region_ids: vec![],
561            }),
562            ..Default::default()
563        };
564        let mut ctx =
565            HeartbeatResponseHandlerContext::new(Arc::new(HeartbeatMailbox::new(tx)), response);
566        alive_keeper.handle(&mut ctx).await.unwrap();
567    }
568
569    #[tokio::test(flavor = "multi_thread")]
570    async fn ignored_leader_grant_does_not_finalize_leadership() {
571        common_telemetry::init_default_ut_logging();
572        let region_server = mock_region_server();
573        let finalized = Arc::new(AtomicUsize::new(0));
574        let (engine, _) = MockRegionEngine::with_custom_apply_fn(MITO_ENGINE_NAME, |engine| {
575            // The engine ignores the grant, so the region stays a follower.
576            engine.mock_role = Some(Some(RegionRole::Follower));
577            let finalized = finalized.clone();
578            engine.handle_set_readonly_gracefully_mock_fn = Some(Box::new(move |_| {
579                finalized.fetch_add(1, Ordering::Relaxed);
580                Ok(SetRegionRoleStateResponse::success(
581                    SetRegionRoleStateSuccess::mito(0),
582                ))
583            }));
584        });
585        let region_id = RegionId::new(1024, 1);
586        region_server.register_test_region(region_id, engine);
587        let alive_keeper =
588            RegionAliveKeeper::new(region_server.clone(), None, Duration::from_millis(100));
589        alive_keeper.register_region(region_id).await;
590        let grant = GrantedRegion {
591            region_id: region_id.as_u64(),
592            role: api::v1::meta::RegionRole::Leader.into(),
593            extensions: HashMap::new(),
594        };
595        handle_lease_response(&alive_keeper, vec![grant]).await;
596        // The countdown task has handled the grant once it answers.
597        alive_keeper.deadline(region_id).await.unwrap();
598        assert_eq!(0, finalized.load(Ordering::Relaxed));
599    }
600
601    #[tokio::test(flavor = "multi_thread")]
602    async fn lease_responses_let_regions_write() {
603        common_telemetry::init_default_ut_logging();
604        let mut region_server = mock_region_server();
605        let mut engine_env = TestEnv::with_prefix("region-alive-keeper-write-lease").await;
606        let engine = engine_env.create_engine(MitoConfig::default()).await;
607        engine.require_write_lease();
608        // The clock of the lease checks stands still, so only the lease propagation matters.
609        engine.freeze_lease_clock(std::time::Instant::now());
610        region_server.register_engine(Arc::new(engine));
611        let alive_keeper =
612            RegionAliveKeeper::new(region_server.clone(), None, Duration::from_millis(100));
613        let create = |region_id| {
614            let request = RegionRequest::Create(CreateRequestBuilder::new().build());
615            region_server.handle_request(region_id, request)
616        };
617        let schema = rows_schema(&CreateRequestBuilder::new().build());
618        let put = |region_id| {
619            let rows = Rows {
620                schema: schema.clone(),
621                rows: build_rows(0, 1),
622            };
623            let request = RegionRequest::Put(RegionPutRequest {
624                rows,
625                hint: None,
626                partition_expr_version: None,
627                skip_wal: false,
628            });
629            region_server.handle_request(region_id, request)
630        };
631
632        // Created before any heartbeat response, the region has no lease.
633        let region_id = RegionId::new(1024, 1);
634        create(region_id).await.unwrap();
635        assert!(put(region_id).await.unwrap_err().is_retryable());
636
637        // A response renews the lease of the node, under which regions created later write.
638        handle_lease_response(&alive_keeper, vec![]).await;
639        let created_region_id = RegionId::new(1024, 2);
640        create(created_region_id).await.unwrap();
641        put(created_region_id).await.unwrap();
642
643        // A Leader grant renews the lease of the region.
644        alive_keeper.register_region(region_id).await;
645        let grant = GrantedRegion {
646            region_id: region_id.as_u64(),
647            role: api::v1::meta::RegionRole::Leader.into(),
648            extensions: HashMap::new(),
649        };
650        handle_lease_response(&alive_keeper, vec![grant.clone()]).await;
651        // The countdown task has applied the grant once it answers.
652        alive_keeper.deadline(region_id).await.unwrap();
653        put(region_id).await.unwrap();
654
655        // A grant for a heartbeat sent before a downgrade does not make the region a leader again.
656        let sent_before = alive_keeper.epoch().elapsed();
657        region_server
658            .set_region_role_state_gracefully(region_id, SettableRegionRoleState::Follower)
659            .await
660            .unwrap();
661        handle_lease_response_sent_at(&alive_keeper, sent_before, vec![grant.clone()]).await;
662        alive_keeper.deadline(region_id).await.unwrap();
663        assert_eq!(region_server.is_region_leader(region_id), Some(false));
664
665        // Send times have millisecond precision, so the next heartbeat is sent after the downgrade.
666        tokio::time::sleep(Duration::from_millis(2)).await;
667        handle_lease_response(&alive_keeper, vec![grant]).await;
668        alive_keeper.deadline(region_id).await.unwrap();
669        put(region_id).await.unwrap();
670    }
671
672    #[tokio::test(flavor = "multi_thread")]
673    async fn region_alive_keeper() {
674        common_telemetry::init_default_ut_logging();
675        let mut region_server = mock_region_server();
676        let mut engine_env = TestEnv::with_prefix("region-alive-keeper").await;
677        let engine = engine_env.create_engine(MitoConfig::default()).await;
678        let engine = Arc::new(engine);
679        region_server.register_engine(engine.clone());
680
681        let alive_keeper = Arc::new(RegionAliveKeeper::new(
682            region_server.clone(),
683            None,
684            Duration::from_millis(100),
685        ));
686
687        let region_id = RegionId::new(1024, 1);
688        let builder = CreateRequestBuilder::new();
689        region_server
690            .handle_request(region_id, RegionRequest::Create(builder.build()))
691            .await
692            .unwrap();
693        region_server
694            .set_region_role(region_id, RegionRole::Leader)
695            .unwrap();
696
697        // Register a region before starting.
698        alive_keeper.register_region(region_id).await;
699        assert!(alive_keeper.find_handle(region_id).await.is_some());
700
701        info!("Start the keeper");
702        alive_keeper.start(None).await.unwrap();
703
704        // The started alive keeper should assign deadline to this region.
705        let deadline = alive_keeper.deadline(region_id).await.unwrap();
706        assert!(deadline >= Instant::now());
707        assert_eq!(engine.role(region_id).unwrap(), RegionRole::Leader);
708
709        info!("Wait for lease expired");
710        // Sleep to wait lease expired.
711        tokio::time::sleep(Duration::from_millis(500)).await;
712        assert!(alive_keeper.find_handle(region_id).await.is_some());
713        assert_eq!(engine.role(region_id).unwrap(), RegionRole::Follower);
714
715        info!("Renew the region lease");
716        // Renew lease then sleep.
717        alive_keeper
718            .renew_region_leases(
719                &[GrantedRegion {
720                    region_id: region_id.as_u64(),
721                    role: api::v1::meta::RegionRole::Leader.into(),
722                    extensions: HashMap::new(),
723                }],
724                Instant::now(),
725                Instant::now() + Duration::from_millis(200),
726            )
727            .await;
728        tokio::time::sleep(Duration::from_millis(100)).await;
729        assert!(alive_keeper.find_handle(region_id).await.is_some());
730        let deadline = alive_keeper.deadline(region_id).await.unwrap();
731        assert!(deadline >= Instant::now());
732        assert_eq!(engine.role(region_id).unwrap(), RegionRole::Leader);
733
734        info!("Wait for lease expired");
735        // Sleep to wait lease expired.
736        tokio::time::sleep(Duration::from_millis(200)).await;
737        assert!(alive_keeper.find_handle(region_id).await.is_some());
738        assert_eq!(engine.role(region_id).unwrap(), RegionRole::Follower);
739
740        let deadline = alive_keeper.deadline(region_id).await.unwrap();
741        assert!(deadline > Instant::now() + Duration::from_secs(86400 * 365 * 29));
742    }
743
744    #[tokio::test(flavor = "multi_thread")]
745    async fn countdown_task() {
746        let region_server = mock_region_server();
747
748        let countdown_handle =
749            CountdownTaskHandle::new(region_server, None, RegionId::new(9999, 2));
750
751        // If countdown task is not started, its deadline is set to far future.
752        assert!(
753            countdown_handle.deadline().await.unwrap()
754                > Instant::now() + Duration::from_secs(86400 * 365 * 29)
755        );
756
757        // The first deadline should be set to 4 * heartbeat_interval_millis.
758        // We assert it to be greater than 3 * heartbeat_interval_millis to avoid flaky test.
759        let heartbeat_interval_millis = 100;
760        countdown_handle.start(heartbeat_interval_millis).await;
761        assert!(
762            countdown_handle.deadline().await.unwrap()
763                > Instant::now() + Duration::from_millis(heartbeat_interval_millis * 3)
764        );
765        tokio::time::sleep(Duration::from_millis(heartbeat_interval_millis * 5)).await;
766
767        // No effect.
768        countdown_handle.start(heartbeat_interval_millis).await;
769        assert!(
770            countdown_handle.deadline().await.unwrap()
771                > Instant::now() + Duration::from_secs(86400 * 365 * 29)
772        );
773
774        // Reset deadline.
775        countdown_handle
776            .reset_deadline(
777                RegionRole::Leader,
778                Instant::now(),
779                Instant::now() + Duration::from_millis(heartbeat_interval_millis * 5),
780                HashMap::new(),
781            )
782            .await;
783        assert!(
784            countdown_handle.deadline().await.unwrap()
785                > Instant::now() + Duration::from_millis(heartbeat_interval_millis * 4)
786        );
787    }
788
789    #[tokio::test(flavor = "multi_thread")]
790    async fn renew_staging_leader_keeps_region_in_staging() {
791        let mut region_server = mock_region_server();
792        let mut engine_env = TestEnv::with_prefix("region-alive-keeper-staging").await;
793        let engine = engine_env.create_engine(MitoConfig::default()).await;
794        let engine = Arc::new(engine);
795        region_server.register_engine(engine.clone());
796
797        let alive_keeper = Arc::new(RegionAliveKeeper::new(
798            region_server.clone(),
799            None,
800            Duration::from_millis(100),
801        ));
802
803        let region_id = RegionId::new(1024, 2);
804        region_server
805            .handle_request(
806                region_id,
807                RegionRequest::Create(CreateRequestBuilder::new().build()),
808            )
809            .await
810            .unwrap();
811        region_server
812            .handle_request(
813                region_id,
814                RegionRequest::EnterStaging(EnterStagingRequest {
815                    partition_directive: StagingPartitionDirective::RejectAllWrites,
816                }),
817            )
818            .await
819            .unwrap();
820
821        alive_keeper.register_region(region_id).await;
822        alive_keeper
823            .renew_region_leases(
824                &[GrantedRegion {
825                    region_id: region_id.as_u64(),
826                    role: api::v1::meta::RegionRole::StagingLeader.into(),
827                    extensions: HashMap::new(),
828                }],
829                Instant::now(),
830                Instant::now() + Duration::from_millis(3000),
831            )
832            .await;
833
834        assert_eq!(engine.role(region_id).unwrap(), RegionRole::StagingLeader);
835    }
836
837    #[tokio::test(flavor = "multi_thread")]
838    async fn renew_staging_leader_exit_into_leader() {
839        common_telemetry::init_default_ut_logging();
840        let mut region_server = mock_region_server();
841        let mut engine_env = TestEnv::with_prefix("region-alive-keeper-staging-exit").await;
842        let engine = engine_env.create_engine(MitoConfig::default()).await;
843        let engine = Arc::new(engine);
844        region_server.register_engine(engine.clone());
845
846        let alive_keeper = Arc::new(RegionAliveKeeper::new(
847            region_server.clone(),
848            None,
849            Duration::from_millis(100),
850        ));
851
852        let region_id = RegionId::new(1024, 2);
853        region_server
854            .handle_request(
855                region_id,
856                RegionRequest::Create(CreateRequestBuilder::new().build()),
857            )
858            .await
859            .unwrap();
860        region_server
861            .handle_request(
862                region_id,
863                RegionRequest::EnterStaging(EnterStagingRequest {
864                    partition_directive: StagingPartitionDirective::RejectAllWrites,
865                }),
866            )
867            .await
868            .unwrap();
869
870        alive_keeper.register_region(region_id).await;
871        alive_keeper
872            .renew_region_leases(
873                &[GrantedRegion {
874                    region_id: region_id.as_u64(),
875                    role: api::v1::meta::RegionRole::Leader.into(),
876                    extensions: HashMap::new(),
877                }],
878                Instant::now(),
879                Instant::now() + Duration::from_millis(3000),
880            )
881            .await;
882
883        tokio::time::sleep(Duration::from_millis(100)).await;
884        assert_eq!(engine.role(region_id).unwrap(), RegionRole::Leader);
885    }
886
887    #[tokio::test(flavor = "multi_thread")]
888    async fn renew_staging_leader_does_not_promote_normal_leader_into_staging() {
889        let mut region_server = mock_region_server();
890        let mut engine_env = TestEnv::with_prefix("region-alive-keeper-non-staging").await;
891        let engine = engine_env.create_engine(MitoConfig::default()).await;
892        let engine = Arc::new(engine);
893        region_server.register_engine(engine.clone());
894
895        let alive_keeper = Arc::new(RegionAliveKeeper::new(
896            region_server.clone(),
897            None,
898            Duration::from_millis(100),
899        ));
900
901        let region_id = RegionId::new(1024, 4);
902        region_server
903            .handle_request(
904                region_id,
905                RegionRequest::Create(CreateRequestBuilder::new().build()),
906            )
907            .await
908            .unwrap();
909        region_server
910            .set_region_role(region_id, RegionRole::Leader)
911            .unwrap();
912
913        alive_keeper.register_region(region_id).await;
914        alive_keeper
915            .renew_region_leases(
916                &[GrantedRegion {
917                    region_id: region_id.as_u64(),
918                    role: api::v1::meta::RegionRole::StagingLeader.into(),
919                    extensions: HashMap::new(),
920                }],
921                Instant::now(),
922                Instant::now() + Duration::from_millis(3000),
923            )
924            .await;
925        tokio::time::sleep(Duration::from_millis(100)).await;
926        assert_eq!(engine.role(region_id).unwrap(), RegionRole::Leader);
927    }
928}