1use super::*;
2
3impl ConnectionManager {
4 pub fn new() -> Self {
6 Self::with_send_timeout(DEFAULT_SEND_TIMEOUT)
7 }
8
9 pub fn with_send_timeout(send_timeout: Duration) -> Self {
11 Self::with_limits(send_timeout, DEFAULT_FANOUT_CONCURRENCY)
12 }
13
14 pub fn with_limits(send_timeout: Duration, fanout_concurrency: usize) -> Self {
16 Self::with_write_queue_limits(
17 send_timeout,
18 fanout_concurrency,
19 DEFAULT_WRITE_QUEUE_CAPACITY,
20 )
21 }
22
23 pub fn with_write_queue_limits(
25 send_timeout: Duration,
26 fanout_concurrency: usize,
27 write_queue_capacity: usize,
28 ) -> Self {
29 Self {
30 connection_shards: Arc::new(
31 (0..CONNECTION_SHARD_COUNT)
32 .map(|_| RwLock::new(HashMap::new()))
33 .collect(),
34 ),
35 user_connection_shards: Arc::new(
36 (0..USER_CONNECTION_SHARD_COUNT)
37 .map(|_| RwLock::new(HashMap::new()))
38 .collect(),
39 ),
40 connection_count: Arc::new(AtomicUsize::new(0)),
41 user_count: Arc::new(AtomicUsize::new(0)),
42 send_timeout,
43 fanout_concurrency: fanout_concurrency.max(1),
44 write_queue_capacity: write_queue_capacity.max(1),
45 }
46 }
47
48 pub(super) fn removal_registry(&self) -> ConnectionRemovalRegistry {
49 ConnectionRemovalRegistry {
50 connection_shards: Arc::downgrade(&self.connection_shards),
51 user_connection_shards: Arc::downgrade(&self.user_connection_shards),
52 connection_count: Arc::clone(&self.connection_count),
53 user_count: Arc::clone(&self.user_count),
54 }
55 }
56
57 pub(super) fn new_connection_entry(
58 &self,
59 connection_id: &str,
60 connection: ConnectionHandle,
61 info: ConnectionInfo,
62 ) -> ConnectionEntry {
63 let writer = ConnectionWriteQueue::new(
64 connection_id.to_string(),
65 Arc::clone(&connection),
66 self.send_timeout,
67 self.write_queue_capacity,
68 self.removal_registry(),
69 );
70 (connection, writer, info)
71 }
72
73 pub(super) fn shard_index(key: &str, shard_count: usize) -> usize {
74 let mut hasher = DefaultHasher::new();
75 key.hash(&mut hasher);
76 hasher.finish() as usize % shard_count
77 }
78
79 pub(super) fn connection_shard_index(&self, connection_id: &str) -> usize {
80 Self::shard_index(connection_id, self.connection_shards.len())
81 }
82
83 pub(super) fn connection_shard(&self, connection_id: &str) -> &ConnectionShard {
84 &self.connection_shards[self.connection_shard_index(connection_id)]
85 }
86
87 pub(super) fn user_connection_shard_index(&self, user_id: &str) -> usize {
88 Self::shard_index(user_id, self.user_connection_shards.len())
89 }
90
91 pub(super) fn user_connection_shard(&self, user_id: &str) -> &UserConnectionShard {
92 &self.user_connection_shards[self.user_connection_shard_index(user_id)]
93 }
94
95 pub(super) fn reserve_connection_slot(&self, max_connections: usize) -> Result<()> {
96 loop {
97 let current = self.connection_count.load(Ordering::Relaxed);
98 if current >= max_connections {
99 return Err(FlareError::connection_failed(format!(
100 "Connection limit exceeded: {}",
101 max_connections
102 )));
103 }
104
105 if self
106 .connection_count
107 .compare_exchange_weak(current, current + 1, Ordering::AcqRel, Ordering::Relaxed)
108 .is_ok()
109 {
110 return Ok(());
111 }
112 }
113 }
114
115 pub(super) fn release_connection_slot(&self) {
116 self.connection_count.fetch_sub(1, Ordering::Relaxed);
117 }
118
119 pub(super) fn insert_user_connection(
120 &self,
121 user_id: String,
122 connection_id: &str,
123 ) -> Result<()> {
124 let mut user_connections = self
125 .user_connection_shard(&user_id)
126 .write()
127 .map_err(|_| FlareError::general_error("Failed to lock user_connection shard"))?;
128 if Self::insert_user_connection_index(&mut user_connections, user_id, connection_id) {
129 self.user_count.fetch_add(1, Ordering::Relaxed);
130 }
131 Ok(())
132 }
133
134 pub(super) fn remove_user_connection(&self, user_id: &str, connection_id: &str) -> Result<()> {
135 let mut user_connections = self
136 .user_connection_shard(user_id)
137 .write()
138 .map_err(|_| FlareError::general_error("Failed to lock user_connection shard"))?;
139 if Self::remove_user_connection_index(&mut user_connections, user_id, connection_id) {
140 self.user_count.fetch_sub(1, Ordering::Relaxed);
141 }
142 Ok(())
143 }
144
145 pub(super) fn insert_user_connection_index(
146 user_connections: &mut HashMap<String, Vec<String>>,
147 user_id: String,
148 connection_id: &str,
149 ) -> bool {
150 let is_new_user = !user_connections.contains_key(&user_id);
151 let conn_ids = user_connections.entry(user_id).or_default();
152 if !conn_ids.iter().any(|id| id == connection_id) {
153 conn_ids.push(connection_id.to_string());
154 }
155 is_new_user
156 }
157
158 pub(super) fn remove_user_connection_index(
159 user_connections: &mut HashMap<String, Vec<String>>,
160 user_id: &str,
161 connection_id: &str,
162 ) -> bool {
163 let Some(conn_ids) = user_connections.get_mut(user_id) else {
164 return false;
165 };
166
167 conn_ids.retain(|id| id != connection_id);
168 if conn_ids.is_empty() {
169 user_connections.remove(user_id);
170 true
171 } else {
172 false
173 }
174 }
175
176 pub fn add_connection(
187 &self,
188 connection_id: String,
189 connection: Box<dyn Connection>,
190 user_id: Option<String>,
191 requires_auth: bool,
192 ) -> Result<()> {
193 self.add_connection_with_limit(
194 connection_id,
195 connection,
196 user_id,
197 requires_auth,
198 usize::MAX,
199 )
200 }
201
202 pub fn add_connection_with_limit(
207 &self,
208 connection_id: String,
209 connection: Box<dyn Connection>,
210 user_id: Option<String>,
211 requires_auth: bool,
212 max_connections: usize,
213 ) -> Result<()> {
214 self.reserve_connection_slot(max_connections)?;
215
216 let mut shard = match self.connection_shard(&connection_id).write() {
217 Ok(shard) => shard,
218 Err(_) => {
219 self.release_connection_slot();
220 return Err(FlareError::general_error("Failed to lock connection shard"));
221 }
222 };
223
224 if shard.contains_key(&connection_id) {
225 self.release_connection_slot();
226 return Err(FlareError::protocol_error(format!(
227 "Connection {} already exists",
228 connection_id
229 )));
230 }
231
232 let mut info = ConnectionInfo::new(connection_id.clone(), requires_auth);
233 info.user_id = user_id.clone();
234
235 let connection = Arc::new(Mutex::new(connection));
236 let entry = self.new_connection_entry(&connection_id, connection, info);
237 shard.insert(connection_id.clone(), entry);
238
239 if let Some(user_id) = user_id
241 && let Err(err) = self.insert_user_connection(user_id, &connection_id)
242 {
243 shard.remove(&connection_id);
244 self.release_connection_slot();
245 return Err(err);
246 }
247
248 Ok(())
249 }
250
251 pub fn remove_connection(&self, connection_id: &str) -> Result<()> {
259 let mut shard = self
260 .connection_shard(connection_id)
261 .write()
262 .map_err(|_| FlareError::general_error("Failed to lock connection shard"))?;
263
264 let (_, writer, info) = shard.remove(connection_id).ok_or_else(|| {
265 FlareError::protocol_error(format!("Connection {} not found", connection_id))
266 })?;
267 writer.close_underlying_in_background();
268 self.release_connection_slot();
269
270 if let Some(user_id) = info.user_id {
272 self.remove_user_connection(&user_id, connection_id)?;
273 }
274
275 Ok(())
276 }
277
278 #[allow(clippy::type_complexity)]
286 pub fn get_connection(
287 &self,
288 connection_id: &str,
289 ) -> Option<(ConnectionHandle, ConnectionInfo)> {
290 let shard = self.connection_shard(connection_id).read().ok()?;
291 let (conn, _, info) = shard.get(connection_id)?;
292 let conn_clone = Arc::clone(conn);
293 let info_clone = info.clone();
294 drop(shard);
295 Some((conn_clone, info_clone))
296 }
297
298 pub(super) fn get_connection_snapshot(
299 &self,
300 connection_id: &str,
301 ) -> Option<ConnectionSnapshot> {
302 let shard = self.connection_shard(connection_id).read().ok()?;
303 let (_, writer, info) = shard.get(connection_id)?;
304 Some((connection_id.to_string(), Arc::clone(writer), info.clone()))
305 }
306
307 pub(super) fn connection_handles(&self) -> Vec<ConnectionHandleSnapshot> {
308 self.connection_shards
309 .iter()
310 .flat_map(|shard| {
311 shard
312 .read()
313 .ok()
314 .map(|connections| {
315 connections
316 .iter()
317 .map(|(id, (_, writer, _))| (id.clone(), Arc::clone(writer)))
318 .collect::<Vec<_>>()
319 })
320 .unwrap_or_default()
321 })
322 .collect()
323 }
324
325 pub(super) fn connection_handles_except(
326 &self,
327 exclude_connection_id: &str,
328 ) -> Vec<ConnectionHandleSnapshot> {
329 self.connection_shards
330 .iter()
331 .flat_map(|shard| {
332 shard
333 .read()
334 .ok()
335 .map(|connections| {
336 connections
337 .iter()
338 .filter(|(id, _)| id.as_str() != exclude_connection_id)
339 .map(|(id, (_, writer, _))| (id.clone(), Arc::clone(writer)))
340 .collect::<Vec<_>>()
341 })
342 .unwrap_or_default()
343 })
344 .collect()
345 }
346
347 pub(super) fn connection_handles_for_ids(
348 &self,
349 connection_ids: Vec<String>,
350 ) -> Vec<ConnectionHandleSnapshot> {
351 let mut ids_by_shard = vec![Vec::new(); self.connection_shards.len()];
352 for connection_id in connection_ids {
353 let shard_index = self.connection_shard_index(&connection_id);
354 ids_by_shard[shard_index].push(connection_id);
355 }
356
357 ids_by_shard
358 .into_iter()
359 .enumerate()
360 .flat_map(|(shard_index, ids)| {
361 if ids.is_empty() {
362 return Vec::new();
363 }
364
365 self.connection_shards[shard_index]
366 .read()
367 .ok()
368 .map(|connections| {
369 ids.into_iter()
370 .filter_map(|id| {
371 connections
372 .get(&id)
373 .map(|(_, writer, _)| (id, Arc::clone(writer)))
374 })
375 .collect::<Vec<_>>()
376 })
377 .unwrap_or_default()
378 })
379 .collect()
380 }
381
382 pub(super) fn connection_auth_snapshots(&self) -> Vec<ConnectionAuthSnapshot> {
383 self.connection_shards
384 .iter()
385 .flat_map(|shard| {
386 shard
387 .read()
388 .ok()
389 .map(|connections| {
390 connections
391 .iter()
392 .map(|(id, (_, writer, info))| {
393 (id.clone(), Arc::clone(writer), info.authenticated)
394 })
395 .collect::<Vec<_>>()
396 })
397 .unwrap_or_default()
398 })
399 .collect()
400 }
401
402 pub(super) fn connection_auth_snapshots_except(
403 &self,
404 exclude_connection_id: &str,
405 ) -> Vec<ConnectionAuthSnapshot> {
406 self.connection_shards
407 .iter()
408 .flat_map(|shard| {
409 shard
410 .read()
411 .ok()
412 .map(|connections| {
413 connections
414 .iter()
415 .filter(|(id, _)| id.as_str() != exclude_connection_id)
416 .map(|(id, (_, writer, info))| {
417 (id.clone(), Arc::clone(writer), info.authenticated)
418 })
419 .collect::<Vec<_>>()
420 })
421 .unwrap_or_default()
422 })
423 .collect()
424 }
425
426 pub(super) fn connection_auth_snapshots_for_ids(
427 &self,
428 connection_ids: Vec<String>,
429 ) -> Vec<ConnectionAuthSnapshot> {
430 let mut ids_by_shard = vec![Vec::new(); self.connection_shards.len()];
431 for connection_id in connection_ids {
432 let shard_index = self.connection_shard_index(&connection_id);
433 ids_by_shard[shard_index].push(connection_id);
434 }
435
436 ids_by_shard
437 .into_iter()
438 .enumerate()
439 .flat_map(|(shard_index, ids)| {
440 if ids.is_empty() {
441 return Vec::new();
442 }
443
444 self.connection_shards[shard_index]
445 .read()
446 .ok()
447 .map(|connections| {
448 ids.into_iter()
449 .filter_map(|id| {
450 connections.get(&id).map(|(_, writer, info)| {
451 (id, Arc::clone(writer), info.authenticated)
452 })
453 })
454 .collect::<Vec<_>>()
455 })
456 .unwrap_or_default()
457 })
458 .collect()
459 }
460
461 pub(super) fn connection_snapshots(&self) -> Vec<ConnectionSnapshot> {
462 self.connection_shards
463 .iter()
464 .flat_map(|shard| {
465 shard
466 .read()
467 .ok()
468 .map(|connections| {
469 connections
470 .iter()
471 .map(|(id, (_, writer, info))| {
472 (id.clone(), Arc::clone(writer), info.clone())
473 })
474 .collect::<Vec<_>>()
475 })
476 .unwrap_or_default()
477 })
478 .collect()
479 }
480
481 pub(super) fn connection_snapshots_except(
482 &self,
483 exclude_connection_id: &str,
484 ) -> Vec<ConnectionSnapshot> {
485 self.connection_shards
486 .iter()
487 .flat_map(|shard| {
488 shard
489 .read()
490 .ok()
491 .map(|connections| {
492 connections
493 .iter()
494 .filter(|(id, _)| id.as_str() != exclude_connection_id)
495 .map(|(id, (_, writer, info))| {
496 (id.clone(), Arc::clone(writer), info.clone())
497 })
498 .collect::<Vec<_>>()
499 })
500 .unwrap_or_default()
501 })
502 .collect()
503 }
504
505 pub(super) fn connection_snapshots_for_ids(
506 &self,
507 connection_ids: Vec<String>,
508 ) -> Vec<ConnectionSnapshot> {
509 let mut ids_by_shard = vec![Vec::new(); self.connection_shards.len()];
510 for connection_id in connection_ids {
511 let shard_index = self.connection_shard_index(&connection_id);
512 ids_by_shard[shard_index].push(connection_id);
513 }
514
515 ids_by_shard
516 .into_iter()
517 .enumerate()
518 .flat_map(|(shard_index, ids)| {
519 if ids.is_empty() {
520 return Vec::new();
521 }
522
523 self.connection_shards[shard_index]
524 .read()
525 .ok()
526 .map(|connections| {
527 ids.into_iter()
528 .filter_map(|id| {
529 connections
530 .get(&id)
531 .map(|(_, writer, info)| (id, Arc::clone(writer), info.clone()))
532 })
533 .collect::<Vec<_>>()
534 })
535 .unwrap_or_default()
536 })
537 .collect()
538 }
539
540 pub(super) fn timeout_connection_snapshots(
541 &self,
542 timeout: Duration,
543 ) -> Vec<TimeoutConnectionSnapshot> {
544 self.connection_shards
545 .iter()
546 .flat_map(|shard| {
547 shard
548 .read()
549 .ok()
550 .map(|connections| {
551 connections
552 .iter()
553 .filter(|(_, (_, _, info))| info.is_timeout(timeout))
554 .map(|(id, (connection, _, info))| {
555 (id.clone(), Arc::clone(connection), info.user_id.clone())
556 })
557 .collect::<Vec<_>>()
558 })
559 .unwrap_or_default()
560 })
561 .collect()
562 }
563
564 pub(super) fn remove_connection_snapshots<I>(&self, connection_ids: I) -> Vec<String>
565 where
566 I: IntoIterator<Item = String>,
567 {
568 let mut ids_by_shard = vec![Vec::new(); self.connection_shards.len()];
569 for connection_id in connection_ids {
570 let shard_index = self.connection_shard_index(&connection_id);
571 ids_by_shard[shard_index].push(connection_id);
572 }
573
574 let mut removed_ids = Vec::new();
575 let mut removed_user_connections = Vec::new();
576
577 for (shard_index, ids) in ids_by_shard.into_iter().enumerate() {
578 if ids.is_empty() {
579 continue;
580 }
581
582 let Ok(mut shard) = self.connection_shards[shard_index].write() else {
583 continue;
584 };
585
586 for connection_id in ids {
587 if let Some((_, writer, info)) = shard.remove(&connection_id) {
588 writer.close_underlying_in_background();
589 if let Some(user_id) = info.user_id {
590 removed_user_connections.push((user_id, connection_id.clone()));
591 }
592 removed_ids.push(connection_id);
593 }
594 }
595 }
596
597 if !removed_ids.is_empty() {
598 self.connection_count
599 .fetch_sub(removed_ids.len(), Ordering::Relaxed);
600 }
601
602 self.remove_user_connections_batch(removed_user_connections);
603
604 removed_ids
605 }
606
607 pub(super) fn remove_user_connections_batch<I>(&self, user_connections: I)
608 where
609 I: IntoIterator<Item = (String, String)>,
610 {
611 let mut entries_by_shard = vec![Vec::new(); self.user_connection_shards.len()];
612 for (user_id, connection_id) in user_connections {
613 let shard_index = self.user_connection_shard_index(&user_id);
614 entries_by_shard[shard_index].push((user_id, connection_id));
615 }
616
617 let removed_users = entries_by_shard
618 .into_iter()
619 .enumerate()
620 .map(|(shard_index, entries)| {
621 if entries.is_empty() {
622 return 0;
623 }
624
625 let Ok(mut shard) = self.user_connection_shards[shard_index].write() else {
626 return 0;
627 };
628
629 entries
630 .into_iter()
631 .filter(|(user_id, connection_id)| {
632 Self::remove_user_connection_index(&mut shard, user_id, connection_id)
633 })
634 .count()
635 })
636 .sum::<usize>();
637
638 if removed_users > 0 {
639 self.user_count.fetch_sub(removed_users, Ordering::Relaxed);
640 }
641 }
642}