1use std::fmt;
16use std::sync::Arc;
17use std::time::Duration;
18
19use api::v1::meta::heartbeat_client::HeartbeatClient;
20use api::v1::meta::{HeartbeatRequest, HeartbeatResponse, RequestHeader, Role};
21use common_grpc::channel_manager::ChannelManager;
22use common_meta::distributed_time_constants::BASE_HEARTBEAT_INTERVAL;
23use common_meta::util;
24use common_telemetry::tracing_context::TracingContext;
25use common_telemetry::{info, warn};
26use snafu::{OptionExt, ResultExt, ensure};
27use tokio::sync::{RwLock, mpsc};
28use tokio_stream::wrappers::ReceiverStream;
29use tonic::Streaming;
30use tonic::transport::Channel;
31
32use crate::client::{Id, LeaderProviderRef};
33use crate::error;
34use crate::error::{InvalidResponseHeaderSnafu, Result};
35
36#[derive(Debug, Clone, Copy)]
38pub struct HeartbeatConfig {
39 pub interval: Duration,
40 pub retry_interval: Duration,
41 pub gc_enabled: bool,
42}
43
44impl Default for HeartbeatConfig {
45 fn default() -> Self {
46 Self {
47 interval: BASE_HEARTBEAT_INTERVAL,
48 retry_interval: BASE_HEARTBEAT_INTERVAL,
49 gc_enabled: false,
50 }
51 }
52}
53
54impl fmt::Display for HeartbeatConfig {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 write!(
57 f,
58 "interval={:?}, retry={:?}, gc_enabled={}",
59 self.interval, self.retry_interval, self.gc_enabled
60 )
61 }
62}
63
64impl HeartbeatConfig {
65 pub fn from_response(res: &HeartbeatResponse) -> Self {
67 if let Some(cfg) = &res.heartbeat_config {
68 Self {
70 interval: Duration::from_millis(cfg.heartbeat_interval_ms),
71 retry_interval: Duration::from_millis(cfg.retry_interval_ms),
72 gc_enabled: cfg.gc_enabled,
73 }
74 } else {
75 let fallback = Self::default();
76 warn!(
77 "Metasrv didn't provide heartbeat_config, using default: {}",
78 fallback
79 );
80 fallback
81 }
82 }
83}
84
85pub struct HeartbeatSender {
86 id: Id,
87 role: Role,
88 sender: mpsc::Sender<HeartbeatRequest>,
89}
90
91impl HeartbeatSender {
92 #[inline]
93 fn new(id: Id, role: Role, sender: mpsc::Sender<HeartbeatRequest>) -> Self {
94 Self { id, role, sender }
95 }
96
97 #[inline]
98 pub fn id(&self) -> Id {
99 self.id
100 }
101
102 #[inline]
103 pub async fn send(&self, mut req: HeartbeatRequest) -> Result<()> {
104 req.set_header(
105 self.id,
106 self.role,
107 TracingContext::from_current_span().to_w3c(),
108 );
109 self.sender.send(req).await.map_err(|e| {
110 error::SendHeartbeatSnafu {
111 err_msg: e.to_string(),
112 }
113 .build()
114 })
115 }
116}
117
118#[derive(Debug)]
119pub struct HeartbeatStream {
120 id: Id,
121 stream: Streaming<HeartbeatResponse>,
122}
123
124impl HeartbeatStream {
125 #[inline]
126 fn new(id: Id, stream: Streaming<HeartbeatResponse>) -> Self {
127 Self { id, stream }
128 }
129
130 #[inline]
131 pub fn id(&self) -> Id {
132 self.id
133 }
134
135 #[inline]
137 pub async fn message(&mut self) -> Result<Option<HeartbeatResponse>> {
138 let res = self.stream.message().await.map_err(error::Error::from);
139 if let Ok(Some(heartbeat)) = &res {
140 util::check_response_header(heartbeat.header.as_ref())
141 .context(InvalidResponseHeaderSnafu)?;
142 }
143 res
144 }
145}
146
147#[derive(Clone, Debug)]
148pub struct Client {
149 inner: Arc<RwLock<Inner>>,
150}
151
152impl Client {
153 pub fn new(id: Id, role: Role, channel_manager: ChannelManager) -> Self {
154 let inner = Arc::new(RwLock::new(Inner::new(id, role, channel_manager)));
155 Self { inner }
156 }
157
158 pub(crate) async fn start_with(&self, leader_provider: LeaderProviderRef) -> Result<()> {
160 let mut inner = self.inner.write().await;
161 inner.start_with(leader_provider)
162 }
163
164 pub async fn ask_leader(&mut self) -> Result<String> {
165 let inner = self.inner.read().await;
166 inner.ask_leader().await
167 }
168
169 pub async fn heartbeat(
170 &mut self,
171 ) -> Result<(HeartbeatSender, HeartbeatStream, HeartbeatConfig)> {
172 let inner = self.inner.read().await;
173 inner.ask_leader().await?;
174 inner.heartbeat().await
175 }
176}
177
178#[derive(Debug)]
179struct Inner {
180 id: Id,
181 role: Role,
182 channel_manager: ChannelManager,
183 leader_provider: Option<LeaderProviderRef>,
184}
185
186impl Inner {
187 fn new(id: Id, role: Role, channel_manager: ChannelManager) -> Self {
188 Self {
189 id,
190 role,
191 channel_manager,
192 leader_provider: None,
193 }
194 }
195
196 fn start_with(&mut self, leader_provider: LeaderProviderRef) -> Result<()> {
197 ensure!(
198 !self.is_started(),
199 error::IllegalGrpcClientStateSnafu {
200 err_msg: "Heartbeat client already started"
201 }
202 );
203 self.leader_provider = Some(leader_provider);
204 Ok(())
205 }
206
207 async fn ask_leader(&self) -> Result<String> {
208 let Some(leader_provider) = self.leader_provider.as_ref() else {
209 return error::IllegalGrpcClientStateSnafu {
210 err_msg: "not started",
211 }
212 .fail();
213 };
214 leader_provider.ask_leader().await
215 }
216
217 async fn heartbeat(&self) -> Result<(HeartbeatSender, HeartbeatStream, HeartbeatConfig)> {
218 ensure!(
219 self.is_started(),
220 error::IllegalGrpcClientStateSnafu {
221 err_msg: "Heartbeat client not start"
222 }
223 );
224
225 let leader_addr = self
226 .leader_provider
227 .as_ref()
228 .unwrap()
229 .leader()
230 .context(error::NoLeaderSnafu)?;
231 let mut leader = self.make_client(&leader_addr)?;
232
233 let (sender, receiver) = mpsc::channel::<HeartbeatRequest>(128);
234
235 let header = RequestHeader::new(
236 self.id,
237 self.role,
238 TracingContext::from_current_span().to_w3c(),
239 );
240 let handshake = HeartbeatRequest {
241 header: Some(header),
242 ..Default::default()
243 };
244 sender.send(handshake).await.map_err(|e| {
245 error::SendHeartbeatSnafu {
246 err_msg: e.to_string(),
247 }
248 .build()
249 })?;
250 let receiver = ReceiverStream::new(receiver);
251
252 let mut stream = leader
253 .heartbeat(receiver)
254 .await
255 .map_err(error::Error::from)?
256 .into_inner();
257
258 let res = stream
259 .message()
260 .await
261 .map_err(error::Error::from)?
262 .context(error::CreateHeartbeatStreamSnafu)?;
263
264 let config = HeartbeatConfig::from_response(&res);
265
266 info!(
267 "Handshake successful with Metasrv at {}, received config: {}",
268 leader_addr, config
269 );
270
271 Ok((
272 HeartbeatSender::new(self.id, self.role, sender),
273 HeartbeatStream::new(self.id, stream),
274 config,
275 ))
276 }
277
278 fn make_client(&self, addr: impl AsRef<str>) -> Result<HeartbeatClient<Channel>> {
279 let channel = self
280 .channel_manager
281 .get(addr)
282 .context(error::CreateChannelSnafu)?;
283
284 Ok(common_grpc::configure_tonic_client!(
285 HeartbeatClient::new(channel),
286 self.channel_manager,
287 ))
288 }
289
290 #[inline]
291 pub(crate) fn is_started(&self) -> bool {
292 self.leader_provider.is_some()
293 }
294}
295
296#[cfg(test)]
297mod test {
298 use super::*;
299 use crate::client::AskLeader;
300
301 #[tokio::test]
302 async fn test_already_start() {
303 let client = Client::new(0, Role::Datanode, ChannelManager::default());
304 let leader_provider = Arc::new(AskLeader::new(
305 0,
306 Role::Datanode,
307 vec!["127.0.0.1:1000".to_string(), "127.0.0.1:1001".to_string()],
308 ChannelManager::default(),
309 3,
310 ));
311 client.start_with(leader_provider.clone()).await.unwrap();
312 let res = client.start_with(leader_provider).await;
313 assert!(res.is_err());
314 assert!(matches!(
315 res.err(),
316 Some(error::Error::IllegalGrpcClientState { .. })
317 ));
318 }
319
320 #[tokio::test]
321 async fn test_heartbeat_stream() {
322 let (sender, mut receiver) = mpsc::channel::<HeartbeatRequest>(100);
323 let sender = HeartbeatSender::new(8, Role::Datanode, sender);
324 let _handle = tokio::spawn(async move {
325 for _ in 0..10 {
326 sender.send(HeartbeatRequest::default()).await.unwrap();
327 }
328 });
329 while let Some(req) = receiver.recv().await {
330 let header = req.header.unwrap();
331 assert_eq!(8, header.member_id);
332 }
333 }
334}