refactor: one channel per consumer

This commit is contained in:
izzy
2025-06-28 18:26:51 +01:00
parent 9d576d2430
commit 083229c7f5
6 changed files with 65 additions and 42 deletions

View File

@@ -108,5 +108,5 @@ pub async fn client_subscriber(
} }
} }
consumer.dispose().await; consumer.dispose_channel().await;
} }

View File

@@ -5,18 +5,21 @@ use std::{
use async_std::stream::StreamExt; use async_std::stream::StreamExt;
use lapin::{ use lapin::{
Channel, Channel, Connection,
options::BasicAckOptions, options::BasicAckOptions,
types::{AMQPValue, FieldArray, FieldTable, LongLongInt}, types::{AMQPValue, FieldArray, FieldTable, LongLongInt},
}; };
use log::info;
use rand::Rng; use rand::Rng;
use revolt_config::config; use revolt_config::{capture_internal_error, config};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use crate::event_stream::get_channel; use crate::event_stream::{create_channel, get_connection};
pub struct Consumer { pub struct Consumer {
channel: Arc<Channel>, #[allow(dead_code)]
conn: Arc<Connection>,
channel: Channel,
tag: String, tag: String,
topics: HashSet<String>, topics: HashSet<String>,
topics_changed: bool, topics_changed: bool,
@@ -27,8 +30,13 @@ pub struct Consumer {
impl Consumer { impl Consumer {
/// Create a new event stream consumer /// Create a new event stream consumer
pub async fn new() -> Consumer { pub async fn new() -> Consumer {
let config = config().await;
let conn = get_connection().await;
let channel = create_channel(&conn, config.rabbit.event_stream).await;
Consumer { Consumer {
channel: get_channel().await, conn,
channel,
tag: rand::rng() tag: rand::rng()
.sample_iter::<char, _>(&rand::distr::StandardUniform) .sample_iter::<char, _>(&rand::distr::StandardUniform)
.take(32) .take(32)
@@ -49,10 +57,13 @@ impl Consumer {
/// Get the current consumer /// Get the current consumer
pub async fn ensure_consumer(&mut self) { pub async fn ensure_consumer(&mut self) {
if self.topics_changed { if self.topics_changed {
self.consumer = None; info!("Topics changed, disposing the consumer.");
self.dispose_consumer().await;
self.topics_changed = false;
} }
if self.consumer.is_none() { if self.consumer.is_none() {
info!("Creating a new consumer, tag={}", self.tag);
let config = config().await; let config = config().await;
// Build arguments for consumer // Build arguments for consumer
@@ -88,27 +99,36 @@ impl Consumer {
} }
} }
/// Close the active consumer /// Close the active consumer if one exists
pub async fn dispose(&mut self) { pub async fn dispose_consumer(&mut self) {
// Close the channel -- don't do this actually if let Some(consumer) = self.consumer.as_ref() {
// capture_internal_error!(self.channel.close(0, "closing channel").await); if consumer.state().is_active() {
if let Err(err) = self
.channel
.basic_cancel(&self.tag, Default::default())
.await
{
eprintln!("Failed to close consumer! {:?}", err);
}
// Close the consumer // is this necessary?
if let Err(err) = self // else {
.channel // Read the consumer to the end
.basic_cancel(&self.tag, Default::default()) // while let Some(delivery) = consumer.next().await {
.await // let delivery = delivery.expect("error in consumer");
{ // delivery.ack(BasicAckOptions::default()).await.expect("ack");
eprintln!("Failed to close consumer! {:?}", err); // }
// }
}
self.consumer = None;
} }
// is this necessary? }
// else {
// Read the consumer to the end /// Close the active channel
// while let Some(delivery) = consumer.next().await { pub async fn dispose_channel(&mut self) {
// let delivery = delivery.expect("error in consumer"); // Close the channel -- don't do this actually
// delivery.ack(BasicAckOptions::default()).await.expect("ack"); capture_internal_error!(self.channel.close(0, "closing channel").await);
// }
// }
} }
/// Get the next item /// Get the next item

View File

@@ -3,5 +3,5 @@ mod pool;
mod publish; mod publish;
pub use consumer::Consumer; pub use consumer::Consumer;
pub use pool::get_channel; pub use pool::{create_channel, get_connection};
pub use publish::publish_event; pub use publish::publish_event;

View File

@@ -1,6 +1,6 @@
use async_std::sync::Mutex; use async_std::sync::Mutex;
use lapin::{ use lapin::{
Channel, Connection,
options::QueueDeclareOptions, options::QueueDeclareOptions,
types::{AMQPValue, FieldTable}, types::{AMQPValue, FieldTable},
}; };
@@ -11,10 +11,10 @@ use std::sync::Arc;
use crate::create_client; use crate::create_client;
/// Get a handle to the event stream /// Get a handle to the event stream
pub async fn get_channel() -> Arc<Channel> { pub async fn get_connection() -> Arc<Connection> {
let config = config().await; let config = config().await;
static CONNECTIONS: Mutex<Vec<Arc<lapin::Channel>>> = Mutex::new(Vec::new()); static CONNECTIONS: Mutex<Vec<Arc<Connection>>> = Mutex::new(Vec::new());
let mut connections = CONNECTIONS.lock().await; let mut connections = CONNECTIONS.lock().await;
connections.retain(|item| { connections.retain(|item| {
@@ -39,22 +39,21 @@ pub async fn get_channel() -> Arc<Channel> {
.collect::<Vec<usize>>() .collect::<Vec<usize>>()
); );
for channel in connections.iter() { for conn in connections.iter() {
if Arc::strong_count(channel) < config.rabbit.event_stream.channels_per_conn { if Arc::strong_count(conn) < config.rabbit.event_stream.channels_per_conn {
return channel.clone(); return conn.clone();
} }
} }
let conn = create_client().await; let conn = Arc::new(create_client().await);
let channel = Arc::new(create_channel(conn, config.rabbit.event_stream).await);
connections.push(channel.clone()); connections.push(conn.clone());
channel conn
} }
/// Create a channel /// Create a channel
async fn create_channel( pub async fn create_channel(
conn: lapin::Connection, conn: &lapin::Connection,
event_stream: RabbitEventStream, event_stream: RabbitEventStream,
) -> lapin::Channel { ) -> lapin::Channel {
let channel = conn.create_channel().await.unwrap(); let channel = conn.create_channel().await.unwrap();

View File

@@ -7,7 +7,7 @@ use lapin::{
use revolt_config::config; use revolt_config::config;
use serde::Serialize; use serde::Serialize;
use crate::event_stream::get_channel; use crate::event_stream::{create_channel, get_connection};
/// Publish an event to the message broker /// Publish an event to the message broker
pub async fn publish_event<T: Serialize>( pub async fn publish_event<T: Serialize>(
@@ -22,7 +22,9 @@ pub async fn publish_event<T: Serialize>(
AMQPValue::LongString(channel.into()), AMQPValue::LongString(channel.into()),
); );
get_channel() let conn = get_connection().await;
create_channel(&conn, config.rabbit.event_stream.clone())
.await .await
.basic_publish( .basic_publish(
&config.rabbit.event_stream.exchange, &config.rabbit.event_stream.exchange,

View File

@@ -40,7 +40,9 @@ impl TestHarness {
.collect(); .collect();
static QUEUE_NAME: &str = "revolt.events"; static QUEUE_NAME: &str = "revolt.events";
let consumer = event_stream::get_channel() let conn = event_stream::get_connection().await;
let consumer = event_stream::create_channel(&conn, config.rabbit.event_stream)
.await .await
.basic_consume(QUEUE_NAME, &tag, Default::default(), Default::default()) .basic_consume(QUEUE_NAME, &tag, Default::default(), Default::default())
.await .await