1use std::sync::Arc;
2use std::time::{Duration, Instant};
3use tokio::sync::{RwLock, Semaphore};
4use tracing::{debug, warn};
5
6#[derive(Debug, Clone)]
8pub struct PoolConfig {
9 pub max_connections: usize,
11 pub min_idle: usize,
13 pub max_idle_time: Duration,
15 pub connection_timeout: Duration,
17 pub health_check_enabled: bool,
19 pub health_check_interval: Duration,
21}
22
23impl Default for PoolConfig {
24 fn default() -> Self {
25 Self {
26 max_connections: 100,
27 min_idle: 10,
28 max_idle_time: Duration::from_secs(600), connection_timeout: Duration::from_secs(30),
30 health_check_enabled: true,
31 health_check_interval: Duration::from_secs(60),
32 }
33 }
34}
35
36pub struct PooledConnection<T> {
38 inner: T,
39 created_at: Instant,
40 last_used: Instant,
41}
42
43impl<T> PooledConnection<T> {
44 pub fn new(connection: T) -> Self {
46 let now = Instant::now();
47 Self {
48 inner: connection,
49 created_at: now,
50 last_used: now,
51 }
52 }
53
54 pub fn get(&self) -> &T {
56 &self.inner
57 }
58
59 pub fn get_mut(&mut self) -> &mut T {
61 self.last_used = Instant::now();
62 &mut self.inner
63 }
64
65 pub fn is_stale(&self, max_idle_time: Duration) -> bool {
67 self.last_used.elapsed() > max_idle_time
68 }
69
70 pub fn age(&self) -> Duration {
72 self.created_at.elapsed()
73 }
74}
75
76pub struct ConnectionPool<T> {
78 config: PoolConfig,
79 available: Arc<RwLock<Vec<PooledConnection<T>>>>,
80 semaphore: Arc<Semaphore>,
81 metrics: Arc<RwLock<PoolMetrics>>,
82}
83
84#[derive(Debug, Default, Clone)]
86pub struct PoolMetrics {
87 pub active_connections: usize,
89 pub idle_connections: usize,
91 pub total_acquired: u64,
93 pub total_released: u64,
95 pub total_created: u64,
97 pub total_closed: u64,
99 pub acquire_timeouts: u64,
101 pub health_check_failures: u64,
103}
104
105impl<T> ConnectionPool<T>
106where
107 T: Send + 'static,
108{
109 pub fn new(config: PoolConfig) -> Self {
111 Self {
112 semaphore: Arc::new(Semaphore::new(config.max_connections)),
113 available: Arc::new(RwLock::new(Vec::with_capacity(config.max_connections))),
114 metrics: Arc::new(RwLock::new(PoolMetrics::default())),
115 config,
116 }
117 }
118
119 pub async fn acquire<F, Fut>(&self, create_fn: F) -> Result<PooledConnection<T>, PoolError>
121 where
122 F: FnOnce() -> Fut,
123 Fut: std::future::Future<Output = Result<T, PoolError>>,
124 {
125 let permit = tokio::time::timeout(
127 self.config.connection_timeout,
128 self.semaphore.clone().acquire_owned(),
129 )
130 .await
131 .map_err(|_| {
132 debug!("Connection pool acquire timeout");
133 PoolError::Timeout
134 })?
135 .map_err(|_| PoolError::Closed)?;
136
137 let mut available = self.available.write().await;
139
140 available.retain(|conn| !conn.is_stale(self.config.max_idle_time));
142
143 let connection = if let Some(mut conn) = available.pop() {
144 conn.last_used = Instant::now();
146 drop(available);
147
148 let mut metrics = self.metrics.write().await;
149 metrics.total_acquired += 1;
150 metrics.active_connections += 1;
151 metrics.idle_connections = metrics.idle_connections.saturating_sub(1);
152 drop(metrics);
153
154 debug!("Reusing pooled connection");
155 conn
156 } else {
157 drop(available);
158
159 let inner = create_fn().await?;
161 let conn = PooledConnection::new(inner);
162
163 let mut metrics = self.metrics.write().await;
164 metrics.total_created += 1;
165 metrics.total_acquired += 1;
166 metrics.active_connections += 1;
167 drop(metrics);
168
169 debug!("Created new pooled connection");
170 conn
171 };
172
173 std::mem::forget(permit);
175
176 Ok(connection)
177 }
178
179 pub async fn release(&self, connection: PooledConnection<T>) {
181 let mut available = self.available.write().await;
182
183 if available.len() >= self.config.min_idle && connection.is_stale(self.config.max_idle_time)
185 {
186 drop(available);
187
188 let mut metrics = self.metrics.write().await;
189 metrics.total_closed += 1;
190 metrics.active_connections = metrics.active_connections.saturating_sub(1);
191 drop(metrics);
192
193 self.semaphore.add_permits(1);
194 debug!("Closed stale connection");
195 return;
196 }
197
198 available.push(connection);
199 drop(available);
200
201 let mut metrics = self.metrics.write().await;
202 metrics.total_released += 1;
203 metrics.active_connections = metrics.active_connections.saturating_sub(1);
204 metrics.idle_connections += 1;
205 drop(metrics);
206
207 self.semaphore.add_permits(1);
208 debug!("Released connection to pool");
209 }
210
211 pub async fn metrics(&self) -> PoolMetrics {
213 self.metrics.read().await.clone()
214 }
215
216 pub async fn size(&self) -> usize {
218 self.available.read().await.len()
219 }
220
221 pub async fn health_check<F, Fut>(&self, check_fn: F)
223 where
224 F: Fn(&T) -> Fut,
225 Fut: std::future::Future<Output = bool>,
226 {
227 if !self.config.health_check_enabled {
228 return;
229 }
230
231 let mut available = self.available.write().await;
232 let mut healthy = Vec::new();
233 let mut failures = 0;
234
235 for conn in available.drain(..) {
236 if check_fn(conn.get()).await {
237 healthy.push(conn);
238 } else {
239 failures += 1;
240 warn!("Connection failed health check");
241 }
242 }
243
244 *available = healthy;
245 drop(available);
246
247 if failures > 0 {
248 let mut metrics = self.metrics.write().await;
249 metrics.health_check_failures += failures;
250 metrics.total_closed += failures;
251 metrics.idle_connections = metrics.idle_connections.saturating_sub(failures as usize);
252 drop(metrics);
253
254 self.semaphore.add_permits(failures as usize);
255 }
256 }
257
258 pub async fn maintain_idle<F, Fut>(&self, create_fn: F)
260 where
261 F: Fn() -> Fut,
262 Fut: std::future::Future<Output = Result<T, PoolError>>,
263 {
264 let current_idle = self.available.read().await.len();
265
266 if current_idle < self.config.min_idle {
267 let needed = self.config.min_idle - current_idle;
268
269 for _ in 0..needed {
270 if let Ok(permit) = self.semaphore.clone().try_acquire_owned() {
271 match create_fn().await {
272 Ok(conn) => {
273 let pooled = PooledConnection::new(conn);
274 self.available.write().await.push(pooled);
275
276 let mut metrics = self.metrics.write().await;
277 metrics.total_created += 1;
278 metrics.idle_connections += 1;
279
280 std::mem::forget(permit);
281 }
282 Err(e) => {
283 warn!("Failed to create idle connection: {:?}", e);
284 drop(permit);
285 }
286 }
287 } else {
288 break;
289 }
290 }
291 }
292 }
293}
294
295#[derive(Debug, thiserror::Error)]
297pub enum PoolError {
298 #[error("Connection pool timeout")]
300 Timeout,
301
302 #[error("Connection pool closed")]
304 Closed,
305
306 #[error("Failed to create connection: {0}")]
308 CreateError(String),
309
310 #[error("Connection error: {0}")]
312 ConnectionError(String),
313}
314
315pub type HttpClientPool = ConnectionPool<reqwest::Client>;
317
318impl HttpClientPool {
319 pub fn new_http(config: PoolConfig) -> Self {
321 Self::new(config)
322 }
323
324 pub async fn acquire_client(&self) -> Result<PooledConnection<reqwest::Client>, PoolError> {
326 self.acquire(|| async {
327 reqwest::Client::builder()
328 .timeout(Duration::from_secs(30))
329 .pool_max_idle_per_host(10)
330 .build()
331 .map_err(|e| PoolError::CreateError(e.to_string()))
332 })
333 .await
334 }
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340 use std::sync::atomic::{AtomicUsize, Ordering};
341
342 #[test]
344 fn test_pool_config_default() {
345 let config = PoolConfig::default();
346 assert_eq!(config.max_connections, 100);
347 assert_eq!(config.min_idle, 10);
348 assert_eq!(config.max_idle_time, Duration::from_secs(600));
349 assert_eq!(config.connection_timeout, Duration::from_secs(30));
350 assert!(config.health_check_enabled);
351 assert_eq!(config.health_check_interval, Duration::from_secs(60));
352 }
353
354 #[test]
355 fn test_pool_config_clone() {
356 let config = PoolConfig {
357 max_connections: 50,
358 min_idle: 5,
359 ..Default::default()
360 };
361 let cloned = config.clone();
362 assert_eq!(cloned.max_connections, config.max_connections);
363 assert_eq!(cloned.min_idle, config.min_idle);
364 }
365
366 #[test]
367 fn test_pool_config_debug() {
368 let config = PoolConfig::default();
369 let debug = format!("{:?}", config);
370 assert!(debug.contains("PoolConfig"));
371 assert!(debug.contains("max_connections"));
372 }
373
374 #[test]
376 fn test_pooled_connection_new() {
377 let conn = PooledConnection::new(42u32);
378 assert_eq!(*conn.get(), 42);
379 }
380
381 #[test]
382 fn test_pooled_connection_get() {
383 let conn = PooledConnection::new("test".to_string());
384 assert_eq!(*conn.get(), "test");
385 }
386
387 #[test]
388 fn test_pooled_connection_get_mut() {
389 let mut conn = PooledConnection::new(vec![1, 2, 3]);
390 conn.get_mut().push(4);
391 assert_eq!(*conn.get(), vec![1, 2, 3, 4]);
392 }
393
394 #[test]
395 fn test_pooled_connection_is_stale() {
396 let conn = PooledConnection::new(42u32);
397 assert!(!conn.is_stale(Duration::from_secs(1)));
399 assert!(conn.is_stale(Duration::from_nanos(0)));
401 }
402
403 #[test]
404 fn test_pooled_connection_age() {
405 let conn = PooledConnection::new(42u32);
406 let age = conn.age();
407 assert!(age < Duration::from_secs(1));
409 }
410
411 #[test]
413 fn test_pool_metrics_default() {
414 let metrics = PoolMetrics::default();
415 assert_eq!(metrics.active_connections, 0);
416 assert_eq!(metrics.idle_connections, 0);
417 assert_eq!(metrics.total_acquired, 0);
418 assert_eq!(metrics.total_released, 0);
419 assert_eq!(metrics.total_created, 0);
420 assert_eq!(metrics.total_closed, 0);
421 assert_eq!(metrics.acquire_timeouts, 0);
422 assert_eq!(metrics.health_check_failures, 0);
423 }
424
425 #[test]
426 fn test_pool_metrics_clone() {
427 let mut metrics = PoolMetrics::default();
428 metrics.total_acquired = 10;
429 metrics.active_connections = 5;
430 let cloned = metrics.clone();
431 assert_eq!(cloned.total_acquired, 10);
432 assert_eq!(cloned.active_connections, 5);
433 }
434
435 #[test]
436 fn test_pool_metrics_debug() {
437 let metrics = PoolMetrics::default();
438 let debug = format!("{:?}", metrics);
439 assert!(debug.contains("PoolMetrics"));
440 assert!(debug.contains("active_connections"));
441 }
442
443 #[test]
445 fn test_pool_error_timeout() {
446 let error = PoolError::Timeout;
447 assert!(error.to_string().contains("timeout"));
448 }
449
450 #[test]
451 fn test_pool_error_closed() {
452 let error = PoolError::Closed;
453 assert!(error.to_string().contains("closed"));
454 }
455
456 #[test]
457 fn test_pool_error_create_error() {
458 let error = PoolError::CreateError("connection failed".to_string());
459 let msg = error.to_string();
460 assert!(msg.contains("create connection"));
461 assert!(msg.contains("connection failed"));
462 }
463
464 #[test]
465 fn test_pool_error_connection_error() {
466 let error = PoolError::ConnectionError("network issue".to_string());
467 let msg = error.to_string();
468 assert!(msg.contains("Connection error"));
469 assert!(msg.contains("network issue"));
470 }
471
472 #[test]
473 fn test_pool_error_debug() {
474 let error = PoolError::Timeout;
475 let debug = format!("{:?}", error);
476 assert!(debug.contains("Timeout"));
477 }
478
479 #[tokio::test]
481 async fn test_connection_pool() {
482 let config = PoolConfig {
483 max_connections: 5,
484 min_idle: 2,
485 ..Default::default()
486 };
487
488 let pool = ConnectionPool::<u32>::new(config);
489
490 let conn1 = pool.acquire(|| async { Ok(42) }).await.unwrap();
492
493 assert_eq!(*conn1.get(), 42);
494
495 pool.release(conn1).await;
497
498 let metrics = pool.metrics().await;
500 assert_eq!(metrics.total_created, 1);
501 assert_eq!(metrics.total_acquired, 1);
502 assert_eq!(metrics.total_released, 1);
503 }
504
505 #[tokio::test]
506 async fn test_connection_pool_new() {
507 let config = PoolConfig {
508 max_connections: 10,
509 min_idle: 2,
510 ..Default::default()
511 };
512 let pool = ConnectionPool::<u32>::new(config);
513
514 assert_eq!(pool.size().await, 0);
516 }
517
518 #[tokio::test]
519 async fn test_connection_pool_acquire_creates_connection() {
520 let config = PoolConfig::default();
521 let pool = ConnectionPool::<String>::new(config);
522
523 let conn = pool.acquire(|| async { Ok("test-connection".to_string()) }).await.unwrap();
524
525 assert_eq!(*conn.get(), "test-connection");
526
527 let metrics = pool.metrics().await;
528 assert_eq!(metrics.total_created, 1);
529 assert_eq!(metrics.total_acquired, 1);
530 }
531
532 #[tokio::test]
533 async fn test_connection_pool_reuses_connection() {
534 let config = PoolConfig {
535 max_connections: 5,
536 min_idle: 1,
537 ..Default::default()
538 };
539 let pool = ConnectionPool::<u32>::new(config);
540 let create_count = Arc::new(AtomicUsize::new(0));
541
542 let create_count_clone = create_count.clone();
544 let conn1 = pool
545 .acquire(move || {
546 let count = create_count_clone.clone();
547 async move {
548 count.fetch_add(1, Ordering::SeqCst);
549 Ok(42u32)
550 }
551 })
552 .await
553 .unwrap();
554
555 pool.release(conn1).await;
557
558 let create_count_clone = create_count.clone();
560 let conn2 = pool
561 .acquire(move || {
562 let count = create_count_clone.clone();
563 async move {
564 count.fetch_add(1, Ordering::SeqCst);
565 Ok(100u32)
566 }
567 })
568 .await
569 .unwrap();
570
571 assert_eq!(*conn2.get(), 42);
573 assert_eq!(create_count.load(Ordering::SeqCst), 1); let metrics = pool.metrics().await;
576 assert_eq!(metrics.total_created, 1);
577 assert_eq!(metrics.total_acquired, 2);
578 }
579
580 #[tokio::test]
581 async fn test_connection_pool_release() {
582 let config = PoolConfig::default();
583 let pool = ConnectionPool::<u32>::new(config);
584
585 let conn = pool.acquire(|| async { Ok(42) }).await.unwrap();
586 assert_eq!(pool.size().await, 0); pool.release(conn).await;
589 assert_eq!(pool.size().await, 1); let metrics = pool.metrics().await;
592 assert_eq!(metrics.total_released, 1);
593 assert_eq!(metrics.idle_connections, 1);
594 }
595
596 #[tokio::test]
597 async fn test_connection_pool_metrics() {
598 let config = PoolConfig::default();
599 let pool = ConnectionPool::<u32>::new(config);
600
601 let metrics = pool.metrics().await;
603 assert_eq!(metrics.total_created, 0);
604
605 let conn = pool.acquire(|| async { Ok(1) }).await.unwrap();
607 pool.release(conn).await;
608
609 let metrics = pool.metrics().await;
610 assert_eq!(metrics.total_created, 1);
611 assert_eq!(metrics.total_acquired, 1);
612 assert_eq!(metrics.total_released, 1);
613 }
614
615 #[tokio::test]
616 async fn test_connection_pool_size() {
617 let config = PoolConfig::default();
618 let pool = ConnectionPool::<u32>::new(config);
619
620 assert_eq!(pool.size().await, 0);
621
622 let conn1 = pool.acquire(|| async { Ok(1) }).await.unwrap();
623 let conn2 = pool.acquire(|| async { Ok(2) }).await.unwrap();
624
625 assert_eq!(pool.size().await, 0);
627
628 pool.release(conn1).await;
629 assert_eq!(pool.size().await, 1);
630
631 pool.release(conn2).await;
632 assert_eq!(pool.size().await, 2);
633 }
634
635 #[tokio::test]
636 async fn test_connection_pool_multiple_concurrent_acquires() {
637 let config = PoolConfig {
638 max_connections: 10,
639 ..Default::default()
640 };
641 let pool = Arc::new(ConnectionPool::<u32>::new(config));
642
643 let mut handles = vec![];
644 for i in 0..5 {
645 let pool_clone = pool.clone();
646 let handle = tokio::spawn(async move {
647 let conn = pool_clone.acquire(move || async move { Ok(i as u32) }).await.unwrap();
648 tokio::time::sleep(Duration::from_millis(10)).await;
650 pool_clone.release(conn).await;
651 });
652 handles.push(handle);
653 }
654
655 for handle in handles {
656 handle.await.unwrap();
657 }
658
659 let metrics = pool.metrics().await;
660 assert_eq!(metrics.total_acquired, 5);
661 assert_eq!(metrics.total_released, 5);
662 }
663
664 #[tokio::test]
665 async fn test_connection_pool_acquire_error() {
666 let config = PoolConfig::default();
667 let pool = ConnectionPool::<u32>::new(config);
668
669 let result = pool
670 .acquire(|| async { Err(PoolError::CreateError("test error".to_string())) })
671 .await;
672
673 assert!(result.is_err());
674 if let Err(PoolError::CreateError(msg)) = result {
675 assert_eq!(msg, "test error");
676 }
677 }
678
679 #[tokio::test]
680 async fn test_connection_pool_health_check() {
681 let config = PoolConfig {
682 max_connections: 5,
683 min_idle: 0,
684 health_check_enabled: true,
685 ..Default::default()
686 };
687 let pool = ConnectionPool::<u32>::new(config);
688
689 let conn1 = pool.acquire(|| async { Ok(1) }).await.unwrap();
691 let conn2 = pool.acquire(|| async { Ok(2) }).await.unwrap();
692 pool.release(conn1).await;
693 pool.release(conn2).await;
694
695 pool.health_check(|_| async { true }).await;
697
698 assert_eq!(pool.size().await, 2);
699
700 pool.health_check(|_| async { false }).await;
702
703 assert_eq!(pool.size().await, 0);
704
705 let metrics = pool.metrics().await;
706 assert_eq!(metrics.health_check_failures, 2);
707 }
708
709 #[tokio::test]
710 async fn test_connection_pool_health_check_disabled() {
711 let config = PoolConfig {
712 health_check_enabled: false,
713 ..Default::default()
714 };
715 let pool = ConnectionPool::<u32>::new(config);
716
717 let conn = pool.acquire(|| async { Ok(1) }).await.unwrap();
718 pool.release(conn).await;
719
720 pool.health_check(|_| async { false }).await;
722
723 assert_eq!(pool.size().await, 1);
725 }
726
727 #[tokio::test]
728 async fn test_connection_pool_maintain_idle() {
729 let config = PoolConfig {
730 max_connections: 10,
731 min_idle: 3,
732 ..Default::default()
733 };
734 let pool = ConnectionPool::<u32>::new(config);
735
736 assert_eq!(pool.size().await, 0);
738
739 pool.maintain_idle(|| async { Ok(42u32) }).await;
741
742 assert_eq!(pool.size().await, 3);
743
744 let metrics = pool.metrics().await;
745 assert_eq!(metrics.total_created, 3);
746 assert_eq!(metrics.idle_connections, 3);
747 }
748
749 #[tokio::test]
750 async fn test_connection_pool_maintain_idle_already_sufficient() {
751 let config = PoolConfig {
752 max_connections: 10,
753 min_idle: 2,
754 ..Default::default()
755 };
756 let pool = ConnectionPool::<u32>::new(config);
757
758 let conn1 = pool.acquire(|| async { Ok(1) }).await.unwrap();
760 let conn2 = pool.acquire(|| async { Ok(2) }).await.unwrap();
761 let conn3 = pool.acquire(|| async { Ok(3) }).await.unwrap();
762 pool.release(conn1).await;
763 pool.release(conn2).await;
764 pool.release(conn3).await;
765
766 let initial_created = pool.metrics().await.total_created;
767
768 pool.maintain_idle(|| async { Ok(100u32) }).await;
770
771 let final_created = pool.metrics().await.total_created;
772 assert_eq!(initial_created, final_created);
773 }
774
775 #[tokio::test]
776 async fn test_connection_pool_maintain_idle_error() {
777 let config = PoolConfig {
778 max_connections: 10,
779 min_idle: 3,
780 ..Default::default()
781 };
782 let pool = ConnectionPool::<u32>::new(config);
783
784 pool.maintain_idle(|| async { Err(PoolError::CreateError("test".to_string())) })
786 .await;
787
788 assert_eq!(pool.size().await, 0);
790 }
791
792 #[tokio::test]
794 async fn test_http_client_pool_new() {
795 let config = PoolConfig::default();
796 let pool = HttpClientPool::new_http(config);
797 assert_eq!(pool.size().await, 0);
798 }
799
800 #[tokio::test]
801 async fn test_http_client_pool_acquire() {
802 let config = PoolConfig::default();
803 let pool = HttpClientPool::new_http(config);
804
805 let result = pool.acquire_client().await;
806 assert!(result.is_ok());
807
808 let conn = result.unwrap();
809 let _client: &reqwest::Client = conn.get();
811 }
812
813 #[tokio::test]
815 async fn test_connection_pool_stale_connection_not_returned() {
816 let config = PoolConfig {
817 max_connections: 5,
818 min_idle: 0, max_idle_time: Duration::from_millis(1), ..Default::default()
821 };
822 let pool = ConnectionPool::<u32>::new(config);
823
824 let conn = pool.acquire(|| async { Ok(42) }).await.unwrap();
825
826 tokio::time::sleep(Duration::from_millis(10)).await;
828
829 pool.release(conn).await;
831
832 let metrics = pool.metrics().await;
834 assert_eq!(metrics.total_closed, 1);
835 }
836
837 #[tokio::test]
838 async fn test_connection_pool_with_complex_type() {
839 #[derive(Debug, Clone)]
840 struct ComplexConnection {
841 id: u32,
842 data: Vec<String>,
843 }
844
845 let config = PoolConfig::default();
846 let pool = ConnectionPool::<ComplexConnection>::new(config);
847
848 let conn = pool
849 .acquire(|| async {
850 Ok(ComplexConnection {
851 id: 123,
852 data: vec!["test".to_string()],
853 })
854 })
855 .await
856 .unwrap();
857
858 assert_eq!(conn.get().id, 123);
859 assert_eq!(conn.get().data, vec!["test".to_string()]);
860 }
861
862 #[tokio::test]
863 async fn test_pooled_connection_updates_last_used() {
864 let mut conn = PooledConnection::new(42u32);
865 let initial_time = conn.last_used;
866
867 tokio::time::sleep(Duration::from_millis(1)).await;
869
870 let _ = conn.get_mut();
872
873 assert!(conn.last_used > initial_time);
874 }
875
876 #[tokio::test]
877 async fn test_connection_pool_partial_health_check() {
878 let config = PoolConfig {
879 max_connections: 10,
880 min_idle: 0,
881 health_check_enabled: true,
882 ..Default::default()
883 };
884 let pool = ConnectionPool::<u32>::new(config);
885
886 let conn1 = pool.acquire(|| async { Ok(1) }).await.unwrap();
888 let conn2 = pool.acquire(|| async { Ok(2) }).await.unwrap();
889 let conn3 = pool.acquire(|| async { Ok(3) }).await.unwrap();
890 pool.release(conn1).await;
891 pool.release(conn2).await;
892 pool.release(conn3).await;
893
894 pool.health_check(|val| {
896 let v = *val;
897 async move { v % 2 != 0 }
898 })
899 .await;
900
901 assert_eq!(pool.size().await, 2); let metrics = pool.metrics().await;
905 assert_eq!(metrics.health_check_failures, 1); }
907}