fix(mongodb): support replica set connection URL parsing and timeout
Frontend: add regex-based MongoDB URL parser to handle multi-host URIs that the WHATWG URL parser rejects. Backend: separate server_selection_timeout from connect_timeout for multi-host URIs to prevent topology discovery from being cancelled by tokio timeout.
This commit is contained in:
1 parent
0ce6fde347
commit
80895fe273
3 files changed
+147
-13
No files matched your search
@@ -17,15 +17,35 @@ pub struct MongoDocumentResult {
|
||||
}
|
||||
|
||||
pub async fn connect(url: &str, timeout: Duration) -> Result<Client, String> {
|
||||
with_connection_timeout("MongoDB", timeout, async {
|
||||
let is_multi_host = is_multi_host_mongo_uri(url);
|
||||
let parse_timeout = if is_multi_host { std::cmp::max(timeout * 2, Duration::from_secs(10)) } else { timeout };
|
||||
|
||||
with_connection_timeout("MongoDB", parse_timeout, async {
|
||||
let mut options = ClientOptions::parse(url).await.map_err(|e| format!("MongoDB connection failed: {e}"))?;
|
||||
options.connect_timeout = Some(timeout);
|
||||
options.server_selection_timeout = Some(timeout);
|
||||
options.server_selection_timeout =
|
||||
if is_multi_host { Some(std::cmp::max(timeout * 2, Duration::from_secs(10))) } else { Some(timeout) };
|
||||
Client::with_options(options).map_err(|e| format!("MongoDB connection failed: {e}"))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
fn is_multi_host_mongo_uri(url: &str) -> bool {
|
||||
let rest = match url.strip_prefix("mongodb://").or_else(|| url.strip_prefix("mongodb+srv://")) {
|
||||
Some(r) => r,
|
||||
None => return false,
|
||||
};
|
||||
let authority = match rest.split('/').next() {
|
||||
Some(a) => a,
|
||||
None => return false,
|
||||
};
|
||||
let host_section = match authority.rfind('@') {
|
||||
Some(idx) => &authority[idx + 1..],
|
||||
None => authority,
|
||||
};
|
||||
host_section.contains(',')
|
||||
}
|
||||
|
||||
pub async fn test_connection(client: &Client, _timeout: Duration, database: Option<&str>) -> Result<(), String> {
|
||||
let database = database.map(str::trim).filter(|value| !value.is_empty()).unwrap_or("admin");
|
||||
client
|
||||
|
||||
Reference in new issue
Block a user