sz_orm_sqlx/
unified_pool.rs1use std::sync::Arc;
27use sz_orm_core::{
28 ConnectionFactory, DbError, Dialect, Pool, PoolConfig, PoolConfigBuilder, PoolError,
29 PoolStatus, PooledConnection,
30};
31
32use crate::any::{
33 MySqlPoolHandle, PgPoolHandle, SqlitePoolHandle, SqlxMySqlConnectionFactory,
34 SqlxPgConnectionFactory, SqlxSqliteConnectionFactory,
35};
36use crate::any_driver::AnyBackend;
37
38#[cfg(feature = "oracle")]
39use sz_orm_oracle::{OracleConnectionFactory, OraclePoolHandle};
40
41#[cfg(feature = "mssql")]
42use sz_orm_mssql::{MssqlConnectionFactory, MssqlPoolHandle};
43
44pub struct UnifiedPool {
49 backend: AnyBackend,
50 pool: Pool,
51}
52
53impl UnifiedPool {
54 pub async fn connect(dsn: &str) -> Result<Self, DbError> {
58 let config = PoolConfigBuilder::new()
59 .build()
60 .map_err(DbError::PoolError)?;
61 Self::connect_with_config(dsn, config).await
62 }
63
64 pub async fn connect_with_config(dsn: &str, config: PoolConfig) -> Result<Self, DbError> {
66 let backend = AnyBackend::from_dsn(dsn)?;
67 let factory: Arc<dyn ConnectionFactory> = match backend {
68 AnyBackend::MySql => {
69 let handle = Arc::new(MySqlPoolHandle::connect(dsn).await?);
70 Arc::new(SqlxMySqlConnectionFactory::new(handle))
71 }
72 AnyBackend::Postgres => {
73 let handle = Arc::new(PgPoolHandle::connect(dsn).await?);
74 Arc::new(SqlxPgConnectionFactory::new(handle))
75 }
76 AnyBackend::Sqlite => {
77 let handle = Arc::new(SqlitePoolHandle::connect(dsn).await?);
78 Arc::new(SqlxSqliteConnectionFactory::new(handle))
79 }
80 AnyBackend::Oracle => {
81 #[cfg(feature = "oracle")]
82 {
83 let (username, password, connect_string) =
84 crate::any_driver::parse_oracle_dsn(dsn)?;
85 let handle = Arc::new(OraclePoolHandle::connect(
86 &username,
87 &password,
88 &connect_string,
89 )?);
90 Arc::new(OracleConnectionFactory::new(handle))
91 }
92 #[cfg(not(feature = "oracle"))]
93 {
94 return Err(DbError::ConnectionRefused(
95 "Oracle 后端未启用,请在 Cargo.toml 中添加 features = [\"oracle\"]"
96 .to_string(),
97 ));
98 }
99 }
100 AnyBackend::Mssql => {
101 #[cfg(feature = "mssql")]
102 {
103 let ado_string = crate::any_driver::parse_mssql_dsn(dsn)?;
104 let handle = Arc::new(MssqlPoolHandle::connect(&ado_string).await?);
105 Arc::new(MssqlConnectionFactory::new(handle))
106 }
107 #[cfg(not(feature = "mssql"))]
108 {
109 return Err(DbError::ConnectionRefused(
110 "MSSQL 后端未启用,请在 Cargo.toml 中添加 features = [\"mssql\"]"
111 .to_string(),
112 ));
113 }
114 }
115 };
116 let pool = Pool::new(config, factory).map_err(DbError::PoolError)?;
117 Ok(Self { backend, pool })
118 }
119
120 pub fn from_pool(pool: Pool, backend: AnyBackend) -> Self {
127 Self { backend, pool }
128 }
129
130 #[inline]
132 pub fn backend(&self) -> AnyBackend {
133 self.backend
134 }
135
136 #[inline]
138 pub fn dialect(&self) -> Box<dyn Dialect> {
139 self.backend.dialect()
140 }
141
142 #[inline]
144 pub async fn acquire(&self) -> Result<PooledConnection, PoolError> {
145 self.pool.acquire().await
146 }
147
148 #[inline]
150 pub fn resize(&self, new_max: usize) {
151 self.pool.resize(new_max);
152 }
153
154 #[inline]
156 pub async fn close_all(&self) {
157 self.pool.close_all().await;
158 }
159
160 #[inline]
162 pub async fn status(&self) -> PoolStatus {
163 self.pool.status().await
164 }
165
166 #[inline]
168 pub async fn prewarm(&self) {
169 self.pool.prewarm().await;
170 }
171
172 #[cfg(feature = "auto-prewarm")]
174 pub async fn progressive_prewarm(
175 &self,
176 batch_size: u32,
177 interval: std::time::Duration,
178 total_timeout: std::time::Duration,
179 progress: &sz_orm_core::prewarm::PrewarmProgress,
180 ) {
181 self.pool
182 .progressive_prewarm(batch_size, interval, total_timeout, progress)
183 .await;
184 }
185}
186
187#[cfg(feature = "auto-prewarm")]
189pub struct MultiPoolRegistry {
190 pools: Vec<(String, UnifiedPool)>,
191}
192
193#[cfg(feature = "auto-prewarm")]
194impl MultiPoolRegistry {
195 pub fn new() -> Self {
196 Self { pools: Vec::new() }
197 }
198
199 pub fn register(&mut self, name: impl Into<String>, pool: UnifiedPool) {
200 self.pools.push((name.into(), pool));
201 }
202
203 pub async fn unified_prewarm_all(&self) -> sz_orm_core::prewarm::PrewarmSummary {
205 use std::time::Instant;
206 use sz_orm_core::prewarm::{BackendPrewarmResult, PrewarmSummary};
207
208 let mut summary = PrewarmSummary::new();
209 for (name, pool) in &self.pools {
210 let start = Instant::now();
211 let min_idle = pool.pool.config().min_idle;
212 let progress = sz_orm_core::prewarm::PrewarmProgress::new(min_idle);
213 pool.prewarm().await;
214 let snap = progress.snapshot();
215 summary.add(BackendPrewarmResult {
216 backend: name.clone(),
217 warmed: snap.warmed,
218 failed: snap.failed,
219 elapsed: start.elapsed(),
220 errors: vec![],
221 });
222 }
223 summary
224 }
225}
226
227#[cfg(feature = "auto-prewarm")]
228impl Default for MultiPoolRegistry {
229 fn default() -> Self {
230 Self::new()
231 }
232}
233
234impl std::fmt::Debug for UnifiedPool {
235 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236 f.debug_struct("UnifiedPool")
237 .field("backend", &self.backend)
238 .finish_non_exhaustive()
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[tokio::test]
247 async fn test_unified_pool_sqlite_connect() {
248 let pool = UnifiedPool::connect("sqlite::memory:").await.unwrap();
249 assert_eq!(pool.backend(), AnyBackend::Sqlite);
250
251 let mut conn = pool.acquire().await.unwrap();
252 conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY)")
253 .await
254 .unwrap();
255 conn.execute("INSERT INTO t (id) VALUES (1)").await.unwrap();
256 let rows = conn.query("SELECT * FROM t").await.unwrap();
257 assert_eq!(rows.len(), 1);
258 }
259
260 #[tokio::test]
261 async fn test_unified_pool_dialect() {
262 let pool = UnifiedPool::connect("sqlite::memory:").await.unwrap();
263 let d = pool.dialect();
264 assert_eq!(d.db_type(), sz_orm_core::DbType::Sqlite);
265 }
266
267 #[tokio::test]
268 async fn test_unified_pool_from_pool() {
269 let handle = Arc::new(SqlitePoolHandle::connect("sqlite::memory:").await.unwrap());
270 let factory = Arc::new(SqlxSqliteConnectionFactory::new(handle));
271 let config = PoolConfigBuilder::new().build().unwrap();
272 let pool = Pool::new(config, factory).unwrap();
273
274 let unified = UnifiedPool::from_pool(pool, AnyBackend::Sqlite);
275 assert_eq!(unified.backend(), AnyBackend::Sqlite);
276
277 let mut conn = unified.acquire().await.unwrap();
278 conn.execute("SELECT 1").await.unwrap();
279 }
280
281 #[tokio::test]
282 async fn test_unified_pool_resize_and_close() {
283 let pool = UnifiedPool::connect("sqlite::memory:").await.unwrap();
284 pool.resize(20);
285 let status = pool.status().await;
286 assert_eq!(status.max, 20);
287 pool.close_all().await;
288 }
289
290 #[tokio::test]
291 async fn test_unified_pool_invalid_dsn() {
292 let result = UnifiedPool::connect("invalid://dsn").await;
293 assert!(result.is_err());
294 }
295}