use crate::driver::QdrantDriver;
use crate::error::{QdrantError, QdrantResult};
use std::sync::Arc;
use tokio::sync::{Mutex, Semaphore};
#[derive(Debug, Clone)]
pub struct PoolConfig {
pub max_connections: usize,
pub host: String,
pub port: u16,
pub tls: bool,
}
impl PoolConfig {
pub fn new(host: impl Into<String>, port: u16) -> Self {
Self {
max_connections: 10,
host: host.into(),
port,
tls: false,
}
}
pub fn tls(mut self, enabled: bool) -> Self {
self.tls = enabled;
self
}
pub fn max_connections(mut self, max: usize) -> Self {
self.max_connections = max;
self
}
pub fn from_qail_config(qail: &qail_core::config::QailConfig) -> Option<Self> {
let qdrant = qail.qdrant.as_ref()?;
Some(Self::from_qail_config_ref(qdrant))
}
pub fn from_qail_config_ref(qdrant: &qail_core::config::QdrantConfig) -> Self {
let use_tls = qdrant.tls.unwrap_or_else(|| qdrant.url.starts_with("https://"));
let (host, port) = if let Some(ref grpc) = qdrant.grpc {
if grpc.contains(':') {
let mut parts = grpc.rsplitn(2, ':');
let port = parts.next().and_then(|s| s.parse().ok()).unwrap_or(6334u16);
let host = parts.next().unwrap_or("localhost").to_string();
(host, port)
} else {
(grpc.clone(), 6334)
}
} else {
let host = qdrant
.url
.trim_start_matches("http://")
.trim_start_matches("https://")
.split(':')
.next()
.unwrap_or("localhost")
.to_string();
(host, 6334)
};
Self {
max_connections: qdrant.max_connections,
host,
port,
tls: use_tls,
}
}
}
impl Default for PoolConfig {
fn default() -> Self {
Self {
max_connections: 10,
host: "localhost".to_string(),
port: 6334,
tls: false,
}
}
}
struct PoolInner {
config: PoolConfig,
idle: Mutex<Vec<QdrantDriver>>,
semaphore: Semaphore,
}
#[derive(Clone)]
pub struct QdrantPool {
inner: Arc<PoolInner>,
}
impl QdrantPool {
pub async fn new(config: PoolConfig) -> QdrantResult<Self> {
let max = config.max_connections;
Ok(Self {
inner: Arc::new(PoolInner {
config,
idle: Mutex::new(Vec::with_capacity(max)),
semaphore: Semaphore::new(max),
}),
})
}
pub async fn get(&self) -> QdrantResult<PooledConnection> {
let permit = self
.inner
.semaphore
.acquire()
.await
.map_err(|e| QdrantError::Connection(format!("Semaphore closed: {}", e)))?;
let driver = {
let mut idle = self.inner.idle.lock().await;
idle.pop()
};
let driver = match driver {
Some(d) => d,
None => {
if self.inner.config.tls {
QdrantDriver::connect_tls(&self.inner.config.host, self.inner.config.port).await?
} else {
QdrantDriver::connect(&self.inner.config.host, self.inner.config.port).await?
}
}
};
permit.forget();
Ok(PooledConnection {
driver: Some(driver),
pool: Arc::clone(&self.inner),
})
}
pub async fn idle_count(&self) -> usize {
self.inner.idle.lock().await.len()
}
pub fn available(&self) -> usize {
self.inner.semaphore.available_permits()
}
pub fn max_connections(&self) -> usize {
self.inner.config.max_connections
}
}
pub struct PooledConnection {
driver: Option<QdrantDriver>,
pool: Arc<PoolInner>,
}
impl std::ops::Deref for PooledConnection {
type Target = QdrantDriver;
fn deref(&self) -> &Self::Target {
self.driver.as_ref().expect("driver taken after drop")
}
}
impl std::ops::DerefMut for PooledConnection {
fn deref_mut(&mut self) -> &mut Self::Target {
self.driver.as_mut().expect("driver taken after drop")
}
}
impl Drop for PooledConnection {
fn drop(&mut self) {
if let Some(driver) = self.driver.take() {
let pool = Arc::clone(&self.pool);
tokio::spawn(async move {
{
let mut idle = pool.idle.lock().await;
if idle.len() < pool.config.max_connections {
idle.push(driver);
}
}
pool.semaphore.add_permits(1);
});
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pool_config_builder() {
let config = PoolConfig::new("localhost", 6334).max_connections(20);
assert_eq!(config.max_connections, 20);
assert_eq!(config.host, "localhost");
assert_eq!(config.port, 6334);
assert!(!config.tls);
let tls_config = PoolConfig::new("cloud.qdrant.io", 6334).tls(true);
assert!(tls_config.tls);
}
#[test]
fn test_pool_config_default() {
let config = PoolConfig::default();
assert_eq!(config.max_connections, 10);
assert_eq!(config.host, "localhost");
assert_eq!(config.port, 6334);
}
}