meta_srv/discovery/
node_scheduling.rs1use api::v1::meta::heartbeat_request::NodeWorkloads;
16use common_error::ext::BoxedError;
17use common_meta::cluster::NodeInfo;
18use common_meta::error::Result;
19use common_meta::peer::PeerDiscovery;
20use snafu::ResultExt;
21
22use crate::cluster::MetaPeerClientRef;
23use crate::key::NodeSchedulingRole;
24use crate::node_scheduling::NodeSchedulingManagerRef;
25
26pub struct SchedulingAwarePeerDiscovery {
40 manager: NodeSchedulingManagerRef,
41 inner: MetaPeerClientRef,
42}
43
44impl SchedulingAwarePeerDiscovery {
45 pub fn new(manager: NodeSchedulingManagerRef, inner: MetaPeerClientRef) -> Self {
46 Self { manager, inner }
47 }
48}
49
50#[async_trait::async_trait]
51impl PeerDiscovery for SchedulingAwarePeerDiscovery {
52 async fn active_frontends(&self) -> Result<Vec<NodeInfo>> {
53 self.inner.active_frontends().await
54 }
55
56 async fn active_datanodes(
57 &self,
58 filter: Option<for<'a> fn(&'a NodeWorkloads) -> bool>,
59 ) -> Result<Vec<NodeInfo>> {
60 let mut datanodes = self.inner.active_datanodes(filter).await?;
61 if datanodes.is_empty() {
62 return Ok(datanodes);
63 }
64
65 let unschedulable = self
66 .manager
67 .list_unschedulable_node_ids(NodeSchedulingRole::Datanode)
68 .await
69 .map_err(BoxedError::new)
70 .context(common_meta::error::ExternalSnafu)?;
71 for datanode in &mut datanodes {
72 datanode.schedulable = Some(!unschedulable.contains(&datanode.peer.id));
73 }
74 Ok(datanodes)
75 }
76
77 async fn active_flownodes(
78 &self,
79 filter: Option<for<'a> fn(&'a NodeWorkloads) -> bool>,
80 ) -> Result<Vec<NodeInfo>> {
81 self.inner.active_flownodes(filter).await
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use common_meta::peer::Peer;
88
89 use super::*;
90 use crate::cluster::MetaPeerClientRef;
91 use crate::test_util::{create_meta_peer_client, new_node_scheduling_discovery, put_datanodes};
92
93 async fn new_discovery() -> (
94 MetaPeerClientRef,
95 NodeSchedulingManagerRef,
96 SchedulingAwarePeerDiscovery,
97 ) {
98 let client = create_meta_peer_client();
99 put_datanodes(
100 &client,
101 vec![Peer::empty(1), Peer::empty(2), Peer::empty(3)],
102 )
103 .await;
104 let (manager, discovery) = new_node_scheduling_discovery(&client);
105 (client, manager, discovery)
106 }
107
108 #[tokio::test]
109 async fn test_active_datanodes_marks_unschedulable() {
110 let (_, manager, discovery) = new_discovery().await;
111
112 manager
113 .mark_unschedulable(NodeSchedulingRole::Datanode, 2)
114 .await
115 .unwrap();
116
117 let datanodes = discovery.active_datanodes(None).await.unwrap();
118 assert_eq!(3, datanodes.len());
119
120 let schedulable = |id: u64| {
121 datanodes
122 .iter()
123 .find(|datanode| datanode.peer.id == id)
124 .unwrap()
125 .schedulable
126 };
127 assert_eq!(Some(true), schedulable(1));
128 assert_eq!(Some(false), schedulable(2));
129 assert_eq!(Some(true), schedulable(3));
130 }
131
132 #[tokio::test]
133 async fn test_schedulable_marks_are_not_persisted() {
134 let (client, manager, discovery) = new_discovery().await;
135
136 manager
137 .mark_unschedulable(NodeSchedulingRole::Datanode, 2)
138 .await
139 .unwrap();
140
141 discovery.active_datanodes(None).await.unwrap();
142
143 let datanodes = client.active_datanodes(None).await.unwrap();
144 assert_eq!(3, datanodes.len());
145 assert!(
146 datanodes
147 .iter()
148 .all(|datanode| datanode.schedulable.is_none())
149 );
150 }
151}