Skip to main content

meta_srv/
handler.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::cmp::Ordering;
16use std::collections::{BTreeMap, HashMap, HashSet};
17use std::fmt::{Debug, Display};
18use std::ops::Bound;
19use std::sync::{Arc, Mutex};
20use std::time::{Duration, Instant};
21
22use api::v1::meta::mailbox_message::Payload;
23use api::v1::meta::{
24    HeartbeatConfig, HeartbeatRequest, HeartbeatResponse, MailboxMessage, PROTOCOL_VERSION,
25    RegionLease, ResponseHeader, Role,
26};
27use check_leader_handler::CheckLeaderHandler;
28use collect_cluster_info_handler::{
29    CollectDatanodeClusterInfoHandler, CollectFlownodeClusterInfoHandler,
30    CollectFrontendClusterInfoHandler,
31};
32use collect_leader_region_handler::CollectLeaderRegionHandler;
33use collect_stats_handler::CollectStatsHandler;
34use common_base::Plugins;
35use common_meta::datanode::Stat;
36use common_meta::instruction::InstructionReply;
37use common_meta::sequence::Sequence;
38use common_telemetry::{debug, info, warn};
39use dashmap::DashMap;
40use extract_stat_handler::ExtractStatHandler;
41use failure_handler::RegionFailureHandler;
42use filter_inactive_region_stats::FilterInactiveRegionStatsHandler;
43use futures::future::join_all;
44use keep_lease_handler::{DatanodeKeepLeaseHandler, FlownodeKeepLeaseHandler};
45use mailbox_handler::MailboxHandler;
46use on_leader_start_handler::OnLeaderStartHandler;
47use publish_heartbeat_handler::PublishHeartbeatHandler;
48use region_lease_handler::RegionLeaseHandler;
49use remap_flow_peer_handler::RemapFlowPeerHandler;
50use response_header_handler::ResponseHeaderHandler;
51use snafu::{OptionExt, ResultExt};
52use store_api::storage::RegionId;
53use tokio::sync::mpsc::Sender;
54use tokio::sync::{Notify, RwLock, oneshot, watch};
55
56use crate::error::{self, DeserializeFromJsonSnafu, Result, UnexpectedInstructionReplySnafu};
57use crate::handler::collect_topic_stats_handler::CollectTopicStatsHandler;
58use crate::handler::flow_state_handler::FlowStateHandler;
59use crate::handler::persist_stats_handler::PersistStatsHandler;
60use crate::metasrv::Context;
61use crate::metrics::{METRIC_META_HANDLER_EXECUTE, METRIC_META_HEARTBEAT_CONNECTION_NUM};
62use crate::pubsub::PublisherRef;
63use crate::service::mailbox::{
64    BroadcastChannel, Channel, Mailbox, MailboxReceiver, MailboxRef, MessageId,
65};
66
67pub mod check_leader_handler;
68pub mod collect_cluster_info_handler;
69pub mod collect_leader_region_handler;
70pub mod collect_stats_handler;
71pub mod collect_topic_stats_handler;
72pub mod extract_stat_handler;
73pub mod failure_handler;
74pub mod filter_inactive_region_stats;
75pub mod flow_state_handler;
76pub mod keep_lease_handler;
77pub mod mailbox_handler;
78pub mod on_leader_start_handler;
79pub mod persist_stats_handler;
80pub mod publish_heartbeat_handler;
81pub mod region_lease_handler;
82pub mod remap_flow_peer_handler;
83pub mod response_header_handler;
84
85#[cfg(test)]
86pub mod test_utils;
87
88#[async_trait::async_trait]
89pub trait HeartbeatHandler: Send + Sync {
90    fn is_acceptable(&self, role: Role) -> bool;
91
92    fn name(&self) -> &'static str {
93        let type_name = std::any::type_name::<Self>();
94        // short name
95        type_name.split("::").last().unwrap_or(type_name)
96    }
97
98    async fn handle(
99        &self,
100        req: &HeartbeatRequest,
101        ctx: &mut Context,
102        acc: &mut HeartbeatAccumulator,
103    ) -> Result<HandleControl>;
104}
105
106/// HandleControl
107///
108/// Controls process of handling heartbeat request.
109#[derive(PartialEq, Debug)]
110pub enum HandleControl {
111    Continue,
112    Done,
113}
114
115#[derive(Debug, Default)]
116pub struct HeartbeatAccumulator {
117    pub header: Option<ResponseHeader>,
118    mailbox_message: Option<MailboxMessage>,
119    pub stat: Option<Stat>,
120    pub inactive_region_ids: HashSet<RegionId>,
121    pub region_lease: Option<RegionLease>,
122    /// Generic heartbeat response extensions accumulated by handlers.
123    pub extensions: HashMap<String, Vec<u8>>,
124}
125
126impl HeartbeatAccumulator {
127    pub(crate) fn take_mailbox_message(&mut self) -> Option<MailboxMessage> {
128        self.mailbox_message.take()
129    }
130
131    pub fn set_mailbox_message(&mut self, message: MailboxMessage) {
132        let _ = self.mailbox_message.insert(message);
133    }
134}
135
136#[derive(Copy, Clone)]
137pub struct PusherId {
138    pub role: Role,
139    pub id: u64,
140}
141
142impl PartialEq for PusherId {
143    fn eq(&self, other: &Self) -> bool {
144        self.role as i32 == other.role as i32 && self.id == other.id
145    }
146}
147
148impl Eq for PusherId {}
149
150impl PartialOrd for PusherId {
151    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
152        Some(self.cmp(other))
153    }
154}
155
156impl Ord for PusherId {
157    fn cmp(&self, other: &Self) -> Ordering {
158        (self.role as i32, self.id).cmp(&(other.role as i32, other.id))
159    }
160}
161
162impl Debug for PusherId {
163    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164        write!(f, "{:?}-{}", self.role, self.id)
165    }
166}
167
168impl Display for PusherId {
169    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170        write!(f, "{:?}-{}", self.role, self.id)
171    }
172}
173
174impl PusherId {
175    pub fn new(role: Role, id: u64) -> Self {
176        Self { role, id }
177    }
178
179    fn role_range(role: Role) -> (Bound<Self>, Bound<Self>) {
180        (
181            Bound::Included(Self::new(role, u64::MIN)),
182            Bound::Included(Self::new(role, u64::MAX)),
183        )
184    }
185}
186
187/// The receiver of the deregister signal.
188pub type DeregisterSignalReceiver = watch::Receiver<bool>;
189
190/// The pusher of the heartbeat response.
191pub struct Pusher {
192    sender: Sender<std::result::Result<HeartbeatResponse, tonic::Status>>,
193    // The sender of the deregister signal.
194    // default is false, means the pusher is not deregistered.
195    // when the pusher is deregistered, the sender will be notified.
196    deregister_signal_sender: watch::Sender<bool>,
197    deregister_signal_receiver: DeregisterSignalReceiver,
198
199    res_header: ResponseHeader,
200}
201
202impl Drop for Pusher {
203    fn drop(&mut self) {
204        // Ignore the error here.
205        // if all the receivers have been dropped, means no body cares the deregister signal.
206        let _ = self.deregister_signal_sender.send(true);
207    }
208}
209
210impl Pusher {
211    pub fn new(sender: Sender<std::result::Result<HeartbeatResponse, tonic::Status>>) -> Self {
212        let res_header = ResponseHeader {
213            protocol_version: PROTOCOL_VERSION,
214            ..Default::default()
215        };
216        let (deregister_signal_sender, deregister_signal_receiver) = watch::channel(false);
217        Self {
218            sender,
219            deregister_signal_sender,
220            deregister_signal_receiver,
221            res_header,
222        }
223    }
224
225    #[inline]
226    pub async fn push(&self, res: HeartbeatResponse) -> Result<()> {
227        self.sender.send(Ok(res)).await.map_err(|e| {
228            error::PushMessageSnafu {
229                err_msg: e.to_string(),
230            }
231            .build()
232        })
233    }
234
235    #[inline]
236    pub fn header(&self) -> ResponseHeader {
237        self.res_header.clone()
238    }
239}
240
241/// The group of heartbeat pushers.
242#[derive(Clone, Default)]
243pub struct Pushers(Arc<RwLock<BTreeMap<PusherId, Pusher>>>);
244
245impl Pushers {
246    async fn push(
247        &self,
248        pusher_id: PusherId,
249        mailbox_message: MailboxMessage,
250    ) -> Result<DeregisterSignalReceiver> {
251        let pushers = self.0.read().await;
252        let pusher = pushers
253            .get(&pusher_id)
254            .with_context(|| error::PusherNotFoundSnafu {
255                pusher_id: pusher_id.to_string(),
256            })?;
257
258        pusher
259            .push(HeartbeatResponse {
260                header: Some(pusher.header()),
261                mailbox_message: Some(mailbox_message),
262                ..Default::default()
263            })
264            .await?;
265
266        Ok(pusher.deregister_signal_receiver.clone())
267    }
268
269    async fn broadcast(&self, role: Role, mailbox_message: &MailboxMessage) -> Result<()> {
270        let pushers = self.0.read().await;
271        let pushers = pushers
272            .range(PusherId::role_range(role))
273            .map(|(_, value)| value)
274            .collect::<Vec<_>>();
275        let mut results = Vec::with_capacity(pushers.len());
276
277        for pusher in pushers {
278            let mut mailbox_message = mailbox_message.clone();
279            mailbox_message.id = 0; // one-way message
280
281            results.push(pusher.push(HeartbeatResponse {
282                header: Some(pusher.header()),
283                mailbox_message: Some(mailbox_message),
284                ..Default::default()
285            }))
286        }
287
288        // Checks the error out of the loop.
289        let _ = join_all(results)
290            .await
291            .into_iter()
292            .collect::<Result<Vec<_>>>()?;
293
294        Ok(())
295    }
296
297    pub(crate) async fn insert(&self, pusher_id: PusherId, pusher: Pusher) -> Option<Pusher> {
298        self.0.write().await.insert(pusher_id, pusher)
299    }
300
301    async fn remove(&self, pusher_id: PusherId) -> Option<Pusher> {
302        self.0.write().await.remove(&pusher_id)
303    }
304}
305
306#[derive(Clone)]
307pub struct NameCachedHandler {
308    name: &'static str,
309    handler: Arc<dyn HeartbeatHandler>,
310}
311
312impl NameCachedHandler {
313    fn new(handler: impl HeartbeatHandler + 'static) -> Self {
314        let name = handler.name();
315        let handler = Arc::new(handler);
316        Self { name, handler }
317    }
318}
319
320pub type HeartbeatHandlerGroupRef = Arc<HeartbeatHandlerGroup>;
321
322/// The group of heartbeat handlers.
323#[derive(Default, Clone)]
324pub struct HeartbeatHandlerGroup {
325    handlers: Vec<NameCachedHandler>,
326    pushers: Pushers,
327}
328
329impl HeartbeatHandlerGroup {
330    /// Registers the heartbeat response [`Pusher`] with the given key to the group.
331    pub async fn register_pusher(&self, pusher_id: PusherId, pusher: Pusher) {
332        METRIC_META_HEARTBEAT_CONNECTION_NUM.inc();
333        info!("Pusher register: {}", pusher_id);
334        let _ = self.pushers.insert(pusher_id, pusher).await;
335    }
336
337    /// Deregisters the heartbeat response [`Pusher`] with the given key from the group.
338    pub async fn deregister_push(&self, pusher_id: PusherId) {
339        if self.pushers.remove(pusher_id).await.is_some() {
340            info!("Pusher unregister: {}", pusher_id);
341            METRIC_META_HEARTBEAT_CONNECTION_NUM.dec();
342        }
343    }
344
345    #[cfg(test)]
346    /// Returns whether the group contains the heartbeat response [`Pusher`] with the given key.
347    pub async fn contains_pusher(&self, pusher_id: &PusherId) -> bool {
348        let pushers = self.pushers.0.read().await;
349        pushers.contains_key(pusher_id)
350    }
351
352    /// Returns the [`Pushers`] of the group.
353    pub fn pushers(&self) -> Pushers {
354        self.pushers.clone()
355    }
356
357    /// Handles the heartbeat request.
358    pub async fn handle(
359        &self,
360        req: HeartbeatRequest,
361        mut ctx: Context,
362    ) -> Result<HeartbeatResponse> {
363        let mut acc = HeartbeatAccumulator::default();
364        let role = req
365            .header
366            .as_ref()
367            .and_then(|h| Role::try_from(h.role).ok())
368            .context(error::InvalidArgumentsSnafu {
369                err_msg: format!("invalid role: {:?}", req.header),
370            })?;
371
372        let is_handshake = ctx.is_handshake;
373
374        for NameCachedHandler { name, handler } in self.handlers.iter() {
375            if !handler.is_acceptable(role) {
376                continue;
377            }
378
379            let _timer = METRIC_META_HANDLER_EXECUTE
380                .with_label_values(&[*name])
381                .start_timer();
382
383            if handler.handle(&req, &mut ctx, &mut acc).await? == HandleControl::Done {
384                break;
385            }
386        }
387        let header = std::mem::take(&mut acc.header);
388        let mailbox_message = acc.take_mailbox_message();
389
390        // Populate heartbeat_config during handshake
391        let heartbeat_config = if is_handshake {
392            let mut config: HeartbeatConfig = ctx.heartbeat_options_for(role).into();
393            config.gc_enabled = ctx.gc_enabled;
394
395            info!(
396                "Handshake with {:?} node, sending config: {:?}",
397                role, config
398            );
399
400            Some(config)
401        } else {
402            None
403        };
404
405        let res = HeartbeatResponse {
406            header,
407            region_lease: acc.region_lease,
408            mailbox_message,
409            heartbeat_config,
410            extensions: std::mem::take(&mut acc.extensions),
411        };
412        Ok(res)
413    }
414}
415
416pub struct HeartbeatMailbox {
417    pushers: Pushers,
418    sequence: Sequence,
419    senders: DashMap<MessageId, oneshot::Sender<Result<MailboxMessage>>>,
420    timeouts: DashMap<MessageId, Instant>,
421    timeout_notify: Notify,
422}
423
424impl HeartbeatMailbox {
425    pub fn json_reply(msg: &MailboxMessage) -> Result<InstructionReply> {
426        let Payload::Json(payload) =
427            msg.payload
428                .as_ref()
429                .with_context(|| UnexpectedInstructionReplySnafu {
430                    mailbox_message: msg.to_string(),
431                    reason: "empty JSON payload".to_string(),
432                })?;
433        serde_json::from_str(payload).context(DeserializeFromJsonSnafu {
434            input_len: payload.len(),
435        })
436    }
437
438    /// Parses the [Instruction] from [MailboxMessage].
439    #[cfg(test)]
440    pub(crate) fn json_instruction(
441        msg: &MailboxMessage,
442    ) -> Result<common_meta::instruction::Instruction> {
443        let Payload::Json(payload) =
444            msg.payload
445                .as_ref()
446                .with_context(|| UnexpectedInstructionReplySnafu {
447                    mailbox_message: msg.to_string(),
448                    reason: "empty JSON payload".to_string(),
449                })?;
450        serde_json::from_str(payload).context(DeserializeFromJsonSnafu {
451            input_len: payload.len(),
452        })
453    }
454
455    pub fn create(pushers: Pushers, sequence: Sequence) -> MailboxRef {
456        let mailbox = Arc::new(Self::new(pushers, sequence));
457
458        let timeout_checker = mailbox.clone();
459        let _handle = common_runtime::spawn_global(async move {
460            timeout_checker.check_timeout_bg(10).await;
461        });
462
463        mailbox
464    }
465
466    fn new(pushers: Pushers, sequence: Sequence) -> Self {
467        Self {
468            pushers,
469            sequence,
470            senders: DashMap::default(),
471            timeouts: DashMap::default(),
472            timeout_notify: Notify::new(),
473        }
474    }
475
476    async fn check_timeout_bg(&self, interval_millis: u64) {
477        let mut interval = tokio::time::interval(Duration::from_millis(interval_millis));
478
479        loop {
480            let _ = interval.tick().await;
481
482            if self.timeouts.is_empty() {
483                self.timeout_notify.notified().await;
484            }
485
486            let now = Instant::now();
487            let timeout_ids = self
488                .timeouts
489                .iter()
490                .filter_map(|entry| {
491                    let (id, deadline) = entry.pair();
492                    if deadline < &now { Some(*id) } else { None }
493                })
494                .collect::<Vec<_>>();
495
496            for id in timeout_ids {
497                let _ = self
498                    .on_recv(id, Err(error::MailboxTimeoutSnafu { id }.build()))
499                    .await;
500            }
501        }
502    }
503
504    #[inline]
505    async fn next_message_id(&self) -> Result<u64> {
506        // In this implementation, we pre-occupy the message_id of 0,
507        // and we use `message_id = 0` to mark a Message as a one-way call.
508        loop {
509            let next = self
510                .sequence
511                .next()
512                .await
513                .context(error::NextSequenceSnafu)?;
514            if next > 0 {
515                return Ok(next);
516            }
517        }
518    }
519}
520
521#[async_trait::async_trait]
522impl Mailbox for HeartbeatMailbox {
523    async fn send(
524        &self,
525        ch: &Channel,
526        mut msg: MailboxMessage,
527        timeout: Duration,
528    ) -> Result<MailboxReceiver> {
529        let message_id = self.next_message_id().await?;
530        msg.id = message_id;
531
532        let pusher_id = ch.pusher_id();
533        let payload_len = msg
534            .payload
535            .as_ref()
536            .map(|Payload::Json(payload)| payload.len())
537            .unwrap_or_default();
538        debug!(message_id, payload_len, %pusher_id, "Sending mailbox message");
539
540        let (tx, rx) = oneshot::channel();
541        let _ = self.senders.insert(message_id, tx);
542        let deadline = Instant::now() + timeout;
543        self.timeouts.insert(message_id, deadline);
544        self.timeout_notify.notify_one();
545        let deregister_signal_receiver = self.pushers.push(pusher_id, msg).await?;
546
547        Ok(MailboxReceiver::new(
548            message_id,
549            rx,
550            deregister_signal_receiver,
551            *ch,
552        ))
553    }
554
555    async fn send_oneway(&self, ch: &Channel, mut msg: MailboxMessage) -> Result<()> {
556        let message_id = 0; // one-way message, same as `broadcast`
557        msg.id = message_id;
558
559        let pusher_id = ch.pusher_id();
560        let payload_len = msg
561            .payload
562            .as_ref()
563            .map(|Payload::Json(payload)| payload.len())
564            .unwrap_or_default();
565        debug!(message_id, payload_len, %pusher_id, "Sending one-way mailbox message");
566
567        self.pushers.push(pusher_id, msg).await?;
568
569        Ok(())
570    }
571
572    async fn broadcast(&self, ch: &BroadcastChannel, msg: &MailboxMessage) -> Result<()> {
573        self.pushers.broadcast(ch.role(), msg).await
574    }
575
576    async fn on_recv(&self, id: MessageId, maybe_msg: Result<MailboxMessage>) -> Result<()> {
577        let payload_len = maybe_msg
578            .as_ref()
579            .ok()
580            .and_then(|msg| msg.payload.as_ref())
581            .map(|Payload::Json(payload)| payload.len())
582            .unwrap_or_default();
583        debug!(
584            message_id = id,
585            payload_len,
586            success = maybe_msg.is_ok(),
587            "Received mailbox message"
588        );
589
590        let _ = self.timeouts.remove(&id);
591
592        if let Some((_, tx)) = self.senders.remove(&id) {
593            tx.send(maybe_msg)
594                .map_err(|_| error::MailboxClosedSnafu { id }.build())?;
595        } else if let Ok(finally_msg) = maybe_msg {
596            let payload_len = finally_msg
597                .payload
598                .as_ref()
599                .map(|Payload::Json(payload)| payload.len())
600                .unwrap_or_default();
601            warn!(
602                message_id = id,
603                payload_len, "The mailbox response arrived too late"
604            );
605        }
606
607        Ok(())
608    }
609}
610
611/// The builder to build the group of heartbeat handlers.
612pub struct HeartbeatHandlerGroupBuilder {
613    /// The handler to handle region failure.
614    region_failure_handler: Option<RegionFailureHandler>,
615
616    /// The handler to handle region lease.
617    region_lease_handler: Option<RegionLeaseHandler>,
618
619    /// The factor that determines how often statistics should be flushed,
620    /// based on the number of received heartbeats. When the number of heartbeats
621    /// reaches this factor, a flush operation is triggered.
622    flush_stats_factor: Option<usize>,
623    /// A simple handler for flow internal state report
624    flow_state_handler: Option<FlowStateHandler>,
625
626    /// The handler to persist stats.
627    persist_stats_handler: Option<PersistStatsHandler>,
628
629    /// The plugins.
630    plugins: Option<Plugins>,
631
632    /// The heartbeat response pushers.
633    pushers: Pushers,
634
635    /// The group of heartbeat handlers.
636    handlers: Vec<NameCachedHandler>,
637}
638
639impl HeartbeatHandlerGroupBuilder {
640    pub fn new(pushers: Pushers) -> Self {
641        Self {
642            region_failure_handler: None,
643            region_lease_handler: None,
644            flush_stats_factor: None,
645            flow_state_handler: None,
646            persist_stats_handler: None,
647            plugins: None,
648            pushers,
649            handlers: vec![],
650        }
651    }
652
653    pub fn with_flow_state_handler(mut self, handler: Option<FlowStateHandler>) -> Self {
654        self.flow_state_handler = handler;
655        self
656    }
657
658    pub fn with_region_lease_handler(mut self, handler: Option<RegionLeaseHandler>) -> Self {
659        self.region_lease_handler = handler;
660        self
661    }
662
663    /// Sets the [`RegionFailureHandler`].
664    pub fn with_region_failure_handler(mut self, handler: Option<RegionFailureHandler>) -> Self {
665        self.region_failure_handler = handler;
666        self
667    }
668
669    /// Sets the flush stats factor.
670    pub fn with_flush_stats_factor(mut self, flush_stats_factor: Option<usize>) -> Self {
671        self.flush_stats_factor = flush_stats_factor;
672        self
673    }
674
675    pub fn with_persist_stats_handler(mut self, handler: Option<PersistStatsHandler>) -> Self {
676        self.persist_stats_handler = handler;
677        self
678    }
679
680    /// Sets the [`Plugins`].
681    pub fn with_plugins(mut self, plugins: Option<Plugins>) -> Self {
682        self.plugins = plugins;
683        self
684    }
685
686    /// Adds the default handlers.
687    pub fn add_default_handlers(mut self) -> Self {
688        // Extract the `PublishHeartbeatHandler` from the plugins.
689        let publish_heartbeat_handler = if let Some(plugins) = self.plugins.as_ref() {
690            plugins
691                .get::<PublisherRef>()
692                .map(|publish| PublishHeartbeatHandler::new(publish.clone()))
693        } else {
694            None
695        };
696
697        self.add_handler_last(ResponseHeaderHandler);
698        // `KeepLeaseHandler` should preferably be in front of `CheckLeaderHandler`,
699        // because even if the current meta-server node is no longer the leader it can
700        // still help the datanode to keep lease.
701        self.add_handler_last(DatanodeKeepLeaseHandler);
702        self.add_handler_last(FlownodeKeepLeaseHandler);
703        self.add_handler_last(CheckLeaderHandler);
704        self.add_handler_last(OnLeaderStartHandler);
705        self.add_handler_last(ExtractStatHandler);
706        self.add_handler_last(CollectDatanodeClusterInfoHandler);
707        self.add_handler_last(CollectFrontendClusterInfoHandler);
708        self.add_handler_last(CollectFlownodeClusterInfoHandler);
709        self.add_handler_last(MailboxHandler);
710        if let Some(region_lease_handler) = self.region_lease_handler.take() {
711            self.add_handler_last(region_lease_handler);
712        }
713        self.add_handler_last(FilterInactiveRegionStatsHandler);
714        if let Some(region_failure_handler) = self.region_failure_handler.take() {
715            self.add_handler_last(region_failure_handler);
716        }
717        if let Some(publish_heartbeat_handler) = publish_heartbeat_handler {
718            self.add_handler_last(publish_heartbeat_handler);
719        }
720        self.add_handler_last(CollectLeaderRegionHandler);
721        self.add_handler_last(CollectTopicStatsHandler);
722        // Persist stats handler should be in front of collect stats handler.
723        // Because collect stats handler will consume the stats from the accumulator.
724        if let Some(persist_stats_handler) = self.persist_stats_handler.take() {
725            self.add_handler_last(persist_stats_handler);
726        }
727        self.add_handler_last(CollectStatsHandler::new(self.flush_stats_factor));
728        self.add_handler_last(RemapFlowPeerHandler::default());
729
730        if let Some(flow_state_handler) = self.flow_state_handler.take() {
731            self.add_handler_last(flow_state_handler);
732        }
733
734        self
735    }
736
737    /// Builds the group of heartbeat handlers.
738    ///
739    /// Applies the customizer if it exists.
740    pub fn build(mut self) -> Result<HeartbeatHandlerGroup> {
741        if let Some(customizer) = self
742            .plugins
743            .as_ref()
744            .and_then(|plugins| plugins.get::<HeartbeatHandlerGroupBuilderCustomizerRef>())
745        {
746            debug!("Customizing the heartbeat handler group builder");
747            customizer.customize(&mut self)?;
748        }
749
750        Ok(HeartbeatHandlerGroup {
751            handlers: self.handlers,
752            pushers: self.pushers,
753        })
754    }
755
756    fn add_handler_after_inner(&mut self, target: &str, handler: NameCachedHandler) -> Result<()> {
757        if let Some(pos) = self.handlers.iter().position(|x| x.name == target) {
758            self.handlers.insert(pos + 1, handler);
759            return Ok(());
760        }
761
762        error::HandlerNotFoundSnafu { name: target }.fail()
763    }
764
765    /// Adds the handler after the specified handler.
766    pub fn add_handler_after(
767        &mut self,
768        target: &'static str,
769        handler: impl HeartbeatHandler + 'static,
770    ) -> Result<()> {
771        self.add_handler_after_inner(target, NameCachedHandler::new(handler))
772    }
773
774    fn add_handler_before_inner(&mut self, target: &str, handler: NameCachedHandler) -> Result<()> {
775        if let Some(pos) = self.handlers.iter().position(|x| x.name == target) {
776            self.handlers.insert(pos, handler);
777            return Ok(());
778        }
779
780        error::HandlerNotFoundSnafu { name: target }.fail()
781    }
782
783    /// Adds the handler before the specified handler.
784    pub fn add_handler_before(
785        &mut self,
786        target: &'static str,
787        handler: impl HeartbeatHandler + 'static,
788    ) -> Result<()> {
789        self.add_handler_before_inner(target, NameCachedHandler::new(handler))
790    }
791
792    fn replace_handler_inner(&mut self, target: &str, handler: NameCachedHandler) -> Result<()> {
793        if let Some(pos) = self.handlers.iter().position(|x| x.name == target) {
794            self.handlers[pos] = handler;
795            return Ok(());
796        }
797
798        error::HandlerNotFoundSnafu { name: target }.fail()
799    }
800
801    /// Replaces the handler with the specified name.
802    pub fn replace_handler(
803        &mut self,
804        target: &'static str,
805        handler: impl HeartbeatHandler + 'static,
806    ) -> Result<()> {
807        self.replace_handler_inner(target, NameCachedHandler::new(handler))
808    }
809
810    fn add_handler_last_inner(&mut self, handler: NameCachedHandler) {
811        self.handlers.push(handler);
812    }
813
814    fn add_handler_last(&mut self, handler: impl HeartbeatHandler + 'static) {
815        self.add_handler_last_inner(NameCachedHandler::new(handler));
816    }
817}
818
819pub type HeartbeatHandlerGroupBuilderCustomizerRef =
820    Arc<dyn HeartbeatHandlerGroupBuilderCustomizer>;
821
822pub enum CustomizeHeartbeatGroupAction {
823    AddHandlerAfter {
824        target: String,
825        handler: NameCachedHandler,
826    },
827    AddHandlerBefore {
828        target: String,
829        handler: NameCachedHandler,
830    },
831    ReplaceHandler {
832        target: String,
833        handler: NameCachedHandler,
834    },
835    AddHandlerLast {
836        handler: NameCachedHandler,
837    },
838}
839
840impl CustomizeHeartbeatGroupAction {
841    pub fn new_add_handler_after(
842        target: &'static str,
843        handler: impl HeartbeatHandler + 'static,
844    ) -> Self {
845        Self::AddHandlerAfter {
846            target: target.to_string(),
847            handler: NameCachedHandler::new(handler),
848        }
849    }
850
851    pub fn new_add_handler_before(
852        target: &'static str,
853        handler: impl HeartbeatHandler + 'static,
854    ) -> Self {
855        Self::AddHandlerBefore {
856            target: target.to_string(),
857            handler: NameCachedHandler::new(handler),
858        }
859    }
860
861    pub fn new_replace_handler(
862        target: &'static str,
863        handler: impl HeartbeatHandler + 'static,
864    ) -> Self {
865        Self::ReplaceHandler {
866            target: target.to_string(),
867            handler: NameCachedHandler::new(handler),
868        }
869    }
870
871    pub fn new_add_handler_last(handler: impl HeartbeatHandler + 'static) -> Self {
872        Self::AddHandlerLast {
873            handler: NameCachedHandler::new(handler),
874        }
875    }
876}
877
878/// The customizer of the [`HeartbeatHandlerGroupBuilder`].
879pub trait HeartbeatHandlerGroupBuilderCustomizer: Send + Sync {
880    fn customize(&self, builder: &mut HeartbeatHandlerGroupBuilder) -> Result<()>;
881
882    fn add_action(&self, action: CustomizeHeartbeatGroupAction);
883}
884
885#[derive(Default)]
886pub struct DefaultHeartbeatHandlerGroupBuilderCustomizer {
887    actions: Mutex<Vec<CustomizeHeartbeatGroupAction>>,
888}
889
890impl HeartbeatHandlerGroupBuilderCustomizer for DefaultHeartbeatHandlerGroupBuilderCustomizer {
891    fn customize(&self, builder: &mut HeartbeatHandlerGroupBuilder) -> Result<()> {
892        info!("Customizing the heartbeat handler group builder");
893        let mut actions = self.actions.lock().unwrap();
894        for action in actions.drain(..) {
895            match action {
896                CustomizeHeartbeatGroupAction::AddHandlerAfter { target, handler } => {
897                    builder.add_handler_after_inner(&target, handler)?;
898                }
899                CustomizeHeartbeatGroupAction::AddHandlerBefore { target, handler } => {
900                    builder.add_handler_before_inner(&target, handler)?;
901                }
902                CustomizeHeartbeatGroupAction::ReplaceHandler { target, handler } => {
903                    builder.replace_handler_inner(&target, handler)?;
904                }
905                CustomizeHeartbeatGroupAction::AddHandlerLast { handler } => {
906                    builder.add_handler_last_inner(handler);
907                }
908            }
909        }
910        Ok(())
911    }
912
913    fn add_action(&self, action: CustomizeHeartbeatGroupAction) {
914        self.actions.lock().unwrap().push(action);
915    }
916}
917
918#[cfg(test)]
919mod tests {
920
921    use std::assert_matches;
922    use std::collections::BTreeMap;
923    use std::sync::Arc;
924    use std::time::Duration;
925
926    use api::v1::meta::{HeartbeatRequest, MailboxMessage, RequestHeader, Role};
927    use common_meta::kv_backend::memory::MemoryKvBackend;
928    use common_meta::sequence::SequenceBuilder;
929    use tokio::sync::mpsc;
930
931    use super::{
932        HandleControl, HeartbeatAccumulator, HeartbeatHandler, HeartbeatHandlerGroupBuilder,
933        PusherId, Pushers,
934    };
935    use crate::error;
936    use crate::handler::collect_stats_handler::CollectStatsHandler;
937    use crate::handler::response_header_handler::ResponseHeaderHandler;
938    use crate::handler::test_utils::TestEnv;
939    use crate::handler::{HeartbeatHandlerGroup, HeartbeatMailbox, Pusher};
940    use crate::metasrv::Context;
941    use crate::service::mailbox::{Channel, MailboxReceiver, MailboxRef};
942
943    #[tokio::test]
944    async fn test_mailbox() {
945        let (mailbox, receiver) = push_msg_via_mailbox().await;
946        let id = receiver.message_id();
947
948        let resp_msg = MailboxMessage {
949            id,
950            subject: "resp-test".to_string(),
951            timestamp_millis: 456,
952            ..Default::default()
953        };
954
955        mailbox.on_recv(id, Ok(resp_msg)).await.unwrap();
956
957        let recv_msg = receiver.await.unwrap();
958        assert_eq!(recv_msg.id, id);
959        assert_eq!(recv_msg.timestamp_millis, 456);
960        assert_eq!(recv_msg.subject, "resp-test".to_string());
961    }
962
963    #[tokio::test]
964    async fn test_mailbox_timeout() {
965        let (_, receiver) = push_msg_via_mailbox().await;
966        let res = receiver.await;
967        assert!(res.is_err());
968    }
969
970    async fn push_msg_via_mailbox() -> (MailboxRef, MailboxReceiver) {
971        let datanode_id = 12;
972        let (pusher_tx, mut pusher_rx) = mpsc::channel(16);
973        let pusher_id = PusherId::new(Role::Datanode, datanode_id);
974        let pusher: Pusher = Pusher::new(pusher_tx);
975        let handler_group = HeartbeatHandlerGroup::default();
976        handler_group.register_pusher(pusher_id, pusher).await;
977
978        let kv_backend = Arc::new(MemoryKvBackend::new());
979        let seq = SequenceBuilder::new("test_seq", kv_backend).build();
980        let mailbox = HeartbeatMailbox::create(handler_group.pushers(), seq);
981
982        let msg = MailboxMessage {
983            id: 0,
984            subject: "req-test".to_string(),
985            timestamp_millis: 123,
986            ..Default::default()
987        };
988        let ch = Channel::Datanode(datanode_id);
989
990        let receiver = mailbox
991            .send(&ch, msg, Duration::from_secs(1))
992            .await
993            .unwrap();
994
995        let recv_obj = pusher_rx.recv().await.unwrap().unwrap();
996        let message = recv_obj.mailbox_message.unwrap();
997        assert_eq!(message.timestamp_millis, 123);
998        assert_eq!(message.subject, "req-test".to_string());
999
1000        (mailbox, receiver)
1001    }
1002
1003    #[test]
1004    fn test_pusher_id_role_range() {
1005        let mut pushers = BTreeMap::new();
1006        pushers.insert(PusherId::new(Role::Datanode, u64::MAX), "datanode");
1007        pushers.insert(PusherId::new(Role::Frontend, u64::MIN), "frontend-min");
1008        pushers.insert(PusherId::new(Role::Frontend, u64::MAX), "frontend-max");
1009        pushers.insert(PusherId::new(Role::Flownode, u64::MIN), "flownode");
1010
1011        let frontend_pushers = pushers
1012            .range(PusherId::role_range(Role::Frontend))
1013            .map(|(_, value)| *value)
1014            .collect::<Vec<_>>();
1015
1016        assert_eq!(frontend_pushers, vec!["frontend-min", "frontend-max"]);
1017    }
1018
1019    #[tokio::test]
1020    async fn test_pushers_broadcast_by_role() {
1021        let pushers = Pushers::default();
1022        let (datanode_tx, mut datanode_rx) = mpsc::channel(1);
1023        let (frontend_tx, mut frontend_rx) = mpsc::channel(1);
1024        let (flownode_tx, mut flownode_rx) = mpsc::channel(1);
1025
1026        pushers
1027            .insert(
1028                PusherId::new(Role::Datanode, u64::MAX),
1029                Pusher::new(datanode_tx),
1030            )
1031            .await;
1032        pushers
1033            .insert(PusherId::new(Role::Frontend, 1), Pusher::new(frontend_tx))
1034            .await;
1035        pushers
1036            .insert(
1037                PusherId::new(Role::Flownode, u64::MIN),
1038                Pusher::new(flownode_tx),
1039            )
1040            .await;
1041
1042        let msg = MailboxMessage {
1043            id: 42,
1044            subject: "broadcast-test".to_string(),
1045            timestamp_millis: 123,
1046            ..Default::default()
1047        };
1048
1049        pushers.broadcast(Role::Frontend, &msg).await.unwrap();
1050
1051        let received = frontend_rx.recv().await.unwrap().unwrap();
1052        let mailbox_message = received.mailbox_message.unwrap();
1053        assert_eq!(mailbox_message.id, 0);
1054        assert_eq!(mailbox_message.subject, "broadcast-test");
1055        assert!(datanode_rx.try_recv().is_err());
1056        assert!(flownode_rx.try_recv().is_err());
1057    }
1058
1059    #[test]
1060    fn test_handler_group_builder() {
1061        let group = HeartbeatHandlerGroupBuilder::new(Pushers::default())
1062            .add_default_handlers()
1063            .build()
1064            .unwrap();
1065
1066        let handlers = group.handlers;
1067        let names = [
1068            "ResponseHeaderHandler",
1069            "DatanodeKeepLeaseHandler",
1070            "FlownodeKeepLeaseHandler",
1071            "CheckLeaderHandler",
1072            "OnLeaderStartHandler",
1073            "ExtractStatHandler",
1074            "CollectDatanodeClusterInfoHandler",
1075            "CollectFrontendClusterInfoHandler",
1076            "CollectFlownodeClusterInfoHandler",
1077            "MailboxHandler",
1078            "FilterInactiveRegionStatsHandler",
1079            "CollectLeaderRegionHandler",
1080            "CollectTopicStatsHandler",
1081            "CollectStatsHandler",
1082            "RemapFlowPeerHandler",
1083        ];
1084        assert_eq!(names.len(), handlers.len());
1085        for (handler, name) in handlers.iter().zip(names) {
1086            assert_eq!(handler.name, name);
1087        }
1088    }
1089
1090    #[test]
1091    fn test_handler_group_builder_add_before() {
1092        let mut builder =
1093            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1094        builder
1095            .add_handler_before(
1096                "FilterInactiveRegionStatsHandler",
1097                CollectStatsHandler::default(),
1098            )
1099            .unwrap();
1100
1101        let group = builder.build().unwrap();
1102        let handlers = group.handlers;
1103        let names = [
1104            "ResponseHeaderHandler",
1105            "DatanodeKeepLeaseHandler",
1106            "FlownodeKeepLeaseHandler",
1107            "CheckLeaderHandler",
1108            "OnLeaderStartHandler",
1109            "ExtractStatHandler",
1110            "CollectDatanodeClusterInfoHandler",
1111            "CollectFrontendClusterInfoHandler",
1112            "CollectFlownodeClusterInfoHandler",
1113            "MailboxHandler",
1114            "CollectStatsHandler",
1115            "FilterInactiveRegionStatsHandler",
1116            "CollectLeaderRegionHandler",
1117            "CollectTopicStatsHandler",
1118            "CollectStatsHandler",
1119            "RemapFlowPeerHandler",
1120        ];
1121        assert_eq!(names.len(), handlers.len());
1122        for (handler, name) in handlers.iter().zip(names) {
1123            assert_eq!(handler.name, name);
1124        }
1125    }
1126
1127    #[test]
1128    fn test_handler_group_builder_add_before_first() {
1129        let mut builder =
1130            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1131        builder
1132            .add_handler_before("ResponseHeaderHandler", CollectStatsHandler::default())
1133            .unwrap();
1134
1135        let group = builder.build().unwrap();
1136        let handlers = group.handlers;
1137        let names = [
1138            "CollectStatsHandler",
1139            "ResponseHeaderHandler",
1140            "DatanodeKeepLeaseHandler",
1141            "FlownodeKeepLeaseHandler",
1142            "CheckLeaderHandler",
1143            "OnLeaderStartHandler",
1144            "ExtractStatHandler",
1145            "CollectDatanodeClusterInfoHandler",
1146            "CollectFrontendClusterInfoHandler",
1147            "CollectFlownodeClusterInfoHandler",
1148            "MailboxHandler",
1149            "FilterInactiveRegionStatsHandler",
1150            "CollectLeaderRegionHandler",
1151            "CollectTopicStatsHandler",
1152            "CollectStatsHandler",
1153            "RemapFlowPeerHandler",
1154        ];
1155        assert_eq!(names.len(), handlers.len());
1156        for (handler, name) in handlers.iter().zip(names) {
1157            assert_eq!(handler.name, name);
1158        }
1159    }
1160
1161    #[test]
1162    fn test_handler_group_builder_add_after() {
1163        let mut builder =
1164            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1165        builder
1166            .add_handler_after("MailboxHandler", CollectStatsHandler::default())
1167            .unwrap();
1168
1169        let group = builder.build().unwrap();
1170        let handlers = group.handlers;
1171        let names = [
1172            "ResponseHeaderHandler",
1173            "DatanodeKeepLeaseHandler",
1174            "FlownodeKeepLeaseHandler",
1175            "CheckLeaderHandler",
1176            "OnLeaderStartHandler",
1177            "ExtractStatHandler",
1178            "CollectDatanodeClusterInfoHandler",
1179            "CollectFrontendClusterInfoHandler",
1180            "CollectFlownodeClusterInfoHandler",
1181            "MailboxHandler",
1182            "CollectStatsHandler",
1183            "FilterInactiveRegionStatsHandler",
1184            "CollectLeaderRegionHandler",
1185            "CollectTopicStatsHandler",
1186            "CollectStatsHandler",
1187            "RemapFlowPeerHandler",
1188        ];
1189        assert_eq!(names.len(), handlers.len());
1190        for (handler, name) in handlers.iter().zip(names) {
1191            assert_eq!(handler.name, name);
1192        }
1193    }
1194
1195    #[test]
1196    fn test_handler_group_builder_add_after_last() {
1197        let mut builder =
1198            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1199        builder
1200            .add_handler_after("CollectStatsHandler", ResponseHeaderHandler)
1201            .unwrap();
1202
1203        let group = builder.build().unwrap();
1204        let handlers = group.handlers;
1205        let names = [
1206            "ResponseHeaderHandler",
1207            "DatanodeKeepLeaseHandler",
1208            "FlownodeKeepLeaseHandler",
1209            "CheckLeaderHandler",
1210            "OnLeaderStartHandler",
1211            "ExtractStatHandler",
1212            "CollectDatanodeClusterInfoHandler",
1213            "CollectFrontendClusterInfoHandler",
1214            "CollectFlownodeClusterInfoHandler",
1215            "MailboxHandler",
1216            "FilterInactiveRegionStatsHandler",
1217            "CollectLeaderRegionHandler",
1218            "CollectTopicStatsHandler",
1219            "CollectStatsHandler",
1220            "ResponseHeaderHandler",
1221            "RemapFlowPeerHandler",
1222        ];
1223        assert_eq!(names.len(), handlers.len());
1224        for (handler, name) in handlers.iter().zip(names) {
1225            assert_eq!(handler.name, name);
1226        }
1227    }
1228
1229    #[test]
1230    fn test_handler_group_builder_replace() {
1231        let mut builder =
1232            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1233        builder
1234            .replace_handler("MailboxHandler", CollectStatsHandler::default())
1235            .unwrap();
1236
1237        let group = builder.build().unwrap();
1238        let handlers = group.handlers;
1239        let names = [
1240            "ResponseHeaderHandler",
1241            "DatanodeKeepLeaseHandler",
1242            "FlownodeKeepLeaseHandler",
1243            "CheckLeaderHandler",
1244            "OnLeaderStartHandler",
1245            "ExtractStatHandler",
1246            "CollectDatanodeClusterInfoHandler",
1247            "CollectFrontendClusterInfoHandler",
1248            "CollectFlownodeClusterInfoHandler",
1249            "CollectStatsHandler",
1250            "FilterInactiveRegionStatsHandler",
1251            "CollectLeaderRegionHandler",
1252            "CollectTopicStatsHandler",
1253            "CollectStatsHandler",
1254            "RemapFlowPeerHandler",
1255        ];
1256
1257        assert_eq!(names.len(), handlers.len());
1258        for (handler, name) in handlers.iter().zip(names) {
1259            assert_eq!(handler.name, name);
1260        }
1261    }
1262
1263    #[test]
1264    fn test_handler_group_builder_replace_last() {
1265        let mut builder =
1266            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1267        builder
1268            .replace_handler("CollectStatsHandler", ResponseHeaderHandler)
1269            .unwrap();
1270
1271        let group = builder.build().unwrap();
1272        let handlers = group.handlers;
1273        let names = [
1274            "ResponseHeaderHandler",
1275            "DatanodeKeepLeaseHandler",
1276            "FlownodeKeepLeaseHandler",
1277            "CheckLeaderHandler",
1278            "OnLeaderStartHandler",
1279            "ExtractStatHandler",
1280            "CollectDatanodeClusterInfoHandler",
1281            "CollectFrontendClusterInfoHandler",
1282            "CollectFlownodeClusterInfoHandler",
1283            "MailboxHandler",
1284            "FilterInactiveRegionStatsHandler",
1285            "CollectLeaderRegionHandler",
1286            "CollectTopicStatsHandler",
1287            "ResponseHeaderHandler",
1288            "RemapFlowPeerHandler",
1289        ];
1290
1291        assert_eq!(names.len(), handlers.len());
1292        for (handler, name) in handlers.iter().zip(names) {
1293            assert_eq!(handler.name, name);
1294        }
1295    }
1296
1297    #[test]
1298    fn test_handler_group_builder_replace_first() {
1299        let mut builder =
1300            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1301        builder
1302            .replace_handler("ResponseHeaderHandler", CollectStatsHandler::default())
1303            .unwrap();
1304
1305        let group = builder.build().unwrap();
1306        let handlers = group.handlers;
1307        let names = [
1308            "CollectStatsHandler",
1309            "DatanodeKeepLeaseHandler",
1310            "FlownodeKeepLeaseHandler",
1311            "CheckLeaderHandler",
1312            "OnLeaderStartHandler",
1313            "ExtractStatHandler",
1314            "CollectDatanodeClusterInfoHandler",
1315            "CollectFrontendClusterInfoHandler",
1316            "CollectFlownodeClusterInfoHandler",
1317            "MailboxHandler",
1318            "FilterInactiveRegionStatsHandler",
1319            "CollectLeaderRegionHandler",
1320            "CollectTopicStatsHandler",
1321            "CollectStatsHandler",
1322            "RemapFlowPeerHandler",
1323        ];
1324        assert_eq!(names.len(), handlers.len());
1325        for (handler, name) in handlers.iter().zip(names) {
1326            assert_eq!(handler.name, name);
1327        }
1328    }
1329
1330    #[test]
1331    fn test_handler_group_builder_handler_not_found() {
1332        let mut builder =
1333            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1334        let err = builder
1335            .add_handler_before("NotExists", CollectStatsHandler::default())
1336            .unwrap_err();
1337        assert_matches!(err, error::Error::HandlerNotFound { .. });
1338
1339        let err = builder
1340            .add_handler_after("NotExists", CollectStatsHandler::default())
1341            .unwrap_err();
1342        assert_matches!(err, error::Error::HandlerNotFound { .. });
1343
1344        let err = builder
1345            .replace_handler("NotExists", CollectStatsHandler::default())
1346            .unwrap_err();
1347        assert_matches!(err, error::Error::HandlerNotFound { .. });
1348    }
1349
1350    #[tokio::test]
1351    async fn test_pusher_drop() {
1352        let (tx, _rx) = mpsc::channel(1);
1353        let pusher = Pusher::new(tx);
1354        let mut deregister_signal_tx = pusher.deregister_signal_receiver.clone();
1355
1356        drop(pusher);
1357        deregister_signal_tx.changed().await.unwrap();
1358    }
1359
1360    #[tokio::test]
1361    async fn test_custom_handler_extension_reaches_response() {
1362        struct CustomExtensionHandler;
1363
1364        #[async_trait::async_trait]
1365        impl HeartbeatHandler for CustomExtensionHandler {
1366            fn is_acceptable(&self, role: Role) -> bool {
1367                role == Role::Frontend
1368            }
1369
1370            async fn handle(
1371                &self,
1372                _req: &HeartbeatRequest,
1373                _ctx: &mut Context,
1374                acc: &mut HeartbeatAccumulator,
1375            ) -> crate::error::Result<HandleControl> {
1376                acc.extensions
1377                    .insert("custom.key".to_string(), b"custom-value".to_vec());
1378                Ok(HandleControl::Continue)
1379            }
1380        }
1381
1382        let mut builder =
1383            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1384        builder.add_handler_last(CustomExtensionHandler);
1385
1386        let group = builder.build().unwrap();
1387
1388        let req = HeartbeatRequest {
1389            header: Some(RequestHeader::new(1, Role::Frontend, Default::default())),
1390            ..Default::default()
1391        };
1392        let ctx = TestEnv::new().ctx();
1393
1394        let res = group.handle(req, ctx).await.unwrap();
1395
1396        let extensions = res.extensions;
1397        assert_eq!(extensions.get("custom.key").unwrap(), b"custom-value");
1398    }
1399}