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