Hey there,
We're using this crate for two of our applications, because it is very robust and the API is very nice to work with. Because we recently had some internal networking issues, we've noticed that a MQTT-client's connect procedure has a synchronous DNS resolution. We're not too familiar with the underlying networking crates, but it seems that the synchronous std::sys::net::lookup_host_string is being used in this crate's connect method.
The asynchronous connect method
#[instrument(skip(self))]
pub async fn connect(&self, address: &str) -> Result<()> {
let client_id = self.client_id().await;
tracing::trace!(client_id = %client_id, address = %address, "MQTT CLIENT - connect() method called");
tracing::info!(client_id = %client_id, address = %address, "Initiating MQTT connection");
let result = {
let connection_guard = self.connection_mutex.lock().await;
let options = self.inner.read().await.options.clone();
let result = self.connect_with_options_internal(address, options).await;
drop(connection_guard);
result
};
match result {
Ok(connect_result) => {
tracing::info!(client_id = %client_id, session_present = %connect_result.session_present, "Successfully connected to MQTT broker");
Ok(())
}
Err(e) => {
tracing::error!(client_id = %client_id, error = %e, "Failed to connect to MQTT broker");
Err(e)
}
}
}
has a synchronous DNS resolution procedure in let result = self.connect_with_options_internal(address, options).await;, which can be tracked to to_socket_addrs() in
pub(crate) fn resolve_addresses(host: &str, port: u16) -> Result<Vec<std::net::SocketAddr>> {
let addr_str = format!("{host}:{port}");
tracing::debug!(addr_str = %addr_str, "🌐 DNS RESOLUTION - Starting address resolution");
let addrs: Vec<_> = addr_str
.to_socket_addrs()
.map_err(|e| {
tracing::error!(addr_str = %addr_str, error = %e, "🌐 DNS RESOLUTION - Failed to resolve address");
MqttError::ConnectionError(format!("Failed to resolve address: {e}"))
})?
.collect();
tracing::debug!(addr_str = %addr_str, resolved_count = addrs.len(), "🌐 DNS RESOLUTION - Address resolved successfully");
if addrs.is_empty() {
return Err(MqttError::ConnectionError(
"No valid address found".to_string(),
));
}
Ok(addrs)
}
where the synchronous std::sys::net::lookup_host_string method is being used.
It seems as if for example a spawn_blocking block or the tokio::net::lookup_host methods would be valid asynchronous alternatives for the DNS loopup procedure. We could just add the manual DNS lookup before the crate's connect method or wrap the connect method in spawn_blocking, but that seems unintended. Therefore, we were wondering: Is the use of a synchronous DNS lookup method intended in the crate's connect method?
Thanks for all the work on this crate!
Best,
Tom
Hey there,
We're using this crate for two of our applications, because it is very robust and the API is very nice to work with. Because we recently had some internal networking issues, we've noticed that a MQTT-client's connect procedure has a synchronous DNS resolution. We're not too familiar with the underlying networking crates, but it seems that the synchronous
std::sys::net::lookup_host_stringis being used in this crate'sconnectmethod.The asynchronous
connectmethodhas a synchronous DNS resolution procedure in
let result = self.connect_with_options_internal(address, options).await;, which can be tracked toto_socket_addrs()inwhere the synchronous
std::sys::net::lookup_host_stringmethod is being used.It seems as if for example a
spawn_blockingblock or thetokio::net::lookup_hostmethods would be valid asynchronous alternatives for the DNS loopup procedure. We could just add the manual DNS lookup before the crate'sconnectmethod or wrap theconnectmethod inspawn_blocking, but that seems unintended. Therefore, we were wondering: Is the use of a synchronous DNS lookup method intended in the crate'sconnectmethod?Thanks for all the work on this crate!
Best,
Tom