use std::time::Duration;
use fraiseql_core::{
db::postgres::{Notification, NotifyConnector, NotifySession},
error::{FraiseQLError, Result},
};
pub(super) const PROBE_TIMEOUT: Duration = Duration::from_secs(5);
pub(super) async fn verified_session(
connector: &NotifyConnector,
channels: &[&str],
probe_channel: &str,
probe: &str,
heard: impl Fn(&Notification) -> bool + Send + Sync,
) -> Result<NotifySession> {
let mut session = connector.connect().await?;
for channel in channels {
session.listen(channel).await?;
}
session.notify(probe_channel, probe).await?;
let received = tokio::time::timeout(PROBE_TIMEOUT, async {
while let Some(notification) = session.recv().await {
if heard(¬ification) {
return true;
}
}
false
})
.await;
match received {
Ok(true) => Ok(session),
Ok(false) => Err(FraiseQLError::ConnectionPool {
message: "the notification connection closed before it heard itself".to_string(),
}),
Err(_) => Err(FraiseQLError::ConnectionPool {
message: format!(
"the notification connection did not receive its own message on \
`{probe_channel}` within {}s; is `database_url` a pooler in transaction mode? \
`LISTEN` needs a session of its own",
PROBE_TIMEOUT.as_secs()
),
}),
}
}