1pub mod common;
16pub mod lease_based;
17pub mod load_based;
18pub mod round_robin;
19#[cfg(test)]
20pub(crate) mod test_utils;
21pub mod weight_compute;
22pub mod weighted_choose;
23use std::collections::HashSet;
24
25use api::v1::meta::heartbeat_request::NodeWorkloads;
26use common_meta::affinity::AffinityRequirements;
27use serde::{Deserialize, Serialize};
28use store_api::storage::RegionId;
29use strum::AsRefStr;
30
31use crate::error;
32use crate::error::Result;
33
34#[async_trait::async_trait]
35pub trait Selector: Send + Sync {
36 type Context;
37 type Output;
38
39 async fn select(&self, ctx: &Self::Context, opts: SelectorOptions<'_>) -> Result<Self::Output>;
40}
41
42#[async_trait::async_trait]
47pub trait RegionStatAwareSelector: Send + Sync {
48 type Context;
49 type Output;
50
51 async fn select(
52 &self,
53 ctx: &Self::Context,
54 from_peer_id: u64,
55 region_ids: &[RegionId],
56 exclude_peer_ids: HashSet<u64>,
57 affinity: Option<&AffinityRequirements>,
58 ) -> Result<Self::Output>;
59}
60
61#[derive(Debug, Clone)]
62pub struct SelectorOptions<'a> {
63 pub affinity: Option<&'a AffinityRequirements>,
65 pub min_required_items: usize,
67 pub allow_duplication: bool,
69 pub exclude_peer_ids: HashSet<u64>,
71 pub workload_filter: Option<fn(&NodeWorkloads) -> bool>,
73}
74
75impl Default for SelectorOptions<'_> {
76 fn default() -> Self {
77 Self {
78 affinity: None,
79 min_required_items: 1,
80 allow_duplication: false,
81 exclude_peer_ids: HashSet::new(),
82 workload_filter: None,
83 }
84 }
85}
86
87#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default, AsRefStr)]
89#[serde(try_from = "String")]
90pub enum SelectorType {
91 LoadBased,
95 LeaseBased,
98 #[default]
102 RoundRobin,
103}
104
105impl TryFrom<&str> for SelectorType {
106 type Error = error::Error;
107
108 fn try_from(value: &str) -> Result<Self> {
109 match value {
110 "load_based" | "LoadBased" => Ok(SelectorType::LoadBased),
111 "lease_based" | "LeaseBased" => Ok(SelectorType::LeaseBased),
112 "round_robin" | "RoundRobin" => Ok(SelectorType::RoundRobin),
113 other => error::UnsupportedSelectorTypeSnafu {
114 selector_type: other,
115 }
116 .fail(),
117 }
118 }
119}
120
121impl TryFrom<String> for SelectorType {
122 type Error = error::Error;
123
124 fn try_from(value: String) -> Result<Self> {
125 SelectorType::try_from(value.as_str())
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::SelectorType;
132 use crate::error::Result;
133
134 #[test]
135 fn test_default_selector_type() {
136 assert_eq!(SelectorType::RoundRobin, SelectorType::default());
137 }
138
139 #[test]
140 fn test_convert_str_to_selector_type() {
141 let lease_based = "lease_based";
142 let selector_type = lease_based.try_into().unwrap();
143 assert_eq!(SelectorType::LeaseBased, selector_type);
144 let lease_based = "LeaseBased";
145 let selector_type = lease_based.try_into().unwrap();
146 assert_eq!(SelectorType::LeaseBased, selector_type);
147
148 let load_based = "load_based";
149 let selector_type = load_based.try_into().unwrap();
150 assert_eq!(SelectorType::LoadBased, selector_type);
151 let load_based = "LoadBased";
152 let selector_type = load_based.try_into().unwrap();
153 assert_eq!(SelectorType::LoadBased, selector_type);
154
155 let unknown = "unknown";
156 let selector_type: Result<SelectorType> = unknown.try_into();
157 assert!(selector_type.is_err());
158 }
159}