Skip to content

Commit 72ede91

Browse files
committed
feat: improve middleware performance by 50%
1 parent c3d8015 commit 72ede91

File tree

2 files changed

+92
-5
lines changed

2 files changed

+92
-5
lines changed

src/executors/mod.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,89 @@ where
105105
}
106106
}
107107

108+
// Execute a chain of before middleware functions with batched GIL acquisition
109+
#[inline]
110+
pub async fn execute_before_middleware_chain(
111+
input: &Request,
112+
middlewares: &[FunctionInfo],
113+
) -> Result<MiddlewareReturn> {
114+
let mut current_request = input.clone();
115+
116+
// Check if all middlewares are sync to optimize GIL usage
117+
let all_sync = middlewares.iter().all(|m| !m.is_async);
118+
119+
if all_sync {
120+
// Execute all sync middlewares in a single GIL acquisition
121+
Python::with_gil(|py| -> Result<MiddlewareReturn> {
122+
for middleware in middlewares {
123+
let output = get_function_output(middleware, py, &current_request)?;
124+
125+
// Try response extraction first, then request
126+
match output.extract::<Response>() {
127+
Ok(response) => return Ok(MiddlewareReturn::Response(response)),
128+
Err(_) => match output.extract::<Request>() {
129+
Ok(request) => current_request = request,
130+
Err(e) => return Err(e.into()),
131+
},
132+
}
133+
}
134+
135+
Ok(MiddlewareReturn::Request(current_request))
136+
})
137+
} else {
138+
// Fall back to individual execution for mixed sync/async middlewares
139+
for middleware in middlewares {
140+
current_request = match execute_middleware_function(&current_request, middleware).await? {
141+
MiddlewareReturn::Request(r) => r,
142+
MiddlewareReturn::Response(r) => return Ok(MiddlewareReturn::Response(r)),
143+
};
144+
}
145+
146+
Ok(MiddlewareReturn::Request(current_request))
147+
}
148+
}
149+
150+
// Execute a chain of after middleware functions with batched GIL acquisition
151+
#[inline]
152+
pub async fn execute_after_middleware_chain(
153+
input: &Response,
154+
middlewares: &[FunctionInfo],
155+
) -> Result<MiddlewareReturn> {
156+
let mut current_response = input.clone();
157+
158+
// Check if all middlewares are sync to optimize GIL usage
159+
let all_sync = middlewares.iter().all(|m| !m.is_async);
160+
161+
if all_sync {
162+
// Execute all sync middlewares in a single GIL acquisition
163+
Python::with_gil(|py| -> Result<MiddlewareReturn> {
164+
for middleware in middlewares {
165+
let output = get_function_output(middleware, py, &current_response)?;
166+
167+
// After middleware should return Response
168+
match output.extract::<Response>() {
169+
Ok(response) => current_response = response,
170+
Err(e) => return Err(e.into()),
171+
}
172+
}
173+
174+
Ok(MiddlewareReturn::Response(current_response))
175+
})
176+
} else {
177+
// Fall back to individual execution for mixed sync/async middlewares
178+
for middleware in middlewares {
179+
current_response = match execute_middleware_function(&current_response, middleware).await? {
180+
MiddlewareReturn::Response(r) => r,
181+
MiddlewareReturn::Request(_) => {
182+
return Err(anyhow::anyhow!("After middleware returned a request"))
183+
}
184+
};
185+
}
186+
187+
Ok(MiddlewareReturn::Response(current_response))
188+
}
189+
}
190+
108191
#[inline]
109192
pub async fn execute_http_function(
110193
request: &Request,

src/server.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::executors::{
2-
execute_http_function, execute_middleware_function, execute_startup_handler,
2+
execute_http_function, execute_before_middleware_chain, execute_after_middleware_chain, execute_startup_handler,
33
};
44

55
use crate::routers::const_router::ConstRouter;
@@ -488,8 +488,10 @@ async fn index(
488488
before_middlewares.push(function);
489489
request.path_params = route_params;
490490
}
491-
for before_middleware in before_middlewares {
492-
request = match execute_middleware_function(&request, &before_middleware).await {
491+
492+
// Execute all before middlewares with batched GIL acquisition
493+
if !before_middlewares.is_empty() {
494+
request = match execute_before_middleware_chain(&request, &before_middlewares).await {
493495
Ok(MiddlewareReturn::Request(r)) => r,
494496
Ok(MiddlewareReturn::Response(r)) => {
495497
// If a before middleware returns a response, we abort the request and return the response
@@ -557,10 +559,12 @@ async fn index(
557559
{
558560
after_middlewares.push(function);
559561
}
560-
for after_middleware in after_middlewares {
562+
563+
// Execute all after middlewares with batched GIL acquisition
564+
if !after_middlewares.is_empty() {
561565
// Middleware only works with standard responses
562566
if let ResponseType::Standard(std_response) = response {
563-
response = match execute_middleware_function(&std_response, &after_middleware).await {
567+
response = match execute_after_middleware_chain(&std_response, &after_middlewares).await {
564568
Ok(MiddlewareReturn::Request(_)) => {
565569
error!("After middleware returned a request");
566570
return ResponseType::Standard(Response::internal_server_error(None));

0 commit comments

Comments
 (0)