1use 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
42pub 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 epoch: Instant,
61
62 countdown_task_handler_ext: Option<CountdownTaskHandlerExtRef>,
63}
64
65impl RegionAliveKeeper {
66 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 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(®ion_id).cloned()
92 }
93
94 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 if handles.contains_key(®ion_id) {
108 return;
109 }
110
111 handles.insert(region_id, handle.clone());
113
114 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 pub async fn deregister_region(&self, region_id: RegionId) {
132 if self.tasks.lock().await.remove(®ion_id).is_some() {
133 info!("Deregister alive countdown for region {region_id}")
134 }
135 }
136
137 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 }
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 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 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 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(®ion_lease.regions, start_instant, deadline)
290 .await;
291 self.close_staled_regions(®ion_lease.closeable_region_ids)
292 .await;
293
294 Ok(HandleControl::Continue)
295 }
296}
297
298#[derive(Debug)]
299enum CountdownCommand {
300 Start(u64),
303 Reset((RegionRole, Instant, Instant, HashMap<String, Vec<u8>>)),
306 #[cfg(test)]
308 Deadline(oneshot::Sender<Instant>),
309}
310
311pub type CountdownTaskHandlerExtRef = Arc<dyn CountdownTaskHandlerExt>;
312
313#[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 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 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 let far_future = Instant::now() + Duration::from_secs(86400 * 365 * 30);
428
429 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 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 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 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 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 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 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 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 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 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 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 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 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 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 alive_keeper.deadline(region_id).await.unwrap();
653 put(region_id).await.unwrap();
654
655 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 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 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 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 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 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 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 assert!(
753 countdown_handle.deadline().await.unwrap()
754 > Instant::now() + Duration::from_secs(86400 * 365 * 29)
755 );
756
757 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 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 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}