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: format!("empty payload, msg: {msg:?}"),
432                })?;
433        serde_json::from_str(payload).context(DeserializeFromJsonSnafu { input: payload })
434    }
435
436    /// Parses the [Instruction] from [MailboxMessage].
437    #[cfg(test)]
438    pub(crate) fn json_instruction(
439        msg: &MailboxMessage,
440    ) -> Result<common_meta::instruction::Instruction> {
441        let Payload::Json(payload) =
442            msg.payload
443                .as_ref()
444                .with_context(|| UnexpectedInstructionReplySnafu {
445                    mailbox_message: msg.to_string(),
446                    reason: format!("empty payload, msg: {msg:?}"),
447                })?;
448        serde_json::from_str(payload).context(DeserializeFromJsonSnafu { input: payload })
449    }
450
451    pub fn create(pushers: Pushers, sequence: Sequence) -> MailboxRef {
452        let mailbox = Arc::new(Self::new(pushers, sequence));
453
454        let timeout_checker = mailbox.clone();
455        let _handle = common_runtime::spawn_global(async move {
456            timeout_checker.check_timeout_bg(10).await;
457        });
458
459        mailbox
460    }
461
462    fn new(pushers: Pushers, sequence: Sequence) -> Self {
463        Self {
464            pushers,
465            sequence,
466            senders: DashMap::default(),
467            timeouts: DashMap::default(),
468            timeout_notify: Notify::new(),
469        }
470    }
471
472    async fn check_timeout_bg(&self, interval_millis: u64) {
473        let mut interval = tokio::time::interval(Duration::from_millis(interval_millis));
474
475        loop {
476            let _ = interval.tick().await;
477
478            if self.timeouts.is_empty() {
479                self.timeout_notify.notified().await;
480            }
481
482            let now = Instant::now();
483            let timeout_ids = self
484                .timeouts
485                .iter()
486                .filter_map(|entry| {
487                    let (id, deadline) = entry.pair();
488                    if deadline < &now { Some(*id) } else { None }
489                })
490                .collect::<Vec<_>>();
491
492            for id in timeout_ids {
493                let _ = self
494                    .on_recv(id, Err(error::MailboxTimeoutSnafu { id }.build()))
495                    .await;
496            }
497        }
498    }
499
500    #[inline]
501    async fn next_message_id(&self) -> Result<u64> {
502        // In this implementation, we pre-occupy the message_id of 0,
503        // and we use `message_id = 0` to mark a Message as a one-way call.
504        loop {
505            let next = self
506                .sequence
507                .next()
508                .await
509                .context(error::NextSequenceSnafu)?;
510            if next > 0 {
511                return Ok(next);
512            }
513        }
514    }
515}
516
517#[async_trait::async_trait]
518impl Mailbox for HeartbeatMailbox {
519    async fn send(
520        &self,
521        ch: &Channel,
522        mut msg: MailboxMessage,
523        timeout: Duration,
524    ) -> Result<MailboxReceiver> {
525        let message_id = self.next_message_id().await?;
526        msg.id = message_id;
527
528        let pusher_id = ch.pusher_id();
529        debug!("Sending mailbox message {msg:?} to {pusher_id}");
530
531        let (tx, rx) = oneshot::channel();
532        let _ = self.senders.insert(message_id, tx);
533        let deadline = Instant::now() + timeout;
534        self.timeouts.insert(message_id, deadline);
535        self.timeout_notify.notify_one();
536        let deregister_signal_receiver = self.pushers.push(pusher_id, msg).await?;
537
538        Ok(MailboxReceiver::new(
539            message_id,
540            rx,
541            deregister_signal_receiver,
542            *ch,
543        ))
544    }
545
546    async fn send_oneway(&self, ch: &Channel, mut msg: MailboxMessage) -> Result<()> {
547        let message_id = 0; // one-way message, same as `broadcast`
548        msg.id = message_id;
549
550        let pusher_id = ch.pusher_id();
551        debug!("Sending mailbox message {msg:?} to {pusher_id}");
552
553        self.pushers.push(pusher_id, msg).await?;
554
555        Ok(())
556    }
557
558    async fn broadcast(&self, ch: &BroadcastChannel, msg: &MailboxMessage) -> Result<()> {
559        self.pushers.broadcast(ch.role(), msg).await
560    }
561
562    async fn on_recv(&self, id: MessageId, maybe_msg: Result<MailboxMessage>) -> Result<()> {
563        debug!("Received mailbox message {maybe_msg:?}");
564
565        let _ = self.timeouts.remove(&id);
566
567        if let Some((_, tx)) = self.senders.remove(&id) {
568            tx.send(maybe_msg)
569                .map_err(|_| error::MailboxClosedSnafu { id }.build())?;
570        } else if let Ok(finally_msg) = maybe_msg {
571            warn!("The response arrived too late: {finally_msg:?}");
572        }
573
574        Ok(())
575    }
576}
577
578/// The builder to build the group of heartbeat handlers.
579pub struct HeartbeatHandlerGroupBuilder {
580    /// The handler to handle region failure.
581    region_failure_handler: Option<RegionFailureHandler>,
582
583    /// The handler to handle region lease.
584    region_lease_handler: Option<RegionLeaseHandler>,
585
586    /// The factor that determines how often statistics should be flushed,
587    /// based on the number of received heartbeats. When the number of heartbeats
588    /// reaches this factor, a flush operation is triggered.
589    flush_stats_factor: Option<usize>,
590    /// A simple handler for flow internal state report
591    flow_state_handler: Option<FlowStateHandler>,
592
593    /// The handler to persist stats.
594    persist_stats_handler: Option<PersistStatsHandler>,
595
596    /// The plugins.
597    plugins: Option<Plugins>,
598
599    /// The heartbeat response pushers.
600    pushers: Pushers,
601
602    /// The group of heartbeat handlers.
603    handlers: Vec<NameCachedHandler>,
604}
605
606impl HeartbeatHandlerGroupBuilder {
607    pub fn new(pushers: Pushers) -> Self {
608        Self {
609            region_failure_handler: None,
610            region_lease_handler: None,
611            flush_stats_factor: None,
612            flow_state_handler: None,
613            persist_stats_handler: None,
614            plugins: None,
615            pushers,
616            handlers: vec![],
617        }
618    }
619
620    pub fn with_flow_state_handler(mut self, handler: Option<FlowStateHandler>) -> Self {
621        self.flow_state_handler = handler;
622        self
623    }
624
625    pub fn with_region_lease_handler(mut self, handler: Option<RegionLeaseHandler>) -> Self {
626        self.region_lease_handler = handler;
627        self
628    }
629
630    /// Sets the [`RegionFailureHandler`].
631    pub fn with_region_failure_handler(mut self, handler: Option<RegionFailureHandler>) -> Self {
632        self.region_failure_handler = handler;
633        self
634    }
635
636    /// Sets the flush stats factor.
637    pub fn with_flush_stats_factor(mut self, flush_stats_factor: Option<usize>) -> Self {
638        self.flush_stats_factor = flush_stats_factor;
639        self
640    }
641
642    pub fn with_persist_stats_handler(mut self, handler: Option<PersistStatsHandler>) -> Self {
643        self.persist_stats_handler = handler;
644        self
645    }
646
647    /// Sets the [`Plugins`].
648    pub fn with_plugins(mut self, plugins: Option<Plugins>) -> Self {
649        self.plugins = plugins;
650        self
651    }
652
653    /// Adds the default handlers.
654    pub fn add_default_handlers(mut self) -> Self {
655        // Extract the `PublishHeartbeatHandler` from the plugins.
656        let publish_heartbeat_handler = if let Some(plugins) = self.plugins.as_ref() {
657            plugins
658                .get::<PublisherRef>()
659                .map(|publish| PublishHeartbeatHandler::new(publish.clone()))
660        } else {
661            None
662        };
663
664        self.add_handler_last(ResponseHeaderHandler);
665        // `KeepLeaseHandler` should preferably be in front of `CheckLeaderHandler`,
666        // because even if the current meta-server node is no longer the leader it can
667        // still help the datanode to keep lease.
668        self.add_handler_last(DatanodeKeepLeaseHandler);
669        self.add_handler_last(FlownodeKeepLeaseHandler);
670        self.add_handler_last(CheckLeaderHandler);
671        self.add_handler_last(OnLeaderStartHandler);
672        self.add_handler_last(ExtractStatHandler);
673        self.add_handler_last(CollectDatanodeClusterInfoHandler);
674        self.add_handler_last(CollectFrontendClusterInfoHandler);
675        self.add_handler_last(CollectFlownodeClusterInfoHandler);
676        self.add_handler_last(MailboxHandler);
677        if let Some(region_lease_handler) = self.region_lease_handler.take() {
678            self.add_handler_last(region_lease_handler);
679        }
680        self.add_handler_last(FilterInactiveRegionStatsHandler);
681        if let Some(region_failure_handler) = self.region_failure_handler.take() {
682            self.add_handler_last(region_failure_handler);
683        }
684        if let Some(publish_heartbeat_handler) = publish_heartbeat_handler {
685            self.add_handler_last(publish_heartbeat_handler);
686        }
687        self.add_handler_last(CollectLeaderRegionHandler);
688        self.add_handler_last(CollectTopicStatsHandler);
689        // Persist stats handler should be in front of collect stats handler.
690        // Because collect stats handler will consume the stats from the accumulator.
691        if let Some(persist_stats_handler) = self.persist_stats_handler.take() {
692            self.add_handler_last(persist_stats_handler);
693        }
694        self.add_handler_last(CollectStatsHandler::new(self.flush_stats_factor));
695        self.add_handler_last(RemapFlowPeerHandler::default());
696
697        if let Some(flow_state_handler) = self.flow_state_handler.take() {
698            self.add_handler_last(flow_state_handler);
699        }
700
701        self
702    }
703
704    /// Builds the group of heartbeat handlers.
705    ///
706    /// Applies the customizer if it exists.
707    pub fn build(mut self) -> Result<HeartbeatHandlerGroup> {
708        if let Some(customizer) = self
709            .plugins
710            .as_ref()
711            .and_then(|plugins| plugins.get::<HeartbeatHandlerGroupBuilderCustomizerRef>())
712        {
713            debug!("Customizing the heartbeat handler group builder");
714            customizer.customize(&mut self)?;
715        }
716
717        Ok(HeartbeatHandlerGroup {
718            handlers: self.handlers,
719            pushers: self.pushers,
720        })
721    }
722
723    fn add_handler_after_inner(&mut self, target: &str, handler: NameCachedHandler) -> Result<()> {
724        if let Some(pos) = self.handlers.iter().position(|x| x.name == target) {
725            self.handlers.insert(pos + 1, handler);
726            return Ok(());
727        }
728
729        error::HandlerNotFoundSnafu { name: target }.fail()
730    }
731
732    /// Adds the handler after the specified handler.
733    pub fn add_handler_after(
734        &mut self,
735        target: &'static str,
736        handler: impl HeartbeatHandler + 'static,
737    ) -> Result<()> {
738        self.add_handler_after_inner(target, NameCachedHandler::new(handler))
739    }
740
741    fn add_handler_before_inner(&mut self, target: &str, handler: NameCachedHandler) -> Result<()> {
742        if let Some(pos) = self.handlers.iter().position(|x| x.name == target) {
743            self.handlers.insert(pos, handler);
744            return Ok(());
745        }
746
747        error::HandlerNotFoundSnafu { name: target }.fail()
748    }
749
750    /// Adds the handler before the specified handler.
751    pub fn add_handler_before(
752        &mut self,
753        target: &'static str,
754        handler: impl HeartbeatHandler + 'static,
755    ) -> Result<()> {
756        self.add_handler_before_inner(target, NameCachedHandler::new(handler))
757    }
758
759    fn replace_handler_inner(&mut self, target: &str, handler: NameCachedHandler) -> Result<()> {
760        if let Some(pos) = self.handlers.iter().position(|x| x.name == target) {
761            self.handlers[pos] = handler;
762            return Ok(());
763        }
764
765        error::HandlerNotFoundSnafu { name: target }.fail()
766    }
767
768    /// Replaces the handler with the specified name.
769    pub fn replace_handler(
770        &mut self,
771        target: &'static str,
772        handler: impl HeartbeatHandler + 'static,
773    ) -> Result<()> {
774        self.replace_handler_inner(target, NameCachedHandler::new(handler))
775    }
776
777    fn add_handler_last_inner(&mut self, handler: NameCachedHandler) {
778        self.handlers.push(handler);
779    }
780
781    fn add_handler_last(&mut self, handler: impl HeartbeatHandler + 'static) {
782        self.add_handler_last_inner(NameCachedHandler::new(handler));
783    }
784}
785
786pub type HeartbeatHandlerGroupBuilderCustomizerRef =
787    Arc<dyn HeartbeatHandlerGroupBuilderCustomizer>;
788
789pub enum CustomizeHeartbeatGroupAction {
790    AddHandlerAfter {
791        target: String,
792        handler: NameCachedHandler,
793    },
794    AddHandlerBefore {
795        target: String,
796        handler: NameCachedHandler,
797    },
798    ReplaceHandler {
799        target: String,
800        handler: NameCachedHandler,
801    },
802    AddHandlerLast {
803        handler: NameCachedHandler,
804    },
805}
806
807impl CustomizeHeartbeatGroupAction {
808    pub fn new_add_handler_after(
809        target: &'static str,
810        handler: impl HeartbeatHandler + 'static,
811    ) -> Self {
812        Self::AddHandlerAfter {
813            target: target.to_string(),
814            handler: NameCachedHandler::new(handler),
815        }
816    }
817
818    pub fn new_add_handler_before(
819        target: &'static str,
820        handler: impl HeartbeatHandler + 'static,
821    ) -> Self {
822        Self::AddHandlerBefore {
823            target: target.to_string(),
824            handler: NameCachedHandler::new(handler),
825        }
826    }
827
828    pub fn new_replace_handler(
829        target: &'static str,
830        handler: impl HeartbeatHandler + 'static,
831    ) -> Self {
832        Self::ReplaceHandler {
833            target: target.to_string(),
834            handler: NameCachedHandler::new(handler),
835        }
836    }
837
838    pub fn new_add_handler_last(handler: impl HeartbeatHandler + 'static) -> Self {
839        Self::AddHandlerLast {
840            handler: NameCachedHandler::new(handler),
841        }
842    }
843}
844
845/// The customizer of the [`HeartbeatHandlerGroupBuilder`].
846pub trait HeartbeatHandlerGroupBuilderCustomizer: Send + Sync {
847    fn customize(&self, builder: &mut HeartbeatHandlerGroupBuilder) -> Result<()>;
848
849    fn add_action(&self, action: CustomizeHeartbeatGroupAction);
850}
851
852#[derive(Default)]
853pub struct DefaultHeartbeatHandlerGroupBuilderCustomizer {
854    actions: Mutex<Vec<CustomizeHeartbeatGroupAction>>,
855}
856
857impl HeartbeatHandlerGroupBuilderCustomizer for DefaultHeartbeatHandlerGroupBuilderCustomizer {
858    fn customize(&self, builder: &mut HeartbeatHandlerGroupBuilder) -> Result<()> {
859        info!("Customizing the heartbeat handler group builder");
860        let mut actions = self.actions.lock().unwrap();
861        for action in actions.drain(..) {
862            match action {
863                CustomizeHeartbeatGroupAction::AddHandlerAfter { target, handler } => {
864                    builder.add_handler_after_inner(&target, handler)?;
865                }
866                CustomizeHeartbeatGroupAction::AddHandlerBefore { target, handler } => {
867                    builder.add_handler_before_inner(&target, handler)?;
868                }
869                CustomizeHeartbeatGroupAction::ReplaceHandler { target, handler } => {
870                    builder.replace_handler_inner(&target, handler)?;
871                }
872                CustomizeHeartbeatGroupAction::AddHandlerLast { handler } => {
873                    builder.add_handler_last_inner(handler);
874                }
875            }
876        }
877        Ok(())
878    }
879
880    fn add_action(&self, action: CustomizeHeartbeatGroupAction) {
881        self.actions.lock().unwrap().push(action);
882    }
883}
884
885#[cfg(test)]
886mod tests {
887
888    use std::assert_matches;
889    use std::collections::BTreeMap;
890    use std::sync::Arc;
891    use std::time::Duration;
892
893    use api::v1::meta::{HeartbeatRequest, MailboxMessage, RequestHeader, Role};
894    use common_meta::kv_backend::memory::MemoryKvBackend;
895    use common_meta::sequence::SequenceBuilder;
896    use tokio::sync::mpsc;
897
898    use super::{
899        HandleControl, HeartbeatAccumulator, HeartbeatHandler, HeartbeatHandlerGroupBuilder,
900        PusherId, Pushers,
901    };
902    use crate::error;
903    use crate::handler::collect_stats_handler::CollectStatsHandler;
904    use crate::handler::response_header_handler::ResponseHeaderHandler;
905    use crate::handler::test_utils::TestEnv;
906    use crate::handler::{HeartbeatHandlerGroup, HeartbeatMailbox, Pusher};
907    use crate::metasrv::Context;
908    use crate::service::mailbox::{Channel, MailboxReceiver, MailboxRef};
909
910    #[tokio::test]
911    async fn test_mailbox() {
912        let (mailbox, receiver) = push_msg_via_mailbox().await;
913        let id = receiver.message_id();
914
915        let resp_msg = MailboxMessage {
916            id,
917            subject: "resp-test".to_string(),
918            timestamp_millis: 456,
919            ..Default::default()
920        };
921
922        mailbox.on_recv(id, Ok(resp_msg)).await.unwrap();
923
924        let recv_msg = receiver.await.unwrap();
925        assert_eq!(recv_msg.id, id);
926        assert_eq!(recv_msg.timestamp_millis, 456);
927        assert_eq!(recv_msg.subject, "resp-test".to_string());
928    }
929
930    #[tokio::test]
931    async fn test_mailbox_timeout() {
932        let (_, receiver) = push_msg_via_mailbox().await;
933        let res = receiver.await;
934        assert!(res.is_err());
935    }
936
937    async fn push_msg_via_mailbox() -> (MailboxRef, MailboxReceiver) {
938        let datanode_id = 12;
939        let (pusher_tx, mut pusher_rx) = mpsc::channel(16);
940        let pusher_id = PusherId::new(Role::Datanode, datanode_id);
941        let pusher: Pusher = Pusher::new(pusher_tx);
942        let handler_group = HeartbeatHandlerGroup::default();
943        handler_group.register_pusher(pusher_id, pusher).await;
944
945        let kv_backend = Arc::new(MemoryKvBackend::new());
946        let seq = SequenceBuilder::new("test_seq", kv_backend).build();
947        let mailbox = HeartbeatMailbox::create(handler_group.pushers(), seq);
948
949        let msg = MailboxMessage {
950            id: 0,
951            subject: "req-test".to_string(),
952            timestamp_millis: 123,
953            ..Default::default()
954        };
955        let ch = Channel::Datanode(datanode_id);
956
957        let receiver = mailbox
958            .send(&ch, msg, Duration::from_secs(1))
959            .await
960            .unwrap();
961
962        let recv_obj = pusher_rx.recv().await.unwrap().unwrap();
963        let message = recv_obj.mailbox_message.unwrap();
964        assert_eq!(message.timestamp_millis, 123);
965        assert_eq!(message.subject, "req-test".to_string());
966
967        (mailbox, receiver)
968    }
969
970    #[test]
971    fn test_pusher_id_role_range() {
972        let mut pushers = BTreeMap::new();
973        pushers.insert(PusherId::new(Role::Datanode, u64::MAX), "datanode");
974        pushers.insert(PusherId::new(Role::Frontend, u64::MIN), "frontend-min");
975        pushers.insert(PusherId::new(Role::Frontend, u64::MAX), "frontend-max");
976        pushers.insert(PusherId::new(Role::Flownode, u64::MIN), "flownode");
977
978        let frontend_pushers = pushers
979            .range(PusherId::role_range(Role::Frontend))
980            .map(|(_, value)| *value)
981            .collect::<Vec<_>>();
982
983        assert_eq!(frontend_pushers, vec!["frontend-min", "frontend-max"]);
984    }
985
986    #[tokio::test]
987    async fn test_pushers_broadcast_by_role() {
988        let pushers = Pushers::default();
989        let (datanode_tx, mut datanode_rx) = mpsc::channel(1);
990        let (frontend_tx, mut frontend_rx) = mpsc::channel(1);
991        let (flownode_tx, mut flownode_rx) = mpsc::channel(1);
992
993        pushers
994            .insert(
995                PusherId::new(Role::Datanode, u64::MAX),
996                Pusher::new(datanode_tx),
997            )
998            .await;
999        pushers
1000            .insert(PusherId::new(Role::Frontend, 1), Pusher::new(frontend_tx))
1001            .await;
1002        pushers
1003            .insert(
1004                PusherId::new(Role::Flownode, u64::MIN),
1005                Pusher::new(flownode_tx),
1006            )
1007            .await;
1008
1009        let msg = MailboxMessage {
1010            id: 42,
1011            subject: "broadcast-test".to_string(),
1012            timestamp_millis: 123,
1013            ..Default::default()
1014        };
1015
1016        pushers.broadcast(Role::Frontend, &msg).await.unwrap();
1017
1018        let received = frontend_rx.recv().await.unwrap().unwrap();
1019        let mailbox_message = received.mailbox_message.unwrap();
1020        assert_eq!(mailbox_message.id, 0);
1021        assert_eq!(mailbox_message.subject, "broadcast-test");
1022        assert!(datanode_rx.try_recv().is_err());
1023        assert!(flownode_rx.try_recv().is_err());
1024    }
1025
1026    #[test]
1027    fn test_handler_group_builder() {
1028        let group = HeartbeatHandlerGroupBuilder::new(Pushers::default())
1029            .add_default_handlers()
1030            .build()
1031            .unwrap();
1032
1033        let handlers = group.handlers;
1034        let names = [
1035            "ResponseHeaderHandler",
1036            "DatanodeKeepLeaseHandler",
1037            "FlownodeKeepLeaseHandler",
1038            "CheckLeaderHandler",
1039            "OnLeaderStartHandler",
1040            "ExtractStatHandler",
1041            "CollectDatanodeClusterInfoHandler",
1042            "CollectFrontendClusterInfoHandler",
1043            "CollectFlownodeClusterInfoHandler",
1044            "MailboxHandler",
1045            "FilterInactiveRegionStatsHandler",
1046            "CollectLeaderRegionHandler",
1047            "CollectTopicStatsHandler",
1048            "CollectStatsHandler",
1049            "RemapFlowPeerHandler",
1050        ];
1051        assert_eq!(names.len(), handlers.len());
1052        for (handler, name) in handlers.iter().zip(names) {
1053            assert_eq!(handler.name, name);
1054        }
1055    }
1056
1057    #[test]
1058    fn test_handler_group_builder_add_before() {
1059        let mut builder =
1060            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1061        builder
1062            .add_handler_before(
1063                "FilterInactiveRegionStatsHandler",
1064                CollectStatsHandler::default(),
1065            )
1066            .unwrap();
1067
1068        let group = builder.build().unwrap();
1069        let handlers = group.handlers;
1070        let names = [
1071            "ResponseHeaderHandler",
1072            "DatanodeKeepLeaseHandler",
1073            "FlownodeKeepLeaseHandler",
1074            "CheckLeaderHandler",
1075            "OnLeaderStartHandler",
1076            "ExtractStatHandler",
1077            "CollectDatanodeClusterInfoHandler",
1078            "CollectFrontendClusterInfoHandler",
1079            "CollectFlownodeClusterInfoHandler",
1080            "MailboxHandler",
1081            "CollectStatsHandler",
1082            "FilterInactiveRegionStatsHandler",
1083            "CollectLeaderRegionHandler",
1084            "CollectTopicStatsHandler",
1085            "CollectStatsHandler",
1086            "RemapFlowPeerHandler",
1087        ];
1088        assert_eq!(names.len(), handlers.len());
1089        for (handler, name) in handlers.iter().zip(names) {
1090            assert_eq!(handler.name, name);
1091        }
1092    }
1093
1094    #[test]
1095    fn test_handler_group_builder_add_before_first() {
1096        let mut builder =
1097            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1098        builder
1099            .add_handler_before("ResponseHeaderHandler", CollectStatsHandler::default())
1100            .unwrap();
1101
1102        let group = builder.build().unwrap();
1103        let handlers = group.handlers;
1104        let names = [
1105            "CollectStatsHandler",
1106            "ResponseHeaderHandler",
1107            "DatanodeKeepLeaseHandler",
1108            "FlownodeKeepLeaseHandler",
1109            "CheckLeaderHandler",
1110            "OnLeaderStartHandler",
1111            "ExtractStatHandler",
1112            "CollectDatanodeClusterInfoHandler",
1113            "CollectFrontendClusterInfoHandler",
1114            "CollectFlownodeClusterInfoHandler",
1115            "MailboxHandler",
1116            "FilterInactiveRegionStatsHandler",
1117            "CollectLeaderRegionHandler",
1118            "CollectTopicStatsHandler",
1119            "CollectStatsHandler",
1120            "RemapFlowPeerHandler",
1121        ];
1122        assert_eq!(names.len(), handlers.len());
1123        for (handler, name) in handlers.iter().zip(names) {
1124            assert_eq!(handler.name, name);
1125        }
1126    }
1127
1128    #[test]
1129    fn test_handler_group_builder_add_after() {
1130        let mut builder =
1131            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1132        builder
1133            .add_handler_after("MailboxHandler", CollectStatsHandler::default())
1134            .unwrap();
1135
1136        let group = builder.build().unwrap();
1137        let handlers = group.handlers;
1138        let names = [
1139            "ResponseHeaderHandler",
1140            "DatanodeKeepLeaseHandler",
1141            "FlownodeKeepLeaseHandler",
1142            "CheckLeaderHandler",
1143            "OnLeaderStartHandler",
1144            "ExtractStatHandler",
1145            "CollectDatanodeClusterInfoHandler",
1146            "CollectFrontendClusterInfoHandler",
1147            "CollectFlownodeClusterInfoHandler",
1148            "MailboxHandler",
1149            "CollectStatsHandler",
1150            "FilterInactiveRegionStatsHandler",
1151            "CollectLeaderRegionHandler",
1152            "CollectTopicStatsHandler",
1153            "CollectStatsHandler",
1154            "RemapFlowPeerHandler",
1155        ];
1156        assert_eq!(names.len(), handlers.len());
1157        for (handler, name) in handlers.iter().zip(names) {
1158            assert_eq!(handler.name, name);
1159        }
1160    }
1161
1162    #[test]
1163    fn test_handler_group_builder_add_after_last() {
1164        let mut builder =
1165            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1166        builder
1167            .add_handler_after("CollectStatsHandler", ResponseHeaderHandler)
1168            .unwrap();
1169
1170        let group = builder.build().unwrap();
1171        let handlers = group.handlers;
1172        let names = [
1173            "ResponseHeaderHandler",
1174            "DatanodeKeepLeaseHandler",
1175            "FlownodeKeepLeaseHandler",
1176            "CheckLeaderHandler",
1177            "OnLeaderStartHandler",
1178            "ExtractStatHandler",
1179            "CollectDatanodeClusterInfoHandler",
1180            "CollectFrontendClusterInfoHandler",
1181            "CollectFlownodeClusterInfoHandler",
1182            "MailboxHandler",
1183            "FilterInactiveRegionStatsHandler",
1184            "CollectLeaderRegionHandler",
1185            "CollectTopicStatsHandler",
1186            "CollectStatsHandler",
1187            "ResponseHeaderHandler",
1188            "RemapFlowPeerHandler",
1189        ];
1190        assert_eq!(names.len(), handlers.len());
1191        for (handler, name) in handlers.iter().zip(names) {
1192            assert_eq!(handler.name, name);
1193        }
1194    }
1195
1196    #[test]
1197    fn test_handler_group_builder_replace() {
1198        let mut builder =
1199            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1200        builder
1201            .replace_handler("MailboxHandler", CollectStatsHandler::default())
1202            .unwrap();
1203
1204        let group = builder.build().unwrap();
1205        let handlers = group.handlers;
1206        let names = [
1207            "ResponseHeaderHandler",
1208            "DatanodeKeepLeaseHandler",
1209            "FlownodeKeepLeaseHandler",
1210            "CheckLeaderHandler",
1211            "OnLeaderStartHandler",
1212            "ExtractStatHandler",
1213            "CollectDatanodeClusterInfoHandler",
1214            "CollectFrontendClusterInfoHandler",
1215            "CollectFlownodeClusterInfoHandler",
1216            "CollectStatsHandler",
1217            "FilterInactiveRegionStatsHandler",
1218            "CollectLeaderRegionHandler",
1219            "CollectTopicStatsHandler",
1220            "CollectStatsHandler",
1221            "RemapFlowPeerHandler",
1222        ];
1223
1224        assert_eq!(names.len(), handlers.len());
1225        for (handler, name) in handlers.iter().zip(names) {
1226            assert_eq!(handler.name, name);
1227        }
1228    }
1229
1230    #[test]
1231    fn test_handler_group_builder_replace_last() {
1232        let mut builder =
1233            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1234        builder
1235            .replace_handler("CollectStatsHandler", ResponseHeaderHandler)
1236            .unwrap();
1237
1238        let group = builder.build().unwrap();
1239        let handlers = group.handlers;
1240        let names = [
1241            "ResponseHeaderHandler",
1242            "DatanodeKeepLeaseHandler",
1243            "FlownodeKeepLeaseHandler",
1244            "CheckLeaderHandler",
1245            "OnLeaderStartHandler",
1246            "ExtractStatHandler",
1247            "CollectDatanodeClusterInfoHandler",
1248            "CollectFrontendClusterInfoHandler",
1249            "CollectFlownodeClusterInfoHandler",
1250            "MailboxHandler",
1251            "FilterInactiveRegionStatsHandler",
1252            "CollectLeaderRegionHandler",
1253            "CollectTopicStatsHandler",
1254            "ResponseHeaderHandler",
1255            "RemapFlowPeerHandler",
1256        ];
1257
1258        assert_eq!(names.len(), handlers.len());
1259        for (handler, name) in handlers.iter().zip(names) {
1260            assert_eq!(handler.name, name);
1261        }
1262    }
1263
1264    #[test]
1265    fn test_handler_group_builder_replace_first() {
1266        let mut builder =
1267            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1268        builder
1269            .replace_handler("ResponseHeaderHandler", CollectStatsHandler::default())
1270            .unwrap();
1271
1272        let group = builder.build().unwrap();
1273        let handlers = group.handlers;
1274        let names = [
1275            "CollectStatsHandler",
1276            "DatanodeKeepLeaseHandler",
1277            "FlownodeKeepLeaseHandler",
1278            "CheckLeaderHandler",
1279            "OnLeaderStartHandler",
1280            "ExtractStatHandler",
1281            "CollectDatanodeClusterInfoHandler",
1282            "CollectFrontendClusterInfoHandler",
1283            "CollectFlownodeClusterInfoHandler",
1284            "MailboxHandler",
1285            "FilterInactiveRegionStatsHandler",
1286            "CollectLeaderRegionHandler",
1287            "CollectTopicStatsHandler",
1288            "CollectStatsHandler",
1289            "RemapFlowPeerHandler",
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_handler_not_found() {
1299        let mut builder =
1300            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1301        let err = builder
1302            .add_handler_before("NotExists", CollectStatsHandler::default())
1303            .unwrap_err();
1304        assert_matches!(err, error::Error::HandlerNotFound { .. });
1305
1306        let err = builder
1307            .add_handler_after("NotExists", CollectStatsHandler::default())
1308            .unwrap_err();
1309        assert_matches!(err, error::Error::HandlerNotFound { .. });
1310
1311        let err = builder
1312            .replace_handler("NotExists", CollectStatsHandler::default())
1313            .unwrap_err();
1314        assert_matches!(err, error::Error::HandlerNotFound { .. });
1315    }
1316
1317    #[tokio::test]
1318    async fn test_pusher_drop() {
1319        let (tx, _rx) = mpsc::channel(1);
1320        let pusher = Pusher::new(tx);
1321        let mut deregister_signal_tx = pusher.deregister_signal_receiver.clone();
1322
1323        drop(pusher);
1324        deregister_signal_tx.changed().await.unwrap();
1325    }
1326
1327    #[tokio::test]
1328    async fn test_custom_handler_extension_reaches_response() {
1329        struct CustomExtensionHandler;
1330
1331        #[async_trait::async_trait]
1332        impl HeartbeatHandler for CustomExtensionHandler {
1333            fn is_acceptable(&self, role: Role) -> bool {
1334                role == Role::Frontend
1335            }
1336
1337            async fn handle(
1338                &self,
1339                _req: &HeartbeatRequest,
1340                _ctx: &mut Context,
1341                acc: &mut HeartbeatAccumulator,
1342            ) -> crate::error::Result<HandleControl> {
1343                acc.extensions
1344                    .insert("custom.key".to_string(), b"custom-value".to_vec());
1345                Ok(HandleControl::Continue)
1346            }
1347        }
1348
1349        let mut builder =
1350            HeartbeatHandlerGroupBuilder::new(Pushers::default()).add_default_handlers();
1351        builder.add_handler_last(CustomExtensionHandler);
1352
1353        let group = builder.build().unwrap();
1354
1355        let req = HeartbeatRequest {
1356            header: Some(RequestHeader::new(1, Role::Frontend, Default::default())),
1357            ..Default::default()
1358        };
1359        let ctx = TestEnv::new().ctx();
1360
1361        let res = group.handle(req, ctx).await.unwrap();
1362
1363        let extensions = res.extensions;
1364        assert_eq!(extensions.get("custom.key").unwrap(), b"custom-value");
1365    }
1366}