Skip to main content

meta_srv/service/
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::io::ErrorKind;
16use std::sync::Arc;
17use std::sync::atomic::AtomicU64;
18
19use api::v1::meta::{
20    AskLeaderRequest, AskLeaderResponse, HeartbeatRequest, HeartbeatResponse, Peer, RequestHeader,
21    ResponseHeader, Role, heartbeat_server,
22};
23use common_meta::election::LeaderChangeMessage;
24use common_telemetry::{debug, error, info, warn};
25use futures::StreamExt;
26use once_cell::sync::OnceCell;
27use snafu::{OptionExt, ResultExt};
28use tokio::sync::broadcast::error::RecvError;
29use tokio::sync::mpsc;
30use tokio::sync::mpsc::Sender;
31use tokio_stream::wrappers::ReceiverStream;
32use tonic::{Request, Response, Status, Streaming};
33
34use crate::error::{self, Result};
35use crate::handler::{HeartbeatHandlerGroup, Pusher, PusherId};
36use crate::metasrv::{Context, ElectionRef, Metasrv};
37use crate::metrics::METRIC_META_HEARTBEAT_RECV;
38use crate::service::{GrpcResult, GrpcStream};
39
40type HeartbeatResponseResult = std::result::Result<HeartbeatResponse, Status>;
41
42#[async_trait::async_trait]
43trait HeartbeatRequestStream {
44    async fn next(&mut self) -> Option<std::result::Result<HeartbeatRequest, Status>>;
45}
46
47struct TonicHeartbeatRequestStream {
48    inner: Streaming<HeartbeatRequest>,
49}
50
51impl TonicHeartbeatRequestStream {
52    fn new(inner: Streaming<HeartbeatRequest>) -> Self {
53        Self { inner }
54    }
55}
56
57#[async_trait::async_trait]
58impl HeartbeatRequestStream for TonicHeartbeatRequestStream {
59    async fn next(&mut self) -> Option<std::result::Result<HeartbeatRequest, Status>> {
60        self.inner.next().await
61    }
62}
63
64enum LeaderStepDownEvent {
65    StepDown,
66    Closed,
67}
68
69#[async_trait::async_trait]
70trait LeaderStepDown {
71    async fn wait(&mut self) -> LeaderStepDownEvent;
72}
73
74struct ElectionLeaderStepDown {
75    rx: tokio::sync::broadcast::Receiver<LeaderChangeMessage>,
76}
77
78impl ElectionLeaderStepDown {
79    fn new(election: ElectionRef) -> Self {
80        Self {
81            rx: election.subscribe_leader_change(),
82        }
83    }
84}
85
86#[async_trait::async_trait]
87impl LeaderStepDown for ElectionLeaderStepDown {
88    async fn wait(&mut self) -> LeaderStepDownEvent {
89        loop {
90            match self.rx.recv().await {
91                Ok(LeaderChangeMessage::StepDown(_)) => return LeaderStepDownEvent::StepDown,
92                Ok(LeaderChangeMessage::Elected(_)) => {}
93                Err(RecvError::Lagged(skipped)) => {
94                    warn!(
95                        "Leader step-down watcher lagged, skipped {} leader change events",
96                        skipped
97                    );
98                }
99                Err(RecvError::Closed) => return LeaderStepDownEvent::Closed,
100            }
101        }
102    }
103}
104
105struct HeartbeatSession<R, L> {
106    requests: R,
107    tx: Sender<HeartbeatResponseResult>,
108    leader_step_down: Option<L>,
109    handler_group: Arc<HeartbeatHandlerGroup>,
110    ctx: Context,
111    sender_id: PusherId,
112}
113
114impl<R, L> HeartbeatSession<R, L>
115where
116    R: HeartbeatRequestStream,
117    L: LeaderStepDown,
118{
119    /// Initializes the heartbeat session by receiving the first request,
120    /// and returns `None` if the stream is closed or an error occurs.
121    async fn init(
122        mut requests: R,
123        tx: Sender<HeartbeatResponseResult>,
124        leader_step_down: Option<L>,
125        handler_group: Arc<HeartbeatHandlerGroup>,
126        ctx: Context,
127    ) -> Option<Self> {
128        let msg = requests.next().await?;
129
130        let req = match msg {
131            Ok(req) => req,
132            Err(err) => {
133                error!("Failed to receive the first heartbeat request, error: {err}");
134                let _ = handle_request_stream_error(None, &tx, err).await;
135                return None;
136            }
137        };
138
139        let Some(header) = req.header.as_ref() else {
140            error!("Exit on malformed request: MissingRequestHeader");
141            let _ = tx
142                .send(Err(error::MissingRequestHeaderSnafu {}.build().into()))
143                .await;
144            return None;
145        };
146
147        let sender_id = register_pusher(&handler_group, header, tx.clone()).await;
148        let mut session = Self {
149            requests,
150            tx,
151            leader_step_down,
152            handler_group,
153            ctx,
154            sender_id,
155        };
156
157        if session.handle_request(req, true).await {
158            Some(session)
159        } else {
160            session.cleanup().await;
161            None
162        }
163    }
164
165    /// Runs the heartbeat session until the stream is closed or an error occurs.
166    async fn run(mut self) {
167        let mut leader_step_down = self.leader_step_down.take();
168
169        loop {
170            tokio::select! {
171                msg = self.requests.next() => {
172                    let Some(msg) = msg else {
173                        break;
174                    };
175
176                    if !self.handle_message(msg).await {
177                        break;
178                    }
179                }
180                event = wait_leader_step_down(leader_step_down.as_mut()), if leader_step_down.is_some() => {
181                    match event {
182                        LeaderStepDownEvent::StepDown => {
183                            self.send_not_leader_error().await;
184                            break;
185                        }
186                        LeaderStepDownEvent::Closed => {
187                            warn!("Leader step-down watcher closed");
188                            self.send_election_unavailable_error().await;
189                            break;
190                        }
191                    }
192                }
193            }
194        }
195
196        self.cleanup().await;
197    }
198
199    /// Handles the incoming message, and returns whether to continue the session.
200    async fn handle_message(&mut self, msg: std::result::Result<HeartbeatRequest, Status>) -> bool {
201        match msg {
202            Ok(req) => self.handle_request(req, false).await,
203            Err(err) => handle_request_stream_error(Some(self.sender_id), &self.tx, err).await,
204        }
205    }
206
207    /// Handles the incoming heartbeat request, and returns whether to continue the session.
208    async fn handle_request(&mut self, req: HeartbeatRequest, is_handshake: bool) -> bool {
209        let mailbox_message_id = req.mailbox_message.as_ref().map(|msg| msg.id);
210        debug!(
211            sender_id = %self.sender_id,
212            ?mailbox_message_id,
213            is_handshake,
214            "Receiving heartbeat request"
215        );
216
217        let sender_id = self.sender_id.to_string();
218        METRIC_META_HEARTBEAT_RECV
219            .with_label_values(&[sender_id.as_str()])
220            .inc();
221
222        let res = self
223            .handler_group
224            .handle(req, self.ctx.clone().with_handshake(is_handshake))
225            .await
226            .inspect_err(
227                |e| warn!(e; "Failed to handle heartbeat request, sender: {}", self.sender_id),
228            )
229            .map_err(|e| e.into());
230
231        let is_not_leader = res.as_ref().is_ok_and(|r| r.is_not_leader());
232
233        debug!(
234            sender_id = %self.sender_id,
235            success = res.is_ok(),
236            is_not_leader,
237            "Sending heartbeat response"
238        );
239
240        if self.tx.send(res).await.is_err() {
241            info!(
242                "ReceiverStream was dropped; shutting down, sender: {}",
243                self.sender_id
244            );
245            return false;
246        }
247
248        if is_not_leader {
249            warn!(
250                "Quit because it is no longer the leader, sender: {}",
251                self.sender_id
252            );
253            self.send_not_leader_error().await;
254            return false;
255        }
256
257        true
258    }
259
260    async fn send_not_leader_error(&mut self) {
261        let _ = self
262            .tx
263            .send(Err(Status::aborted(format!(
264                "The requested metasrv node is not leader, node addr: {}",
265                self.ctx.server_addr
266            ))))
267            .await;
268    }
269
270    async fn send_election_unavailable_error(&mut self) {
271        let _ = self
272            .tx
273            .send(Err(Status::unavailable(format!(
274                "The requested metasrv node is shutting down, node addr: {}",
275                self.ctx.server_addr
276            ))))
277            .await;
278    }
279
280    async fn cleanup(&self) {
281        info!("Heartbeat stream closed, sender: {}", self.sender_id);
282        let _ = self.handler_group.deregister_push(self.sender_id).await;
283    }
284}
285
286async fn wait_leader_step_down<L>(leader_step_down: Option<&mut L>) -> LeaderStepDownEvent
287where
288    L: LeaderStepDown,
289{
290    match leader_step_down {
291        Some(leader_step_down) => leader_step_down.wait().await,
292        None => std::future::pending().await,
293    }
294}
295
296/// Handles request stream error by logging and forwarding the error to the client if possible.
297///
298/// Returns `false` if the stream should be terminated.
299async fn handle_request_stream_error(
300    sender_id: Option<PusherId>,
301    tx: &Sender<HeartbeatResponseResult>,
302    err: Status,
303) -> bool {
304    if let Some(io_err) = error::match_for_io_error(&err)
305        && io_err.kind() == ErrorKind::BrokenPipe
306    {
307        error!("Client disconnected: broken pipe, sender: {:?}", sender_id);
308        return false;
309    }
310    error!(err; "Error while receiving heartbeat request, sender: {:?}", sender_id);
311
312    if tx.send(Err(err)).await.is_err() {
313        info!(
314            "Failed to forward heartbeat request stream error; response stream was dropped, sender: {:?}",
315            sender_id
316        );
317        return false;
318    }
319
320    true
321}
322
323#[async_trait::async_trait]
324impl heartbeat_server::Heartbeat for Metasrv {
325    type HeartbeatStream = GrpcStream<HeartbeatResponse>;
326
327    async fn heartbeat(
328        &self,
329        req: Request<Streaming<HeartbeatRequest>>,
330    ) -> GrpcResult<Self::HeartbeatStream> {
331        let (tx, rx) = mpsc::channel(128);
332        let handler_group = self.handler_group().context(error::UnexpectedSnafu {
333            violated: "expected heartbeat handlers",
334        })?;
335
336        let ctx = self.new_ctx();
337        let requests = TonicHeartbeatRequestStream::new(req.into_inner());
338        let _handle = common_runtime::spawn_global(async move {
339            if let Some(session) = HeartbeatSession::init(
340                requests,
341                tx,
342                ctx.election
343                    .as_ref()
344                    .map(|r| ElectionLeaderStepDown::new(r.clone())),
345                handler_group,
346                ctx,
347            )
348            .await
349            {
350                session.run().await;
351            }
352        });
353
354        let out_stream = ReceiverStream::new(rx);
355
356        Ok(Response::new(Box::pin(out_stream)))
357    }
358
359    async fn ask_leader(&self, req: Request<AskLeaderRequest>) -> GrpcResult<AskLeaderResponse> {
360        let req = req.into_inner();
361        let ctx = self.new_ctx();
362        let res = handle_ask_leader(req, ctx).await?;
363
364        Ok(Response::new(res))
365    }
366}
367
368async fn handle_ask_leader(_req: AskLeaderRequest, ctx: Context) -> Result<AskLeaderResponse> {
369    let addr = match ctx.election {
370        Some(election) => {
371            if election.is_leader() {
372                ctx.server_addr
373            } else {
374                election.leader().await.context(error::KvBackendSnafu)?.0
375            }
376        }
377        None => ctx.server_addr,
378    };
379
380    let leader = Some(Peer {
381        id: 0, // TODO(jiachun): meta node should have a Id
382        addr,
383    });
384
385    let header = Some(ResponseHeader::success());
386    Ok(AskLeaderResponse { header, leader })
387}
388
389fn get_node_id(header: &RequestHeader) -> u64 {
390    static ID: OnceCell<Arc<AtomicU64>> = OnceCell::new();
391
392    fn next_id() -> u64 {
393        let id = ID.get_or_init(|| Arc::new(AtomicU64::new(0))).clone();
394        id.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
395    }
396
397    match header.role() {
398        Role::Frontend => next_id(),
399        Role::Datanode | Role::Flownode => header.member_id,
400    }
401}
402
403async fn register_pusher(
404    handler_group: &HeartbeatHandlerGroup,
405    header: &RequestHeader,
406    sender: Sender<std::result::Result<HeartbeatResponse, tonic::Status>>,
407) -> PusherId {
408    let role = header.role();
409    let id = get_node_id(header);
410    let pusher_id = PusherId::new(role, id);
411    let pusher = Pusher::new(sender);
412    handler_group.register_pusher(pusher_id, pusher).await;
413    pusher_id
414}
415
416#[cfg(test)]
417mod tests {
418    use std::collections::VecDeque;
419    use std::sync::Arc;
420
421    use api::v1::meta::heartbeat_server::Heartbeat;
422    use api::v1::meta::*;
423    use common_meta::kv_backend::memory::MemoryKvBackend;
424    use common_telemetry::tracing_context::W3cTrace;
425    use servers::grpc::GrpcOptions;
426    use tokio::sync::mpsc;
427    use tonic::{Code, IntoRequest};
428
429    use super::*;
430    use crate::handler::test_utils::TestEnv;
431    use crate::metasrv::MetasrvOptions;
432    use crate::metasrv::builder::MetasrvBuilder;
433
434    struct MockHeartbeatRequestStream {
435        messages: VecDeque<std::result::Result<HeartbeatRequest, Status>>,
436        pending_when_empty: bool,
437    }
438
439    impl MockHeartbeatRequestStream {
440        fn new(messages: Vec<std::result::Result<HeartbeatRequest, Status>>) -> Self {
441            Self {
442                messages: messages.into(),
443                pending_when_empty: false,
444            }
445        }
446
447        fn pending_after(messages: Vec<std::result::Result<HeartbeatRequest, Status>>) -> Self {
448            Self {
449                messages: messages.into(),
450                pending_when_empty: true,
451            }
452        }
453    }
454
455    #[async_trait::async_trait]
456    impl HeartbeatRequestStream for MockHeartbeatRequestStream {
457        async fn next(&mut self) -> Option<std::result::Result<HeartbeatRequest, Status>> {
458            if let Some(message) = self.messages.pop_front() {
459                return Some(message);
460            }
461
462            if self.pending_when_empty {
463                std::future::pending().await
464            } else {
465                None
466            }
467        }
468    }
469
470    struct MockLeaderStepDown {
471        event: Option<LeaderStepDownEvent>,
472    }
473
474    impl MockLeaderStepDown {
475        fn new(event: LeaderStepDownEvent) -> Self {
476            Self { event: Some(event) }
477        }
478    }
479
480    #[async_trait::async_trait]
481    impl LeaderStepDown for MockLeaderStepDown {
482        async fn wait(&mut self) -> LeaderStepDownEvent {
483            self.event.take().unwrap()
484        }
485    }
486
487    fn heartbeat_request(role: Role, member_id: u64) -> HeartbeatRequest {
488        HeartbeatRequest {
489            header: Some(RequestHeader {
490                role: role.into(),
491                member_id,
492                ..Default::default()
493            }),
494            ..Default::default()
495        }
496    }
497
498    fn sender_id(role: Role, member_id: u64) -> PusherId {
499        PusherId::new(role, member_id)
500    }
501
502    fn test_context() -> Context {
503        TestEnv::new().ctx()
504    }
505
506    fn test_handler_group() -> Arc<HeartbeatHandlerGroup> {
507        Arc::new(HeartbeatHandlerGroup::default())
508    }
509
510    async fn init_session<L>(
511        requests: MockHeartbeatRequestStream,
512        tx: Sender<HeartbeatResponseResult>,
513        leader_step_down: Option<L>,
514        handler_group: Arc<HeartbeatHandlerGroup>,
515    ) -> Option<HeartbeatSession<MockHeartbeatRequestStream, L>>
516    where
517        L: LeaderStepDown,
518    {
519        HeartbeatSession::init(
520            requests,
521            tx,
522            leader_step_down,
523            handler_group,
524            test_context(),
525        )
526        .await
527    }
528
529    async fn recv_response(
530        rx: &mut mpsc::Receiver<HeartbeatResponseResult>,
531    ) -> HeartbeatResponseResult {
532        rx.recv().await.unwrap()
533    }
534
535    #[tokio::test]
536    async fn test_heartbeat_session_init_returns_none_on_empty_stream() {
537        let (tx, _rx) = mpsc::channel(8);
538        let handler_group = test_handler_group();
539        let requests = MockHeartbeatRequestStream::new(vec![]);
540
541        let session = init_session(
542            requests,
543            tx,
544            None::<MockLeaderStepDown>,
545            handler_group.clone(),
546        )
547        .await;
548
549        assert!(session.is_none());
550        assert!(
551            !handler_group
552                .contains_pusher(&sender_id(Role::Datanode, 42))
553                .await
554        );
555    }
556
557    #[tokio::test]
558    async fn test_heartbeat_session_init_forwards_first_stream_error() {
559        let (tx, mut rx) = mpsc::channel(8);
560        let handler_group = test_handler_group();
561        let requests = MockHeartbeatRequestStream::new(vec![Err(Status::internal("boom"))]);
562
563        let session = init_session(requests, tx, None::<MockLeaderStepDown>, handler_group).await;
564
565        assert!(session.is_none());
566        let status = recv_response(&mut rx).await.unwrap_err();
567        assert_eq!(Code::Internal, status.code());
568        assert_eq!("boom", status.message());
569    }
570
571    #[tokio::test]
572    async fn test_heartbeat_session_init_sends_error_on_missing_header() {
573        let (tx, mut rx) = mpsc::channel(8);
574        let handler_group = test_handler_group();
575        let requests = MockHeartbeatRequestStream::new(vec![Ok(HeartbeatRequest::default())]);
576
577        let session = init_session(
578            requests,
579            tx,
580            None::<MockLeaderStepDown>,
581            handler_group.clone(),
582        )
583        .await;
584
585        assert!(session.is_none());
586        assert!(
587            !handler_group
588                .contains_pusher(&sender_id(Role::Datanode, 42))
589                .await
590        );
591
592        let status = recv_response(&mut rx).await.unwrap_err();
593        assert_eq!(Code::InvalidArgument, status.code());
594    }
595
596    #[tokio::test]
597    async fn test_heartbeat_session_init_registers_sender() {
598        let (tx, mut rx) = mpsc::channel(8);
599        let handler_group = test_handler_group();
600        let sender_id = sender_id(Role::Datanode, 42);
601        let requests =
602            MockHeartbeatRequestStream::new(vec![Ok(heartbeat_request(Role::Datanode, 42))]);
603
604        let session = init_session(
605            requests,
606            tx,
607            None::<MockLeaderStepDown>,
608            handler_group.clone(),
609        )
610        .await;
611
612        assert!(session.is_some());
613        assert!(handler_group.contains_pusher(&sender_id).await);
614
615        let response = recv_response(&mut rx).await.unwrap();
616        assert!(response.heartbeat_config.is_some());
617    }
618
619    #[tokio::test]
620    async fn test_heartbeat_session_run_deregisters_sender_on_stream_close() {
621        let (tx, mut rx) = mpsc::channel(8);
622        let handler_group = test_handler_group();
623        let sender_id = sender_id(Role::Datanode, 42);
624        let requests =
625            MockHeartbeatRequestStream::new(vec![Ok(heartbeat_request(Role::Datanode, 42))]);
626        let session = init_session(
627            requests,
628            tx,
629            None::<MockLeaderStepDown>,
630            handler_group.clone(),
631        )
632        .await
633        .unwrap();
634        let _ = recv_response(&mut rx).await.unwrap();
635
636        session.run().await;
637
638        assert!(!handler_group.contains_pusher(&sender_id).await);
639    }
640
641    #[tokio::test]
642    async fn test_heartbeat_session_run_forwards_stream_error_after_init() {
643        let (tx, mut rx) = mpsc::channel(8);
644        let handler_group = test_handler_group();
645        let sender_id = sender_id(Role::Datanode, 42);
646        let requests = MockHeartbeatRequestStream::new(vec![
647            Ok(heartbeat_request(Role::Datanode, 42)),
648            Err(Status::unavailable("temporary")),
649        ]);
650        let session = init_session(
651            requests,
652            tx,
653            None::<MockLeaderStepDown>,
654            handler_group.clone(),
655        )
656        .await
657        .unwrap();
658        let _ = recv_response(&mut rx).await.unwrap();
659
660        session.run().await;
661
662        let status = recv_response(&mut rx).await.unwrap_err();
663        assert_eq!(Code::Unavailable, status.code());
664        assert_eq!("temporary", status.message());
665        assert!(!handler_group.contains_pusher(&sender_id).await);
666    }
667
668    #[tokio::test]
669    async fn test_heartbeat_session_leader_step_down_sends_aborted_and_deregisters() {
670        let (tx, mut rx) = mpsc::channel(8);
671        let handler_group = test_handler_group();
672        let sender_id = sender_id(Role::Datanode, 42);
673        let requests = MockHeartbeatRequestStream::pending_after(vec![Ok(heartbeat_request(
674            Role::Datanode,
675            42,
676        ))]);
677        let session = init_session(
678            requests,
679            tx,
680            Some(MockLeaderStepDown::new(LeaderStepDownEvent::StepDown)),
681            handler_group.clone(),
682        )
683        .await
684        .unwrap();
685        let _ = recv_response(&mut rx).await.unwrap();
686
687        session.run().await;
688
689        let status = recv_response(&mut rx).await.unwrap_err();
690        assert_eq!(Code::Aborted, status.code());
691        assert!(!handler_group.contains_pusher(&sender_id).await);
692    }
693
694    #[tokio::test]
695    async fn test_heartbeat_session_leader_watcher_closed_sends_unavailable_and_deregisters() {
696        let (tx, mut rx) = mpsc::channel(8);
697        let handler_group = test_handler_group();
698        let sender_id = sender_id(Role::Datanode, 42);
699        let requests = MockHeartbeatRequestStream::pending_after(vec![Ok(heartbeat_request(
700            Role::Datanode,
701            42,
702        ))]);
703        let session = init_session(
704            requests,
705            tx,
706            Some(MockLeaderStepDown::new(LeaderStepDownEvent::Closed)),
707            handler_group.clone(),
708        )
709        .await
710        .unwrap();
711        let _ = recv_response(&mut rx).await.unwrap();
712
713        session.run().await;
714
715        let status = recv_response(&mut rx).await.unwrap_err();
716        assert_eq!(Code::Unavailable, status.code());
717        assert!(!handler_group.contains_pusher(&sender_id).await);
718    }
719
720    #[tokio::test]
721    async fn test_ask_leader() {
722        let kv_backend = Arc::new(MemoryKvBackend::new());
723
724        let metasrv = MetasrvBuilder::new()
725            .kv_backend(kv_backend)
726            .options(MetasrvOptions {
727                grpc: GrpcOptions {
728                    server_addr: "127.0.0.1:3002".to_string(),
729                    ..Default::default()
730                },
731                ..Default::default()
732            })
733            .build()
734            .await
735            .unwrap();
736
737        let req = AskLeaderRequest {
738            header: Some(RequestHeader::new(1, Role::Datanode, W3cTrace::new())),
739        };
740
741        let res = metasrv.ask_leader(req.into_request()).await.unwrap();
742        let res = res.into_inner();
743        assert_eq!(metasrv.options().grpc.server_addr, res.leader.unwrap().addr);
744    }
745
746    #[test]
747    fn test_get_node_id() {
748        let header = RequestHeader {
749            role: Role::Datanode.into(),
750            member_id: 11,
751            ..Default::default()
752        };
753        assert_eq!(11, get_node_id(&header));
754
755        let header = RequestHeader {
756            role: Role::Frontend.into(),
757            ..Default::default()
758        };
759        for i in 0..10 {
760            assert_eq!(i, get_node_id(&header));
761        }
762
763        let header = RequestHeader {
764            role: Role::Frontend.into(),
765            member_id: 11,
766            ..Default::default()
767        };
768        for i in 10..20 {
769            assert_eq!(i, get_node_id(&header));
770        }
771    }
772}