1use std::collections::HashMap;
16use std::sync::Arc;
17use std::sync::atomic::{AtomicBool, Ordering};
18use std::time::Duration;
19
20use api::v1::meta::heartbeat_request::NodeWorkloads;
21use api::v1::meta::{DatanodeWorkloads, HeartbeatRequest, NodeInfo, Peer, RegionRole, RegionStat};
22use common_base::Plugins;
23use common_meta::cache_invalidator::CacheInvalidatorRef;
24use common_meta::datanode::{EnvVars, REGION_STATISTIC_KEY};
25use common_meta::distributed_time_constants::BASE_HEARTBEAT_INTERVAL;
26use common_meta::heartbeat::handler::invalidate_table_cache::InvalidateCacheHandler;
27use common_meta::heartbeat::handler::parse_mailbox_message::ParseMailboxMessageHandler;
28use common_meta::heartbeat::handler::suspend::SuspendHandler;
29use common_meta::heartbeat::handler::{
30 HandlerGroupExecutor, HeartbeatResponseHandlerContext, HeartbeatResponseHandlerExecutorRef,
31};
32use common_meta::heartbeat::mailbox::{HeartbeatMailbox, MailboxRef};
33use common_meta::heartbeat::utils::outgoing_message_to_mailbox_message;
34use common_meta::kv_backend::KvBackendRef;
35use common_stat::ResourceStatRef;
36use common_telemetry::{debug, error, info, trace, warn};
37use common_workload::DatanodeWorkloadType;
38use meta_client::MetaClientRef;
39use meta_client::client::heartbeat::HeartbeatConfig;
40use meta_client::client::{HeartbeatSender, MetaClient};
41use servers::addrs;
42use snafu::{OptionExt as _, ResultExt};
43use tokio::sync::{Notify, mpsc};
44use tokio::time::Instant;
45
46use self::handler::RegionHeartbeatResponseHandler;
47use crate::alive_keeper::{CountdownTaskHandlerExtRef, RegionAliveKeeper};
48use crate::config::DatanodeOptions;
49use crate::error::{self, MetaClientInitSnafu, RegionEngineNotFoundSnafu, Result};
50use crate::event_listener::RegionServerEventReceiver;
51use crate::metrics::{self, HEARTBEAT_RECV_COUNT, HEARTBEAT_SENT_COUNT};
52use crate::region_server::RegionServer;
53
54pub(crate) mod handler;
55pub(crate) mod task_tracker;
56
57pub struct HeartbeatTask {
59 node_id: u64,
60 workload_types: Vec<DatanodeWorkloadType>,
61 node_epoch: u64,
62 peer_addr: String,
63 running: Arc<AtomicBool>,
64 meta_client: MetaClientRef,
65 region_server: RegionServer,
66 resp_handler_executor: HeartbeatResponseHandlerExecutorRef,
67 region_alive_keeper: Arc<RegionAliveKeeper>,
68 resource_stat: ResourceStatRef,
69 env_vars: EnvVars,
70}
71
72impl Drop for HeartbeatTask {
73 fn drop(&mut self) {
74 self.running.store(false, Ordering::Release);
75 }
76}
77
78impl HeartbeatTask {
79 pub async fn try_new(
81 opts: &DatanodeOptions,
82 region_server: RegionServer,
83 meta_client: MetaClientRef,
84 kv_backend: KvBackendRef,
85 cache_invalidator: CacheInvalidatorRef,
86 plugins: Plugins,
87 resource_stat: ResourceStatRef,
88 ) -> Result<Self> {
89 let countdown_task_handler_ext = plugins.get::<CountdownTaskHandlerExtRef>();
90 let region_alive_keeper = Arc::new(RegionAliveKeeper::new(
91 region_server.clone(),
92 countdown_task_handler_ext,
93 BASE_HEARTBEAT_INTERVAL,
94 ));
95 let resp_handler_executor = Arc::new(HandlerGroupExecutor::new(vec![
96 region_alive_keeper.clone(),
97 Arc::new(ParseMailboxMessageHandler),
98 Arc::new(SuspendHandler::new(region_server.suspend_state())),
99 Arc::new(
100 RegionHeartbeatResponseHandler::new(region_server.clone(), kv_backend)
101 .with_open_region_parallelism(opts.init_regions_parallelism),
102 ),
103 Arc::new(InvalidateCacheHandler::new(cache_invalidator)),
104 ]));
105
106 Ok(Self {
107 node_id: opts.node_id.unwrap_or(0),
108 workload_types: opts.workload_types.clone(),
109 node_epoch: common_time::util::current_time_millis() as u64,
111 peer_addr: addrs::resolve_addr(&opts.grpc.bind_addr, Some(&opts.grpc.server_addr)),
112 running: Arc::new(AtomicBool::new(false)),
113 meta_client,
114 region_server,
115 resp_handler_executor,
116 region_alive_keeper,
117 resource_stat,
118 env_vars: EnvVars::from_config(&opts.heartbeat_env_vars),
119 })
120 }
121
122 pub async fn create_streams(
123 meta_client: &MetaClient,
124 local_gc_enabled: bool,
125 running: Arc<AtomicBool>,
126 handler_executor: HeartbeatResponseHandlerExecutorRef,
127 mailbox: MailboxRef,
128 mut notify: Option<Arc<Notify>>,
129 quit_signal: Arc<Notify>,
130 ) -> Result<(HeartbeatSender, HeartbeatConfig)> {
131 let client_id = meta_client.id();
132 let (tx, mut rx, config) = meta_client.heartbeat().await.context(MetaClientInitSnafu)?;
133 if config.gc_enabled != local_gc_enabled {
134 return error::GcConfigMismatchSnafu {
135 metasrv_gc_enabled: config.gc_enabled,
136 datanode_gc_enabled: local_gc_enabled,
137 }
138 .fail();
139 }
140
141 let mut last_received_lease = Instant::now();
142
143 let _handle = common_runtime::spawn_hb(async move {
144 while let Some(res) = rx.message().await.unwrap_or_else(|e| {
145 error!(e; "Error while reading heartbeat response");
146 None
147 }) {
148 if let Some(msg) = res.mailbox_message.as_ref() {
149 info!("Received mailbox message: {msg:?}, meta_client id: {client_id:?}");
150 }
151 if let Some(lease) = res.region_lease.as_ref() {
152 metrics::LAST_RECEIVED_HEARTBEAT_ELAPSED
153 .set(last_received_lease.elapsed().as_millis() as i64);
154 last_received_lease = Instant::now();
156
157 let mut leader_region_lease_count = 0;
158 let mut follower_region_lease_count = 0;
159 for lease in &lease.regions {
160 match lease.role() {
161 RegionRole::Leader
162 | RegionRole::StagingLeader
163 | RegionRole::DowngradingLeader => leader_region_lease_count += 1,
164 RegionRole::Follower => follower_region_lease_count += 1,
165 }
166 }
167
168 metrics::HEARTBEAT_REGION_LEASES
169 .with_label_values(&["leader"])
170 .set(leader_region_lease_count);
171 metrics::HEARTBEAT_REGION_LEASES
172 .with_label_values(&["follower"])
173 .set(follower_region_lease_count);
174 }
175 let ctx = HeartbeatResponseHandlerContext::new(mailbox.clone(), res);
176 if let Err(e) = Self::handle_response(ctx, handler_executor.clone()).await {
177 error!(e; "Error while handling heartbeat response");
178 }
179 if let Some(notify) = notify.take() {
180 notify.notify_one();
181 }
182 if !running.load(Ordering::Acquire) {
183 info!("Heartbeat task shutdown");
184 }
185 }
186 quit_signal.notify_one();
187 info!("Heartbeat handling loop exit.");
188 });
189 Ok((tx, config))
190 }
191
192 async fn handle_response(
193 ctx: HeartbeatResponseHandlerContext,
194 handler_executor: HeartbeatResponseHandlerExecutorRef,
195 ) -> Result<()> {
196 trace!("Heartbeat response: {:?}", ctx.response);
197 handler_executor
198 .handle(ctx)
199 .await
200 .context(error::HandleHeartbeatResponseSnafu)
201 }
202
203 #[allow(deprecated)]
204 pub async fn start(
206 &self,
207 event_receiver: RegionServerEventReceiver,
208 notify: Option<Arc<Notify>>,
209 ) -> Result<()> {
210 let running = self.running.clone();
211 if running
212 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
213 .is_err()
214 {
215 warn!("Heartbeat task started multiple times");
216 return Ok(());
217 }
218 let node_id = self.node_id;
219 let node_epoch = self.node_epoch;
220 let addr = &self.peer_addr;
221
222 let meta_client = self.meta_client.clone();
223 let region_server_clone = self.region_server.clone();
224
225 let handler_executor = self.resp_handler_executor.clone();
226
227 let (outgoing_tx, mut outgoing_rx) = mpsc::channel(16);
228 let mailbox = Arc::new(HeartbeatMailbox::new(outgoing_tx));
229
230 let quit_signal = Arc::new(Notify::new());
231 let local_gc_enabled = self
232 .region_server
233 .mito_engine()
234 .context(RegionEngineNotFoundSnafu { name: "mito" })?
235 .mito_config()
236 .gc
237 .enable;
238
239 let (mut tx, config) = Self::create_streams(
240 &meta_client,
241 local_gc_enabled,
242 running.clone(),
243 handler_executor.clone(),
244 mailbox.clone(),
245 notify,
246 quit_signal.clone(),
247 )
248 .await?;
249
250 let interval = config.interval.as_millis() as u64;
251 let mut retry_interval = config.retry_interval;
252
253 self.region_alive_keeper.update_heartbeat_interval(interval);
255
256 info!(
257 "Starting heartbeat to Metasrv with config: {}. My node id is {}, address is {}.",
258 config, node_id, addr
259 );
260
261 let self_peer = Some(Peer {
262 id: node_id,
263 addr: addr.clone(),
264 });
265 let epoch = self.region_alive_keeper.epoch();
266 let workload_types = self.workload_types.clone();
267
268 self.region_alive_keeper.start(Some(event_receiver)).await?;
269 let mut last_sent = Instant::now();
270 let total_cpu_millicores = self.resource_stat.get_total_cpu_millicores();
271 let total_memory_bytes = self.resource_stat.get_total_memory_bytes();
272 let resource_stat = self.resource_stat.clone();
273 let region_alive_keeper = self.region_alive_keeper.clone();
274 let gc_limiter = self
275 .region_server
276 .mito_engine()
277 .context(RegionEngineNotFoundSnafu { name: "mito" })?
278 .gc_limiter();
279 let mut env_var_extensions = HashMap::new();
280 self.env_vars.into_extensions(&mut env_var_extensions);
281
282 common_runtime::spawn_hb(async move {
283 let sleep = tokio::time::sleep(Duration::from_millis(0));
284 tokio::pin!(sleep);
285
286 let build_info = common_version::build_info();
287
288 let heartbeat_request = HeartbeatRequest {
289 peer: self_peer,
290 node_epoch,
291 info: Some(NodeInfo {
292 version: build_info.version.to_string(),
293 git_commit: build_info.commit_short.to_string(),
294 start_time_ms: node_epoch,
295 total_cpu_millicores,
296 total_memory_bytes,
297 cpu_usage_millicores: 0,
298 memory_usage_bytes: 0,
299 cpus: total_cpu_millicores as u32,
301 memory_bytes: total_memory_bytes as u64,
302 hostname: hostname::get()
303 .unwrap_or_default()
304 .to_string_lossy()
305 .to_string(),
306 }),
307 node_workloads: Some(NodeWorkloads::Datanode(DatanodeWorkloads {
308 types: workload_types.iter().map(|w| w.to_i32()).collect(),
309 })),
310 ..Default::default()
311 };
312
313 loop {
314 if !running.load(Ordering::Relaxed) {
315 info!("shutdown heartbeat task");
316 break;
317 }
318 let req = tokio::select! {
319 message = outgoing_rx.recv() => {
320 if let Some(message) = message {
321 match outgoing_message_to_mailbox_message(message) {
322 Ok(message) => {
323 let mut extensions = env_var_extensions.clone();
324 let gc_stat = gc_limiter.gc_stat();
325 gc_stat.into_extensions(&mut extensions);
326
327 let req = HeartbeatRequest {
328 mailbox_message: Some(message),
329 extensions,
330 ..heartbeat_request.clone()
331 };
332 HEARTBEAT_RECV_COUNT.with_label_values(&["success"]).inc();
333 Some(req)
334 }
335 Err(e) => {
336 error!(e; "Failed to encode mailbox messages!");
337 HEARTBEAT_RECV_COUNT.with_label_values(&["error"]).inc();
338 None
339 }
340 }
341 } else {
342 None
343 }
344 }
345 _ = &mut sleep => {
346 let region_stats = Self::load_region_stats(®ion_server_clone);
347 let topic_stats = region_server_clone.topic_stats();
348 let now = Instant::now();
349 let duration_since_epoch = (now - epoch).as_millis() as u64;
350
351 let mut extensions = env_var_extensions.clone();
352 let gc_stat = gc_limiter.gc_stat();
353 gc_stat.into_extensions(&mut extensions);
354
355 let mut req = HeartbeatRequest {
356 region_stats,
357 topic_stats,
358 duration_since_epoch,
359 extensions,
360 ..heartbeat_request.clone()
361 };
362
363 if let Some(info) = req.info.as_mut() {
364 info.cpu_usage_millicores = resource_stat.get_cpu_usage_millicores();
365 info.memory_usage_bytes = resource_stat.get_memory_usage_bytes();
366 }
367
368 sleep.as_mut().reset(now + Duration::from_millis(interval));
369 Some(req)
370 }
371 _ = quit_signal.notified() => {
373 let req = HeartbeatRequest::default();
374 Some(req)
375 }
376 };
377 if let Some(req) = req {
378 metrics::LAST_SENT_HEARTBEAT_ELAPSED
379 .set(last_sent.elapsed().as_millis() as i64);
380 last_sent = Instant::now();
382 debug!("Sending heartbeat request: {:?}", req);
383 if let Err(e) = tx.send(req).await {
384 error!(e; "Failed to send heartbeat to metasrv");
385 match Self::create_streams(
386 &meta_client,
387 local_gc_enabled,
388 running.clone(),
389 handler_executor.clone(),
390 mailbox.clone(),
391 None,
392 quit_signal.clone(),
393 )
394 .await
395 {
396 Ok((new_tx, new_config)) => {
397 info!("Reconnected to metasrv, heartbeat config: {}", new_config);
398 tx = new_tx;
399 retry_interval = new_config.retry_interval;
401 region_alive_keeper.update_heartbeat_interval(
403 new_config.interval.as_millis() as u64,
404 );
405 sleep.as_mut().reset(Instant::now());
407 }
408 Err(e) => {
409 sleep.as_mut().reset(Instant::now() + retry_interval);
413 error!(e; "Failed to reconnect to metasrv!");
414 }
415 }
416 } else {
417 HEARTBEAT_SENT_COUNT.inc();
418 }
419 }
420 }
421 });
422
423 Ok(())
424 }
425
426 fn load_region_stats(region_server: &RegionServer) -> Vec<RegionStat> {
427 region_server
428 .reportable_regions()
429 .into_iter()
430 .map(|stat| {
431 let region_stat = region_server
432 .region_statistic(stat.region_id)
433 .unwrap_or_default();
434 let mut extensions = HashMap::new();
435 if let Some(serialized) = region_stat.serialize_to_vec() {
436 extensions.insert(REGION_STATISTIC_KEY.to_string(), serialized);
437 }
438 RegionStat {
439 region_id: stat.region_id.as_u64(),
440 engine: stat.engine,
441 role: RegionRole::from(stat.role).into(),
442 rcus: 0,
443 wcus: 0,
444 approximate_bytes: region_stat.estimated_disk_size() as i64,
445 extensions,
446 }
447 })
448 .collect()
449 }
450
451 pub fn close(&self) -> Result<()> {
452 let running = self.running.clone();
453 if running
454 .compare_exchange(true, false, Ordering::AcqRel, Ordering::Acquire)
455 .is_err()
456 {
457 warn!("Call close heartbeat task multiple times");
458 }
459
460 Ok(())
461 }
462}