Skip to main content

meta_client/client/
heartbeat.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::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, HEARTBEAT_TIMEOUT};
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/// Heartbeat configuration received from Metasrv during handshake.
37#[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    /// Extract configuration from HeartbeatResponse.
66    pub fn from_response(res: &HeartbeatResponse) -> Self {
67        if let Some(cfg) = &res.heartbeat_config {
68            // Metasrv provided complete configuration
69            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    /// Fetch the next message from this stream.
136    #[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    /// Start the client with a [LeaderProvider].
159    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 handshake = async {
253            let mut stream = leader
254                .heartbeat(receiver)
255                .await
256                .map_err(error::Error::from)?
257                .into_inner();
258
259            let res = stream
260                .message()
261                .await
262                .map_err(error::Error::from)?
263                .context(error::CreateHeartbeatStreamSnafu)?;
264            Ok::<_, error::Error>((stream, res))
265        };
266
267        let (stream, res) = tokio::time::timeout(HEARTBEAT_TIMEOUT, handshake)
268            .await
269            .with_context(|_| error::HeartbeatHandshakeTimeoutSnafu {
270                timeout: HEARTBEAT_TIMEOUT,
271            })??;
272
273        let config = HeartbeatConfig::from_response(&res);
274
275        info!(
276            "Handshake successful with Metasrv at {}, received config: {}",
277            leader_addr, config
278        );
279
280        Ok((
281            HeartbeatSender::new(self.id, self.role, sender),
282            HeartbeatStream::new(self.id, stream),
283            config,
284        ))
285    }
286
287    fn make_client(&self, addr: impl AsRef<str>) -> Result<HeartbeatClient<Channel>> {
288        let channel = self
289            .channel_manager
290            .get(addr)
291            .context(error::CreateChannelSnafu)?;
292
293        Ok(common_grpc::configure_tonic_client!(
294            HeartbeatClient::new(channel),
295            self.channel_manager,
296        ))
297    }
298
299    #[inline]
300    pub(crate) fn is_started(&self) -> bool {
301        self.leader_provider.is_some()
302    }
303}
304
305#[cfg(test)]
306mod test {
307    use super::*;
308    use crate::client::AskLeader;
309
310    #[tokio::test]
311    async fn test_already_start() {
312        let client = Client::new(0, Role::Datanode, ChannelManager::default());
313        let leader_provider = Arc::new(AskLeader::new(
314            0,
315            Role::Datanode,
316            vec!["127.0.0.1:1000".to_string(), "127.0.0.1:1001".to_string()],
317            ChannelManager::default(),
318            3,
319        ));
320        client.start_with(leader_provider.clone()).await.unwrap();
321        let res = client.start_with(leader_provider).await;
322        assert!(res.is_err());
323        assert!(matches!(
324            res.err(),
325            Some(error::Error::IllegalGrpcClientState { .. })
326        ));
327    }
328
329    #[tokio::test]
330    async fn test_heartbeat_stream() {
331        let (sender, mut receiver) = mpsc::channel::<HeartbeatRequest>(100);
332        let sender = HeartbeatSender::new(8, Role::Datanode, sender);
333        let _handle = tokio::spawn(async move {
334            for _ in 0..10 {
335                sender.send(HeartbeatRequest::default()).await.unwrap();
336            }
337        });
338        while let Some(req) = receiver.recv().await {
339            let header = req.header.unwrap();
340            assert_eq!(8, header.member_id);
341        }
342    }
343}