wip feat: improve middleware performance by 50%#1209
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
✨ No issues found! Your code is sparkling clean! ✨ Need help? Join our Discord for support! |
5fb08e0 to
0472e42
Compare
72ede91 to
cdd6889
Compare
📝 WalkthroughWalkthroughThis PR introduces middleware chain execution functions in Changes
Sequence DiagramsequenceDiagram
actor Client
participant Server as Server
participant BeforeChain as Before Chain
participant Middleware as Middleware(s)
participant AfterChain as After Chain
participant HTTP as HTTP Handler
Client->>Server: Request
Server->>BeforeChain: execute_before_middleware_chain()
rect rgba(100, 150, 200, 0.5)
Note over BeforeChain,Middleware: Detect all_sync?
alt All Synchronous
BeforeChain->>BeforeChain: Single GIL block
BeforeChain->>Middleware: Execute all
else Has Async
loop Each Middleware
BeforeChain->>Middleware: await execute_middleware_function()
end
end
end
alt Early Response from Chain
Middleware-->>BeforeChain: Response
BeforeChain-->>Server: MiddlewareReturn::Response
Server-->>Client: Response
else Request Continues
Middleware-->>BeforeChain: Request
BeforeChain-->>Server: MiddlewareReturn::Request
Server->>HTTP: execute_http_function()
HTTP-->>Server: Response
Server->>AfterChain: execute_after_middleware_chain()
rect rgba(150, 150, 100, 0.5)
AfterChain->>AfterChain: Detect all_sync?
alt All Synchronous
AfterChain->>Middleware: Execute all in GIL
else Has Async
loop Each Middleware
AfterChain->>Middleware: await execution
end
end
end
Middleware-->>AfterChain: Response
AfterChain-->>Server: Updated Response
Server-->>Client: Response
end
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/executors/mod.rs`:
- Around line 263-298: The after-middleware chain currently only forwards a
Response, breaking the (request, response) contract used by
execute_after_middleware_function; update execute_after_middleware_chain to
accept and pass both &Request and &Response to middleware invocations (use
get_function_output and execute_middleware_function with both references so
after_request(req, resp) callbacks can access request-scoped data), ensure you
handle the returned MiddlewareReturn the same way (only accept
MiddlewareReturn::Response) and update the caller in server.rs to pass &request
into this helper accordingly.
In `@src/server.rs`:
- Around line 517-520: The current early return in index() when
execute_before_middleware_chain returns MiddlewareReturn::Response(r) skips
global response header application and the after-middleware chain; instead,
change the handling so index() continues down the common response path: if
execute_before_middleware_chain yields MiddlewareReturn::Response, store that
Response as the "short-circuit" response while also preserving the most-recent
mutated Request (from MiddlewareReturn::Request or a sidecar), then proceed
through the same global header application and execute_after_middleware_chain on
the response before returning; update the match on
execute_before_middleware_chain (and any variables around before_middlewares) to
propagate both the latest Request and the Response into the standard response
flow rather than returning ResponseType::Standard immediately.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a573752f-50fa-4ab3-a2d6-3721ec7f5e1f
📒 Files selected for processing (2)
src/executors/mod.rssrc/server.rs
| pub async fn execute_after_middleware_chain( | ||
| input: &Response, | ||
| middlewares: &[FunctionInfo], | ||
| ) -> Result<MiddlewareReturn> { | ||
| let mut current_response = input.clone(); | ||
|
|
||
| // Check if all middlewares are sync to optimize GIL usage | ||
| let all_sync = middlewares.iter().all(|m| !m.is_async); | ||
|
|
||
| if all_sync { | ||
| // Execute all sync middlewares in a single GIL acquisition | ||
| Python::with_gil(|py| -> Result<MiddlewareReturn> { | ||
| for middleware in middlewares { | ||
| let output = get_function_output(middleware, py, ¤t_response)?; | ||
|
|
||
| // After middleware should return Response | ||
| match output.extract::<Response>() { | ||
| Ok(response) => current_response = response, | ||
| Err(e) => return Err(e.into()), | ||
| } | ||
| } | ||
|
|
||
| Ok(MiddlewareReturn::Response(current_response)) | ||
| }) | ||
| } else { | ||
| // Fall back to individual execution for mixed sync/async middlewares | ||
| for middleware in middlewares { | ||
| current_response = match execute_middleware_function(¤t_response, middleware).await? { | ||
| MiddlewareReturn::Response(r) => r, | ||
| MiddlewareReturn::Request(_) => { | ||
| return Err(anyhow::anyhow!("After middleware returned a request")) | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| Ok(MiddlewareReturn::Response(current_response)) |
There was a problem hiding this comment.
Preserve the (request, response) contract for after middleware.
This chain runner only passes a Response: the sync path calls get_function_output(...), and the async path calls execute_middleware_function(...). That regresses the behavior implemented by execute_after_middleware_function at Lines 181-217, which passes both request and response. Any after_request(req, resp) middleware will now fail at runtime, and those callbacks can no longer inspect request-scoped data such as path params.
🔧 Suggested fix
-pub async fn execute_after_middleware_chain(
- input: &Response,
+pub async fn execute_after_middleware_chain(
+ request: &Request,
+ input: &Response,
middlewares: &[FunctionInfo],
) -> Result<MiddlewareReturn> {
let mut current_response = input.clone();
@@
if all_sync {
Python::with_gil(|py| -> Result<MiddlewareReturn> {
for middleware in middlewares {
- let output = get_function_output(middleware, py, ¤t_response)?;
+ let output = get_function_output_with_two_args(
+ middleware,
+ py,
+ request,
+ ¤t_response,
+ )?;
@@
} else {
for middleware in middlewares {
- current_response = match execute_middleware_function(¤t_response, middleware).await? {
+ current_response = match execute_after_middleware_function(
+ request,
+ ¤t_response,
+ middleware,
+ )
+ .await? {
MiddlewareReturn::Response(r) => r,
MiddlewareReturn::Request(_) => {
return Err(anyhow::anyhow!("After middleware returned a request"))
}
};
}src/server.rs will need to pass &request into this helper as well.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/executors/mod.rs` around lines 263 - 298, The after-middleware chain
currently only forwards a Response, breaking the (request, response) contract
used by execute_after_middleware_function; update execute_after_middleware_chain
to accept and pass both &Request and &Response to middleware invocations (use
get_function_output and execute_middleware_function with both references so
after_request(req, resp) callbacks can access request-scoped data), ensure you
handle the returned MiddlewareReturn the same way (only accept
MiddlewareReturn::Response) and update the caller in server.rs to pass &request
into this helper accordingly.
| request = match execute_before_middleware_chain(&request, &before_middlewares).await { | ||
| Ok(MiddlewareReturn::Request(r)) => r, | ||
| Ok(MiddlewareReturn::Response(r)) => { | ||
| return ResponseType::Standard(r); |
There was a problem hiding this comment.
Avoid returning short-circuited before-middleware responses here.
This exits index() before the common response path at Lines 567-609, so responses produced by before middleware no longer receive global response headers or the after-middleware chain. That will show up on auth/preflight-style short circuits as missing CORS/security headers. If you keep these responses on the normal path, the chain result also needs to preserve the latest mutated Request.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/server.rs` around lines 517 - 520, The current early return in index()
when execute_before_middleware_chain returns MiddlewareReturn::Response(r) skips
global response header application and the after-middleware chain; instead,
change the handling so index() continues down the common response path: if
execute_before_middleware_chain yields MiddlewareReturn::Response, store that
Response as the "short-circuit" response while also preserving the most-recent
mutated Request (from MiddlewareReturn::Request or a sidecar), then proceed
through the same global header application and execute_after_middleware_chain on
the response before returning; update the match on
execute_before_middleware_chain (and any variables around before_middlewares) to
propagate both the latest Request and the Response into the standard response
flow rather than returning ResponseType::Standard immediately.
Description
This PR fixes #
Summary
This PR does....
PR Checklist
Please ensure that:
Pre-Commit Instructions:
Summary by CodeRabbit