foxtive_supervisor/
task_pool.rs1use crate::Supervisor;
7use crate::contracts::SupervisedTask;
8use std::sync::Arc;
9use tokio::sync::RwLock;
10use tracing::info;
11
12#[derive(Debug, Clone, Copy, Default)]
14pub enum LoadBalancingStrategy {
15 #[default]
17 RoundRobin,
18 Random,
20 LeastLoaded,
22}
23
24pub struct TaskPool {
26 pub id: String,
28 pub pool_size: usize,
30 pub strategy: LoadBalancingStrategy,
32 current_index: Arc<RwLock<usize>>,
34}
35
36impl TaskPool {
37 pub fn new(id: impl Into<String>, pool_size: usize, strategy: LoadBalancingStrategy) -> Self {
39 Self {
40 id: id.into(),
41 pool_size,
42 strategy,
43 current_index: Arc::new(RwLock::new(0)),
44 }
45 }
46
47 pub async fn get_next_worker(&self) -> usize {
49 match self.strategy {
50 LoadBalancingStrategy::RoundRobin => {
51 let mut index = self.current_index.write().await;
52 let worker = *index;
53 *index = (*index + 1) % self.pool_size;
54 worker
55 }
56 LoadBalancingStrategy::Random => {
57 rand::random_range(0..self.pool_size)
60 }
61 LoadBalancingStrategy::LeastLoaded => {
62 let mut index = self.current_index.write().await;
65 let worker = *index;
66 *index = (*index + 1) % self.pool_size;
67 worker
68 }
69 }
70 }
71
72 pub fn build_pool<T, F>(&self, task_factory: F) -> Supervisor
74 where
75 T: SupervisedTask + 'static,
76 F: Fn(usize) -> T,
77 {
78 let mut supervisor = Supervisor::new();
79
80 for i in 0..self.pool_size {
81 let task = task_factory(i);
82 supervisor = supervisor.add(task);
83 }
84
85 info!(pool_id = %self.id, size = self.pool_size, "Created task pool");
86 supervisor
87 }
88
89 pub fn info(&self) -> PoolInfo {
91 PoolInfo {
92 id: self.id.clone(),
93 pool_size: self.pool_size,
94 strategy: self.strategy,
95 }
96 }
97}
98
99#[derive(Debug, Clone)]
101pub struct PoolInfo {
102 pub id: String,
103 pub pool_size: usize,
104 pub strategy: LoadBalancingStrategy,
105}
106
107pub struct TaskPoolBuilder {
109 id: String,
110 pool_size: usize,
111 strategy: LoadBalancingStrategy,
112}
113
114impl TaskPoolBuilder {
115 pub fn new(id: impl Into<String>) -> Self {
117 Self {
118 id: id.into(),
119 pool_size: 1,
120 strategy: LoadBalancingStrategy::default(),
121 }
122 }
123
124 pub fn with_size(mut self, size: usize) -> Self {
126 self.pool_size = size.max(1);
127 self
128 }
129
130 pub fn with_strategy(mut self, strategy: LoadBalancingStrategy) -> Self {
132 self.strategy = strategy;
133 self
134 }
135
136 pub fn build(self) -> TaskPool {
138 TaskPool::new(self.id, self.pool_size, self.strategy)
139 }
140
141 pub fn build_and_create<T, F>(self, task_factory: F) -> Supervisor
143 where
144 T: SupervisedTask + 'static,
145 F: Fn(usize) -> T,
146 {
147 let pool = self.build();
148 pool.build_pool(task_factory)
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[tokio::test]
157 async fn test_round_robin_distribution() {
158 let pool = TaskPool::new("test-pool", 3, LoadBalancingStrategy::RoundRobin);
159
160 assert_eq!(pool.get_next_worker().await, 0);
162 assert_eq!(pool.get_next_worker().await, 1);
163 assert_eq!(pool.get_next_worker().await, 2);
164 assert_eq!(pool.get_next_worker().await, 0);
165 assert_eq!(pool.get_next_worker().await, 1);
166 }
167
168 #[tokio::test]
169 async fn test_random_distribution() {
170 let pool = TaskPool::new("test-pool", 5, LoadBalancingStrategy::Random);
171
172 for _ in 0..20 {
174 let worker = pool.get_next_worker().await;
175 assert!(worker < 5);
176 }
177 }
178
179 #[tokio::test]
180 async fn test_pool_builder() {
181 let pool = TaskPoolBuilder::new("worker-pool")
182 .with_size(4)
183 .with_strategy(LoadBalancingStrategy::RoundRobin)
184 .build();
185
186 assert_eq!(pool.id, "worker-pool");
187 assert_eq!(pool.pool_size, 4);
188 assert!(matches!(pool.strategy, LoadBalancingStrategy::RoundRobin));
189 }
190
191 #[tokio::test]
192 async fn test_pool_info() {
193 let pool = TaskPool::new("info-test", 3, LoadBalancingStrategy::Random);
194 let info = pool.info();
195
196 assert_eq!(info.id, "info-test");
197 assert_eq!(info.pool_size, 3);
198 assert!(matches!(info.strategy, LoadBalancingStrategy::Random));
199 }
200}