1use std::cmp::Ordering;
16use std::sync::Arc;
17
18use api::v1::meta::{HeartbeatRequest, Peer, Role};
19use common_meta::datanode::{DatanodeStatKey, DatanodeStatValue, Stat};
20use common_meta::instruction::CacheIdent;
21use common_meta::key::MetadataKey;
22use common_meta::key::node_address::NodeAddressKey;
23use common_meta::rpc::store::PutRequest;
24use common_telemetry::{error, warn};
25use dashmap::DashMap;
26use snafu::ResultExt;
27use tokio::sync::Mutex;
28
29use crate::error::{self, Result};
30use crate::handler::utils::{self, NodeAddressUpdater};
31use crate::handler::{HandleControl, HeartbeatAccumulator, HeartbeatHandler};
32use crate::metasrv::Context;
33
34#[derive(Debug, Default)]
35struct EpochStats {
36 stats: Vec<Stat>,
37 epoch: Option<u64>,
38}
39
40impl EpochStats {
41 #[inline]
42 fn drain_all(&mut self) -> Vec<Stat> {
43 std::mem::take(&mut self.stats)
44 }
45
46 #[inline]
47 fn clear_stats(&mut self) {
48 self.stats.clear();
49 }
50
51 #[inline]
52 fn push_stat(&mut self, stat: Stat) {
53 self.stats.push(stat);
54 }
55
56 #[inline]
57 fn len(&self) -> usize {
58 self.stats.len()
59 }
60
61 #[inline]
62 fn epoch(&self) -> Option<u64> {
63 self.epoch
64 }
65
66 #[inline]
67 fn set_epoch(&mut self, epoch: u64) {
68 self.epoch = Some(epoch);
69 }
70}
71
72const DEFAULT_FLUSH_STATS_FACTOR: usize = 3;
73
74pub struct CollectStatsHandler {
75 stats_cache: DashMap<DatanodeStatKey, Arc<Mutex<EpochStats>>>,
76 flush_stats_factor: usize,
77 address_updater: NodeAddressUpdater,
78}
79
80impl Default for CollectStatsHandler {
81 fn default() -> Self {
82 Self::new(None)
83 }
84}
85
86impl CollectStatsHandler {
87 pub fn new(flush_stats_factor: Option<usize>) -> Self {
88 Self {
89 flush_stats_factor: flush_stats_factor.unwrap_or(DEFAULT_FLUSH_STATS_FACTOR),
90 stats_cache: DashMap::default(),
91 address_updater: NodeAddressUpdater::default(),
92 }
93 }
94}
95
96#[async_trait::async_trait]
97impl HeartbeatHandler for CollectStatsHandler {
98 fn is_acceptable(&self, role: Role) -> bool {
99 role == Role::Datanode
100 }
101
102 async fn handle(
103 &self,
104 _req: &HeartbeatRequest,
105 ctx: &mut Context,
106 acc: &mut HeartbeatAccumulator,
107 ) -> Result<HandleControl> {
108 let Some(current_stat) = acc.stat.take() else {
109 return Ok(HandleControl::Continue);
110 };
111
112 let key = current_stat.stat_key();
113 let state = {
114 let entry = self
115 .stats_cache
116 .entry(key)
117 .or_insert_with(|| Arc::new(Mutex::new(EpochStats::default())));
118 Arc::clone(entry.value())
119 };
120 let mut epoch_stats = state.lock().await;
121
122 let key: Vec<u8> = key.into();
123
124 let current_epoch = current_stat.node_epoch;
125 let refresh = if let Some(epoch) = epoch_stats.epoch() {
126 match current_stat.node_epoch.cmp(&epoch) {
127 Ordering::Greater => {
128 epoch_stats.clear_stats();
130 epoch_stats.set_epoch(current_stat.node_epoch);
131 epoch_stats.push_stat(current_stat);
132 true
133 }
134 Ordering::Equal => {
135 epoch_stats.push_stat(current_stat);
136 false
137 }
138 Ordering::Less => {
139 warn!("Ignore stale heartbeat: {:?}", current_stat);
140 false
141 }
142 }
143 } else {
144 epoch_stats.set_epoch(current_stat.node_epoch);
145 epoch_stats.push_stat(current_stat);
146 true
150 };
151
152 if epoch_stats.epoch() == Some(current_epoch)
156 && let Some(stat) = epoch_stats.stats.last()
157 {
158 update_datanode_address(&self.address_updater, ctx, stat).await;
159 }
160
161 if !refresh && epoch_stats.len() < self.flush_stats_factor {
162 return Ok(HandleControl::Continue);
163 }
164
165 let value: Vec<u8> = DatanodeStatValue {
166 stats: epoch_stats.drain_all(),
167 }
168 .try_into()
169 .context(error::InvalidDatanodeStatFormatSnafu {})?;
170 let put = PutRequest {
171 key,
172 value,
173 prev_kv: false,
174 };
175
176 let _ = ctx
177 .in_memory
178 .put(put)
179 .await
180 .context(error::KvBackendSnafu)?;
181
182 Ok(HandleControl::Continue)
183 }
184}
185
186async fn update_datanode_address(updater: &NodeAddressUpdater, ctx: &Context, stat: &Stat) {
189 let updated = updater
190 .update_if_needed((Role::Datanode, stat.id), stat.node_epoch, || async {
191 let peer = Peer { id: stat.id, addr: stat.addr.clone() };
192 let key = NodeAddressKey::with_datanode(stat.id).to_bytes();
193 utils::save_node_address(ctx, key, peer).await
194 })
195 .await
196 .inspect_err(|e| {
197 error!(e; "Failed to update datanode address, node: {}, address: {}", stat.id, stat.addr);
198 });
199 if let Ok(true) = updated {
200 let cache_idents = stat
201 .table_ids()
202 .into_iter()
203 .map(CacheIdent::TableId)
204 .collect::<Vec<_>>();
205 utils::invalidate_address_caches(ctx, stat.id, &cache_idents).await;
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use std::any::Any;
212 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering};
213 use std::sync::{Arc, Mutex as StdMutex, mpsc};
214 use std::thread;
215 use std::time::Duration;
216
217 use common_meta::datanode::DatanodeStatKey;
218 use common_meta::error::{Error as MetaError, Result as MetaResult};
219 use common_meta::kv_backend::{KvBackend, KvBackendRef, ResettableKvBackend, TxnService};
220 use common_meta::rpc::store::{
221 BatchDeleteRequest, BatchDeleteResponse, BatchGetRequest, BatchGetResponse,
222 BatchPutRequest, BatchPutResponse, DeleteRangeRequest, DeleteRangeResponse, PutResponse,
223 RangeRequest, RangeResponse,
224 };
225 use tokio::sync::Semaphore;
226 use tokio::time::{sleep, timeout};
227
228 use super::*;
229 use crate::handler::test_utils::TestEnv;
230 use crate::service::store::cached_kv::LeaderCachedKvBackend;
231
232 struct ControlledKvBackend {
233 recorded_puts: StdMutex<Vec<PutRequest>>,
234 put_entered: Semaphore,
235 put_release: Semaphore,
236 block_next_put: AtomicBool,
237 delay_puts: AtomicBool,
238 active_puts: AtomicUsize,
239 max_active_puts: AtomicUsize,
240 }
241
242 impl ControlledKvBackend {
243 fn new() -> Self {
244 Self {
245 recorded_puts: StdMutex::new(Vec::new()),
246 put_entered: Semaphore::new(0),
247 put_release: Semaphore::new(0),
248 block_next_put: AtomicBool::new(false),
249 delay_puts: AtomicBool::new(false),
250 active_puts: AtomicUsize::new(0),
251 max_active_puts: AtomicUsize::new(0),
252 }
253 }
254
255 fn block_next_put(&self) {
256 self.block_next_put.store(true, AtomicOrdering::Relaxed);
257 }
258
259 async fn wait_for_blocked_put(&self) {
260 self.put_entered.acquire().await.unwrap().forget();
261 }
262
263 fn release_one_put(&self) {
264 self.put_release.add_permits(1);
265 }
266
267 fn set_delay_puts(&self, delay: bool) {
268 self.delay_puts.store(delay, AtomicOrdering::Relaxed);
269 }
270
271 fn clear_recorded_puts(&self) {
272 self.recorded_puts.lock().unwrap().clear();
273 }
274
275 fn recorded_puts(&self) -> Vec<PutRequest> {
276 self.recorded_puts.lock().unwrap().clone()
277 }
278
279 fn max_active_puts(&self) -> usize {
280 self.max_active_puts.load(AtomicOrdering::Relaxed)
281 }
282
283 fn start_put(&self, req: &PutRequest) -> PutGuard<'_> {
284 self.recorded_puts.lock().unwrap().push(req.clone());
285 let active = self.active_puts.fetch_add(1, AtomicOrdering::Relaxed) + 1;
286 self.max_active_puts
287 .fetch_max(active, AtomicOrdering::Relaxed);
288 PutGuard { backend: self }
289 }
290 }
291
292 struct PutGuard<'a> {
293 backend: &'a ControlledKvBackend,
294 }
295
296 impl Drop for PutGuard<'_> {
297 fn drop(&mut self) {
298 self.backend
299 .active_puts
300 .fetch_sub(1, AtomicOrdering::Relaxed);
301 }
302 }
303
304 #[async_trait::async_trait]
305 impl TxnService for ControlledKvBackend {
306 type Error = MetaError;
307 }
308
309 #[async_trait::async_trait]
310 impl KvBackend for ControlledKvBackend {
311 fn name(&self) -> &str {
312 "controlled"
313 }
314
315 fn as_any(&self) -> &dyn Any {
316 self
317 }
318
319 async fn range(&self, _req: RangeRequest) -> MetaResult<RangeResponse> {
320 unimplemented!()
321 }
322
323 async fn put(&self, req: PutRequest) -> MetaResult<PutResponse> {
324 let _guard = self.start_put(&req);
325 if self.block_next_put.swap(false, AtomicOrdering::Relaxed) {
326 self.put_entered.add_permits(1);
327 self.put_release.acquire().await.unwrap().forget();
328 }
329 if self.delay_puts.load(AtomicOrdering::Relaxed) {
330 sleep(Duration::from_millis(10)).await;
331 }
332 Ok(PutResponse::default())
333 }
334
335 async fn batch_put(&self, _req: BatchPutRequest) -> MetaResult<BatchPutResponse> {
336 unimplemented!()
337 }
338
339 async fn batch_get(&self, _req: BatchGetRequest) -> MetaResult<BatchGetResponse> {
340 unimplemented!()
341 }
342
343 async fn delete_range(&self, _req: DeleteRangeRequest) -> MetaResult<DeleteRangeResponse> {
344 unimplemented!()
345 }
346
347 async fn batch_delete(&self, _req: BatchDeleteRequest) -> MetaResult<BatchDeleteResponse> {
348 unimplemented!()
349 }
350 }
351
352 impl ResettableKvBackend for ControlledKvBackend {
353 fn reset(&self) {
354 self.clear_recorded_puts();
355 }
356
357 fn as_kv_backend_ref(self: Arc<Self>) -> KvBackendRef {
358 self
359 }
360 }
361
362 fn stat(node_id: u64, epoch: u64, marker: u64, addr: &str) -> Stat {
363 Stat {
364 timestamp_millis: marker as i64,
365 id: node_id,
366 addr: addr.to_string(),
367 region_num: marker,
368 node_epoch: epoch,
369 ..Default::default()
370 }
371 }
372
373 async fn handle_stat(
374 handler: Arc<CollectStatsHandler>,
375 mut ctx: Context,
376 stat: Stat,
377 ) -> Result<HandleControl> {
378 let mut acc = HeartbeatAccumulator {
379 stat: Some(stat),
380 ..Default::default()
381 };
382 handler
383 .handle(&HeartbeatRequest::default(), &mut ctx, &mut acc)
384 .await
385 }
386
387 fn use_controlled_address_backend(ctx: &mut Context) -> Arc<ControlledKvBackend> {
388 let backend = Arc::new(ControlledKvBackend::new());
389 ctx.leader_cached_kv_backend =
390 Arc::new(LeaderCachedKvBackend::with_always_leader(backend.clone()));
391 backend
392 }
393
394 #[tokio::test]
395 async fn test_handle_datanode_stats() {
396 let env = TestEnv::new();
397 let ctx = env.ctx();
398
399 let handler = CollectStatsHandler::default();
400 handle_request_many_times(ctx.clone(), &handler, 1).await;
401
402 let key = DatanodeStatKey { node_id: 101 };
403 let key: Vec<u8> = key.into();
404 let res = ctx.in_memory.get(&key).await.unwrap();
405 let kv = res.unwrap();
406 let key: DatanodeStatKey = kv.key.clone().try_into().unwrap();
407 assert_eq!(101, key.node_id);
408 let val: DatanodeStatValue = kv.value.try_into().unwrap();
409 assert_eq!(1, val.stats.len());
411 assert_eq!(1, val.stats[0].region_num);
412
413 handle_request_many_times(ctx.clone(), &handler, 10).await;
414
415 let key: Vec<u8> = key.into();
416 let res = ctx.in_memory.get(&key).await.unwrap();
417 let kv = res.unwrap();
418 let val: DatanodeStatValue = kv.value.try_into().unwrap();
419 assert_eq!(handler.flush_stats_factor, val.stats.len());
420 }
421
422 #[test]
423 fn test_same_datanode_wait_keeps_current_thread_runtime_responsive() {
424 let (backend_tx, backend_rx) = mpsc::sync_channel(1);
425 let (timer_tx, timer_rx) = mpsc::sync_channel(1);
426 let (done_tx, done_rx) = mpsc::sync_channel(1);
427
428 let worker = thread::spawn(move || {
429 tokio::runtime::Builder::new_current_thread()
430 .enable_time()
431 .build()
432 .unwrap()
433 .block_on(async move {
434 let env = TestEnv::new();
435 let mut ctx = env.ctx();
436 let address_backend = use_controlled_address_backend(&mut ctx);
437 address_backend.block_next_put();
438 backend_tx.send(address_backend.clone()).unwrap();
439
440 let handler = Arc::new(CollectStatsHandler::default());
441 let first = tokio::spawn(handle_stat(
442 handler.clone(),
443 ctx.clone(),
444 stat(101, 1, 1, "dn-101-v1"),
445 ));
446 address_backend.wait_for_blocked_put().await;
447
448 let second =
449 tokio::spawn(handle_stat(handler, ctx, stat(101, 1, 2, "dn-101-v1")));
450 tokio::spawn(async move {
451 sleep(Duration::from_millis(10)).await;
452 timer_tx.send(()).unwrap();
453 });
454 tokio::task::yield_now().await;
455
456 first.await.unwrap().unwrap();
457 second.await.unwrap().unwrap();
458 done_tx.send(()).unwrap();
459 });
460 });
461
462 let address_backend = backend_rx.recv_timeout(Duration::from_secs(1)).unwrap();
463 let timer_result = timer_rx.recv_timeout(Duration::from_secs(1));
464 address_backend.release_one_put();
465 timer_result.expect("the current-thread runtime must remain responsive");
466 done_rx
467 .recv_timeout(Duration::from_secs(1))
468 .expect("both heartbeat handlers must complete after releasing the write");
469 worker.join().unwrap();
470 }
471
472 #[tokio::test]
473 async fn test_concurrent_flush_persists_every_stat_once() {
474 let env = TestEnv::new();
475 let mut ctx = env.ctx();
476 let stats_backend = Arc::new(ControlledKvBackend::new());
477 ctx.in_memory = stats_backend.clone();
478 stats_backend.set_delay_puts(true);
479
480 let flush_stats_factor = 3;
481 let handler = Arc::new(CollectStatsHandler::new(Some(flush_stats_factor)));
482 handle_stat(handler.clone(), ctx.clone(), stat(101, 1, 0, "dn-101"))
483 .await
484 .unwrap();
485 stats_backend.clear_recorded_puts();
486
487 let mut tasks = Vec::with_capacity(2 * flush_stats_factor);
488 for marker in 1..=(2 * flush_stats_factor) {
489 tasks.push(tokio::spawn(handle_stat(
490 handler.clone(),
491 ctx.clone(),
492 stat(101, 1, marker as u64, "dn-101"),
493 )));
494 }
495 for task in tasks {
496 timeout(Duration::from_secs(1), task)
497 .await
498 .unwrap()
499 .unwrap()
500 .unwrap();
501 }
502
503 let puts = stats_backend.recorded_puts();
504 assert_eq!(2, puts.len());
505 let mut markers = puts
506 .into_iter()
507 .flat_map(|put| {
508 let value: DatanodeStatValue = put.value.try_into().unwrap();
509 value
510 .stats
511 .into_iter()
512 .map(|stat| stat.region_num)
513 .collect::<Vec<_>>()
514 })
515 .collect::<Vec<_>>();
516 markers.sort_unstable();
517 assert_eq!((1..=6).collect::<Vec<_>>(), markers);
518 assert_eq!(1, stats_backend.max_active_puts());
519 }
520
521 async fn handle_request_many_times(
522 mut ctx: Context,
523 handler: &CollectStatsHandler,
524 loop_times: i32,
525 ) {
526 let req = HeartbeatRequest::default();
527 for i in 1..=loop_times {
528 let mut acc = HeartbeatAccumulator {
529 stat: Some(Stat {
530 id: 101,
531 region_num: i as _,
532 ..Default::default()
533 }),
534 ..Default::default()
535 };
536 handler.handle(&req, &mut ctx, &mut acc).await.unwrap();
537 }
538 }
539
540 #[tokio::test]
541 async fn test_persistence_failure_retries_but_notification_failure_does_not() {
542 use crate::handler::collect_stats_handler::CollectStatsHandler;
543 use crate::handler::test_utils::FailingCacheInvalidator;
544 use crate::handler::{HandleControl, HeartbeatAccumulator, HeartbeatHandler};
545 use crate::service::store::cached_kv::LeaderCachedKvBackend;
546 use api::v1::meta::HeartbeatRequest;
547 use common_meta::key::MetadataValue;
548 use common_meta::key::node_address::NodeAddressValue;
549 use common_meta::kv_backend::test_util::MockKvBackendBuilder;
550 use common_meta::rpc::store::PutResponse;
551 let mut ctx = TestEnv::new().ctx();
552 let puts = Arc::new(std::sync::Mutex::new(Vec::<PutRequest>::new()));
553 let backend = MockKvBackendBuilder::default()
554 .put_fn(Arc::new({
555 let puts = puts.clone();
556 move |req: PutRequest| {
557 let mut puts = puts.lock().unwrap();
558 puts.push(req);
559 if puts.len() == 1 {
560 return common_meta::error::UnexpectedSnafu {
561 err_msg: "injected persistence failure",
562 }
563 .fail();
564 }
565 Ok(PutResponse::default())
566 }
567 }) as _)
568 .build()
569 .unwrap();
570 ctx.leader_cached_kv_backend =
571 Arc::new(LeaderCachedKvBackend::with_always_leader(Arc::new(backend)));
572 let invalidator = Arc::new(FailingCacheInvalidator::default());
573 ctx.cache_invalidator = invalidator.clone();
574 let handler = CollectStatsHandler::default();
575 let mut stat = Stat {
576 id: 101,
577 addr: "dn-original".into(),
578 node_epoch: 7,
579 ..Default::default()
580 };
581 let req = HeartbeatRequest::default();
582
583 assert!(matches!(
585 handler
586 .handle(
587 &req,
588 &mut ctx,
589 &mut HeartbeatAccumulator {
590 stat: Some(stat.clone()),
591 ..Default::default()
592 }
593 )
594 .await
595 .unwrap(),
596 HandleControl::Continue
597 ));
598 assert_eq!(puts.lock().unwrap().len(), 1);
599 assert_eq!(invalidator.attempts(), 0);
600
601 let stats_key: Vec<u8> = common_meta::datanode::DatanodeStatKey { node_id: 101 }.into();
602 assert!(ctx.in_memory.get(&stats_key).await.unwrap().is_some());
603
604 stat.addr = "dn-retry".into();
606 handler
607 .handle(
608 &req,
609 &mut ctx,
610 &mut HeartbeatAccumulator {
611 stat: Some(stat.clone()),
612 ..Default::default()
613 },
614 )
615 .await
616 .unwrap();
617 {
618 let puts = puts.lock().unwrap();
619 assert_eq!(puts.len(), 2);
620 assert_eq!(puts[1].key, NodeAddressKey::with_datanode(101).to_bytes());
621 assert_eq!(
622 NodeAddressValue::try_from_raw_value(&puts[1].value)
623 .unwrap()
624 .peer
625 .addr,
626 "dn-retry"
627 );
628 }
629 assert_eq!(invalidator.attempts(), 1);
630
631 handler
633 .handle(
634 &req,
635 &mut ctx,
636 &mut HeartbeatAccumulator {
637 stat: Some(stat.clone()),
638 ..Default::default()
639 },
640 )
641 .await
642 .unwrap();
643 assert_eq!(puts.lock().unwrap().len(), 2);
644 assert_eq!(invalidator.attempts(), 1);
645
646 stat.node_epoch = 8;
647 handler
648 .handle(
649 &req,
650 &mut ctx,
651 &mut HeartbeatAccumulator {
652 stat: Some(stat.clone()),
653 ..Default::default()
654 },
655 )
656 .await
657 .unwrap();
658 assert_eq!(puts.lock().unwrap().len(), 3);
659 assert_eq!(invalidator.attempts(), 2);
660 }
661}