1use deadpool::managed::{self, Manager, Metrics, RecycleError, RecycleResult};
35use oracle_rs::{Config, Connection, Error};
36use std::time::Duration;
37
38pub struct OracleConnectionManager {
43 config: Config,
44}
45
46impl OracleConnectionManager {
47 pub fn new(config: Config) -> Self {
49 Self { config }
50 }
51}
52
53impl Manager for OracleConnectionManager {
54 type Type = Connection;
55 type Error = Error;
56
57 async fn create(&self) -> Result<Connection, Error> {
58 Connection::connect_with_config(self.config.clone()).await
59 }
60
61 async fn recycle(
62 &self,
63 conn: &mut Connection,
64 _metrics: &Metrics,
65 ) -> RecycleResult<Error> {
66 if conn.is_closed() {
68 return Err(RecycleError::message("connection closed"));
69 }
70
71 conn.rollback().await.ok();
73
74 conn.ping().await.map_err(RecycleError::Backend)?;
76
77 Ok(())
78 }
79}
80
81pub type Pool = managed::Pool<OracleConnectionManager>;
83
84pub type Object = managed::Object<OracleConnectionManager>;
88
89pub struct PoolBuilder {
106 config: Config,
107 max_size: usize,
108 wait_timeout: Option<Duration>,
109 create_timeout: Option<Duration>,
110 recycle_timeout: Option<Duration>,
111}
112
113impl PoolBuilder {
114 pub fn new(config: Config) -> Self {
116 Self {
117 config,
118 max_size: num_cpus() * 4,
119 wait_timeout: Some(Duration::from_secs(30)),
120 create_timeout: Some(Duration::from_secs(30)),
121 recycle_timeout: Some(Duration::from_secs(5)),
122 }
123 }
124
125 pub fn max_size(mut self, size: usize) -> Self {
129 self.max_size = size;
130 self
131 }
132
133 pub fn wait_timeout(mut self, timeout: Option<Duration>) -> Self {
138 self.wait_timeout = timeout;
139 self
140 }
141
142 pub fn create_timeout(mut self, timeout: Option<Duration>) -> Self {
146 self.create_timeout = timeout;
147 self
148 }
149
150 pub fn recycle_timeout(mut self, timeout: Option<Duration>) -> Self {
154 self.recycle_timeout = timeout;
155 self
156 }
157
158 pub fn build(self) -> Result<Pool, BuildError> {
163 let manager = OracleConnectionManager::new(self.config);
164
165 let builder = managed::Pool::builder(manager)
166 .max_size(self.max_size)
167 .runtime(deadpool::Runtime::Tokio1)
168 .timeouts(managed::Timeouts {
169 wait: self.wait_timeout,
170 create: self.create_timeout,
171 recycle: self.recycle_timeout,
172 });
173
174 builder.build().map_err(BuildError)
175 }
176}
177
178#[derive(Debug)]
180pub struct BuildError(managed::BuildError);
181
182impl std::fmt::Display for BuildError {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 write!(f, "failed to build connection pool: {}", self.0)
185 }
186}
187
188impl std::error::Error for BuildError {
189 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
190 Some(&self.0)
191 }
192}
193
194pub type PoolError = managed::PoolError<Error>;
196
197fn num_cpus() -> usize {
199 std::thread::available_parallelism()
200 .map(|p| p.get())
201 .unwrap_or(4)
202}
203
204pub trait ConfigExt {
206 fn into_pool(self) -> Result<Pool, BuildError>;
208
209 fn into_pool_with_size(self, max_size: usize) -> Result<Pool, BuildError>;
211}
212
213impl ConfigExt for Config {
214 fn into_pool(self) -> Result<Pool, BuildError> {
215 PoolBuilder::new(self).build()
216 }
217
218 fn into_pool_with_size(self, max_size: usize) -> Result<Pool, BuildError> {
219 PoolBuilder::new(self).max_size(max_size).build()
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn test_pool_builder_defaults() {
229 let config = Config::new("localhost", 1521, "FREEPDB1", "test", "test");
230 let builder = PoolBuilder::new(config);
231
232 assert!(builder.max_size > 0);
233 assert!(builder.wait_timeout.is_some());
234 assert!(builder.create_timeout.is_some());
235 assert!(builder.recycle_timeout.is_some());
236 }
237
238 #[test]
239 fn test_pool_builder_configuration() {
240 let config = Config::new("localhost", 1521, "FREEPDB1", "test", "test");
241 let builder = PoolBuilder::new(config)
242 .max_size(5)
243 .wait_timeout(Some(Duration::from_secs(10)))
244 .create_timeout(None)
245 .recycle_timeout(Some(Duration::from_secs(2)));
246
247 assert_eq!(builder.max_size, 5);
248 assert_eq!(builder.wait_timeout, Some(Duration::from_secs(10)));
249 assert_eq!(builder.create_timeout, None);
250 assert_eq!(builder.recycle_timeout, Some(Duration::from_secs(2)));
251 }
252
253 #[test]
254 fn test_pool_build_lazy() {
255 let config = Config::new("localhost", 1521, "FREEPDB1", "test", "test");
256 let pool = PoolBuilder::new(config).max_size(10).build();
257
258 assert!(pool.is_ok());
260
261 let pool = pool.unwrap();
262 let status = pool.status();
263
264 assert_eq!(status.size, 0);
266 assert_eq!(status.available, 0);
267 }
268}