Skip to content

Commit 57d7303

Browse files
committed
editoast: use pooling for amqp
Signed-off-by: ElysaSrc <[email protected]>
1 parent 665e2b6 commit 57d7303

File tree

6 files changed

+122
-74
lines changed

6 files changed

+122
-74
lines changed

editoast/Cargo.lock

+26
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

editoast/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ chrono.workspace = true
9696
clap = { version = "4.5.19", features = ["derive", "env"] }
9797
colored = "2.1.0"
9898
dashmap = "6.1.0"
99+
deadpool-lapin = "0.12.1"
99100
derivative.workspace = true
100101
diesel.workspace = true
101102
diesel-async = { workspace = true }

editoast/openapi.yaml

+40
Original file line numberDiff line numberDiff line change
@@ -4068,6 +4068,8 @@ components:
40684068
- $ref: '#/components/schemas/EditoastEditoastUrlErrorInvalidUrl'
40694069
- $ref: '#/components/schemas/EditoastElectricalProfilesErrorNotFound'
40704070
- $ref: '#/components/schemas/EditoastErrorConnectionDoesNotExist'
4071+
- $ref: '#/components/schemas/EditoastErrorCreatePoolLapin'
4072+
- $ref: '#/components/schemas/EditoastErrorDeadpoolLapin'
40714073
- $ref: '#/components/schemas/EditoastErrorLapin'
40724074
- $ref: '#/components/schemas/EditoastErrorResponseTimeout'
40734075
- $ref: '#/components/schemas/EditoastErrorSerialization'
@@ -4148,6 +4150,44 @@ components:
41484150
type: string
41494151
enum:
41504152
- editoast:coreclient:ConnectionDoesNotExist
4153+
EditoastErrorCreatePoolLapin:
4154+
type: object
4155+
required:
4156+
- type
4157+
- status
4158+
- message
4159+
properties:
4160+
context:
4161+
type: object
4162+
message:
4163+
type: string
4164+
status:
4165+
type: integer
4166+
enum:
4167+
- 500
4168+
type:
4169+
type: string
4170+
enum:
4171+
- editoast:coreclient:CreatePoolLapin
4172+
EditoastErrorDeadpoolLapin:
4173+
type: object
4174+
required:
4175+
- type
4176+
- status
4177+
- message
4178+
properties:
4179+
context:
4180+
type: object
4181+
message:
4182+
type: string
4183+
status:
4184+
type: integer
4185+
enum:
4186+
- 500
4187+
type:
4188+
type: string
4189+
enum:
4190+
- editoast:coreclient:DeadpoolLapin
41514191
EditoastErrorLapin:
41524192
type: object
41534193
required:

editoast/src/core/mq_client.rs

+49-72
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
1+
use deadpool_lapin::{Config, CreatePoolError, Pool, PoolError, Runtime};
12
use editoast_derive::EditoastError;
23
use futures_util::StreamExt;
34
use itertools::Itertools;
45
use lapin::{
56
options::{BasicConsumeOptions, BasicPublishOptions},
67
types::{ByteArray, FieldTable, ShortString},
7-
BasicProperties, Connection, ConnectionProperties,
8+
BasicProperties,
89
};
910
use serde::Serialize;
1011
use serde_json::to_vec;
11-
use std::{fmt::Debug, sync::Arc};
12+
use std::fmt::Debug;
1213
use thiserror::Error;
13-
use tokio::{
14-
sync::RwLock,
15-
time::{timeout, Duration},
16-
};
14+
use tokio::time::{timeout, Duration};
1715

1816
#[derive(Debug, Clone)]
1917
pub struct RabbitMQClient {
20-
connection: Arc<RwLock<Option<Connection>>>,
18+
pub pool: Pool,
2119
exchange: String,
2220
timeout: u64,
2321
hostname: String,
@@ -51,6 +49,12 @@ pub enum Error {
5149
#[error("Connection does not exist")]
5250
#[editoast_error(status = "500")]
5351
ConnectionDoesNotExist,
52+
#[error("Cannot create the pool")]
53+
#[editoast_error(status = "500")]
54+
CreatePoolLapin(CreatePoolError),
55+
#[error("Cannot acquire connection from pool")]
56+
#[editoast_error(status = "500")]
57+
DeadpoolLapin(PoolError),
5458
}
5559

5660
pub struct MQResponse {
@@ -64,61 +68,22 @@ impl RabbitMQClient {
6468
.map(|name| name.to_string_lossy().into_owned())
6569
.unwrap_or_else(|_| "unknown".to_string());
6670

67-
let conn = Arc::new(RwLock::new(None));
68-
69-
tokio::spawn(Self::connection_loop(options.uri, conn.clone()));
71+
let cfg = Config {
72+
url: Some(options.uri),
73+
..Default::default()
74+
};
75+
let pool = cfg
76+
.create_pool(Some(Runtime::Tokio1))
77+
.map_err(Error::CreatePoolLapin)?;
7078

7179
Ok(RabbitMQClient {
72-
connection: conn,
80+
pool,
7381
exchange: format!("{}-req-xchg", options.worker_pool_identifier),
7482
timeout: options.timeout,
7583
hostname,
7684
})
7785
}
7886

79-
async fn connection_ok(connection: &Arc<RwLock<Option<Connection>>>) -> bool {
80-
let guard = connection.as_ref().read().await;
81-
let conn = guard.as_ref();
82-
let status = match conn {
83-
None => return false,
84-
Some(conn) => conn.status().state(),
85-
};
86-
match status {
87-
lapin::ConnectionState::Initial => true,
88-
lapin::ConnectionState::Connecting => true,
89-
lapin::ConnectionState::Connected => true,
90-
lapin::ConnectionState::Closing => true,
91-
lapin::ConnectionState::Closed => false,
92-
lapin::ConnectionState::Error => false,
93-
}
94-
}
95-
96-
async fn connection_loop(uri: String, connection: Arc<RwLock<Option<Connection>>>) {
97-
loop {
98-
if Self::connection_ok(&connection).await {
99-
tokio::time::sleep(Duration::from_secs(2)).await;
100-
continue;
101-
}
102-
103-
tracing::info!("Reconnecting to RabbitMQ");
104-
105-
// Connection should be re-established
106-
let new_connection = Connection::connect(&uri, ConnectionProperties::default()).await;
107-
108-
match new_connection {
109-
Ok(new_connection) => {
110-
*connection.write().await = Some(new_connection);
111-
tracing::info!("Reconnected to RabbitMQ");
112-
}
113-
Err(e) => {
114-
tracing::error!("Error while reconnecting to RabbitMQ: {:?}", e);
115-
}
116-
}
117-
118-
tokio::time::sleep(Duration::from_secs(2)).await;
119-
}
120-
}
121-
12287
#[allow(dead_code)]
12388
pub async fn call<T>(
12489
&self,
@@ -131,14 +96,8 @@ impl RabbitMQClient {
13196
where
13297
T: Serialize,
13398
{
134-
// Get current connection
135-
let connection = self.connection.read().await;
136-
if connection.is_none() {
137-
return Err(Error::ConnectionDoesNotExist);
138-
}
139-
let connection = connection.as_ref().unwrap();
140-
14199
// Create a channel
100+
let connection = self.pool.get().await.map_err(Error::DeadpoolLapin)?;
142101
let channel = connection.create_channel().await.map_err(Error::Lapin)?;
143102

144103
let serialized_payload_vec = to_vec(published_payload).map_err(Error::Serialization)?;
@@ -172,6 +131,12 @@ impl RabbitMQClient {
172131
.await
173132
.map_err(Error::Lapin)?;
174133

134+
// Explicitly close the channel
135+
channel
136+
.close(200, "Normal shutdown")
137+
.await
138+
.map_err(Error::Lapin)?;
139+
175140
Ok(())
176141
}
177142

@@ -186,14 +151,8 @@ impl RabbitMQClient {
186151
where
187152
T: Serialize,
188153
{
189-
// Get current connection
190-
let connection = self.connection.read().await;
191-
if connection.is_none() {
192-
return Err(Error::ConnectionDoesNotExist);
193-
}
194-
let connection = connection.as_ref().unwrap();
195-
196154
// Create a channel
155+
let connection = self.pool.get().await.map_err(Error::DeadpoolLapin)?;
197156
let channel = connection.create_channel().await.map_err(Error::Lapin)?;
198157

199158
let serialized_payload_vec = to_vec(published_payload).map_err(Error::Serialization)?;
@@ -244,10 +203,20 @@ impl RabbitMQClient {
244203
Duration::from_secs(override_timeout.unwrap_or(self.timeout)),
245204
consumer.next(),
246205
)
247-
.await
248-
.map_err(|_| Error::ResponseTimeout)?;
206+
.await;
249207

250-
match response_delivery {
208+
if response_delivery.is_err() {
209+
channel
210+
.close(200, "Normal shutdown")
211+
.await
212+
.map_err(Error::Lapin)?;
213+
214+
return Err(Error::ResponseTimeout);
215+
}
216+
217+
let response_delivery = response_delivery.unwrap();
218+
219+
let result = match response_delivery {
251220
Some(Ok(delivery)) => {
252221
let status = delivery
253222
.properties
@@ -265,7 +234,15 @@ impl RabbitMQClient {
265234
}
266235
Some(Err(e)) => Err(e.into()),
267236
None => panic!("Rabbitmq consumer was cancelled unexpectedly"),
268-
}
237+
};
238+
239+
// Explicitly close the channel
240+
channel
241+
.close(200, "Normal shutdown")
242+
.await
243+
.map_err(Error::Lapin)?;
244+
245+
result
269246
}
270247
}
271248

front/public/locales/en/errors.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@
4747
"Serialization": "Core: cannot serialize request",
4848
"StatusParsing": "Core: cannot parse status",
4949
"UnparsableErrorOutput": "Core returned an error in an unknown format",
50-
"ConnectionDoesNotExist": "Core: message queue: connection not established"
50+
"ConnectionDoesNotExist": "Core: message queue: connection not established",
51+
"CreatePoolLapin": "Core: message queue: cannot create pool",
52+
"DeadpoolLapin": "Core: message queue: pool error"
5153
},
5254
"DatabaseAccessError": "Database access fatal error",
5355
"document": {

front/public/locales/fr/errors.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@
4747
"Serialization": "Core: impossible de sérialiser la requête",
4848
"StatusParsing": "Core: impossible d'obtenir le status",
4949
"UnparsableErrorOutput": "Core: a renvoyé une erreur dans un format inconnu",
50-
"ConnectionDoesNotExist": "Core: file d'attente de messages: connexion non établie"
50+
"ConnectionDoesNotExist": "Core: file d'attente de messages: connexion non établie",
51+
"CreatePoolLapin": "Core: file d'attente de messages: erreur de création de pool",
52+
"DeadpoolLapin": "Core: file d'attente de messages: erreur de pool"
5153
},
5254
"document": {
5355
"NotFound": "Document '{{document_key}}' non trouvé"

0 commit comments

Comments
 (0)