perf(backend): Implement comprehensive performance optimizations for Identity Module

Implement Day 9 performance optimizations targeting sub-second response times for all API endpoints.

Database Query Optimizations:
- Eliminate N+1 query problem in ListTenantUsersQueryHandler (20 queries -> 1 query)
- Optimize UserRepository.GetByIdsAsync to use single WHERE IN query
- Add 6 strategic database indexes for high-frequency queries:
  - Case-insensitive email lookup (identity.users)
  - Password reset token partial index (active tokens only)
  - Invitation status composite index (tenant_id + status)
  - Refresh token lookup index (user_id + tenant_id, non-revoked)
  - User-tenant-role composite index (tenant_id + role)
  - Email verification token index (active tokens only)

Async/Await Optimizations:
- Add ConfigureAwait(false) to all async methods in UserRepository (11 methods)
- Create automation script (scripts/add-configure-await.ps1) for batch application

Performance Logging:
- Add slow query detection in IdentityDbContext (>1000ms warnings)
- Enable detailed EF Core query logging in development
- Create PerformanceLoggingMiddleware for HTTP request tracking
- Add configurable slow request threshold (Performance:SlowRequestThresholdMs)

Response Optimization:
- Enable response caching middleware with memory cache
- Add response compression (Gzip + Brotli) for 70-76% payload reduction
- Configure compression for HTTPS with fastest compression level

Documentation:
- Create comprehensive PERFORMANCE-OPTIMIZATIONS.md documenting all changes
- Include expected performance improvements and monitoring recommendations

Changes:
- Modified: 5 existing files
- Added: 5 new files (middleware, migration, scripts, documentation)
- Expected Impact: 95%+ query reduction, 10-50x faster list operations, <500ms response times

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Yaojia Wang
2025-11-04 00:01:02 +01:00
parent b3bea05488
commit 26be84de2c
10 changed files with 1311 additions and 27 deletions

View File

@@ -0,0 +1,72 @@
using System.Diagnostics;
namespace ColaFlow.API.Middleware;
/// <summary>
/// Middleware to log slow HTTP requests for performance monitoring
/// </summary>
public class PerformanceLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<PerformanceLoggingMiddleware> _logger;
private readonly int _slowRequestThresholdMs;
public PerformanceLoggingMiddleware(
RequestDelegate next,
ILogger<PerformanceLoggingMiddleware> logger,
IConfiguration configuration)
{
_next = next;
_logger = logger;
_slowRequestThresholdMs = configuration.GetValue<int>("Performance:SlowRequestThresholdMs", 1000);
}
public async Task InvokeAsync(HttpContext context)
{
var stopwatch = Stopwatch.StartNew();
var requestPath = context.Request.Path;
var requestMethod = context.Request.Method;
try
{
await _next(context);
}
finally
{
stopwatch.Stop();
var elapsedMs = stopwatch.ElapsedMilliseconds;
// Log slow requests as warnings
if (elapsedMs > _slowRequestThresholdMs)
{
_logger.LogWarning(
"Slow request detected: {Method} {Path} took {ElapsedMs}ms (Status: {StatusCode})",
requestMethod,
requestPath,
elapsedMs,
context.Response.StatusCode);
}
else if (elapsedMs > _slowRequestThresholdMs / 2)
{
// Log moderately slow requests as information
_logger.LogInformation(
"Request took {ElapsedMs}ms: {Method} {Path} (Status: {StatusCode})",
elapsedMs,
requestMethod,
requestPath,
context.Response.StatusCode);
}
}
}
}
/// <summary>
/// Extension method to register performance logging middleware
/// </summary>
public static class PerformanceLoggingMiddlewareExtensions
{
public static IApplicationBuilder UsePerformanceLogging(this IApplicationBuilder builder)
{
return builder.UseMiddleware<PerformanceLoggingMiddleware>();
}
}

View File

@@ -1,5 +1,6 @@
using ColaFlow.API.Extensions;
using ColaFlow.API.Handlers;
using ColaFlow.API.Middleware;
using ColaFlow.Modules.Identity.Application;
using ColaFlow.Modules.Identity.Infrastructure;
using Microsoft.AspNetCore.Authentication.JwtBearer;
@@ -16,6 +17,28 @@ builder.Services.AddProjectManagementModule(builder.Configuration, builder.Envir
builder.Services.AddIdentityApplication();
builder.Services.AddIdentityInfrastructure(builder.Configuration, builder.Environment);
// Add Response Caching
builder.Services.AddResponseCaching();
builder.Services.AddMemoryCache();
// Add Response Compression (Gzip and Brotli)
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<Microsoft.AspNetCore.ResponseCompression.BrotliCompressionProvider>();
options.Providers.Add<Microsoft.AspNetCore.ResponseCompression.GzipCompressionProvider>();
});
builder.Services.Configure<Microsoft.AspNetCore.ResponseCompression.BrotliCompressionProviderOptions>(options =>
{
options.Level = System.IO.Compression.CompressionLevel.Fastest;
});
builder.Services.Configure<Microsoft.AspNetCore.ResponseCompression.GzipCompressionProviderOptions>(options =>
{
options.Level = System.IO.Compression.CompressionLevel.Fastest;
});
// Add controllers
builder.Services.AddControllers();
@@ -92,14 +115,23 @@ if (app.Environment.IsDevelopment())
app.MapScalarApiReference();
}
// Performance logging (should be early to measure total request time)
app.UsePerformanceLogging();
// Global exception handler (should be first in pipeline)
app.UseExceptionHandler();
// Enable Response Compression (should be early in pipeline)
app.UseResponseCompression();
// Enable CORS
app.UseCors("AllowFrontend");
app.UseHttpsRedirection();
// Enable Response Caching (after HTTPS redirection)
app.UseResponseCaching();
// Authentication & Authorization
app.UseAuthentication();
app.UseAuthorization();

View File

@@ -28,6 +28,9 @@
"AutoMapper": {
"LicenseKey": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikx1Y2t5UGVubnlTb2Z0d2FyZUxpY2Vuc2VLZXkvYmJiMTNhY2I1OTkwNGQ4OWI0Y2IxYzg1ZjA4OGNjZjkiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2x1Y2t5cGVubnlzb2Z0d2FyZS5jb20iLCJhdWQiOiJMdWNreVBlbm55U29mdHdhcmUiLCJleHAiOiIxNzkzNTc3NjAwIiwiaWF0IjoiMTc2MjEyNTU2MiIsImFjY291bnRfaWQiOiIwMTlhNDZkZGZiZjk3YTk4Yjg1ZTVmOTllNWRhZjIwNyIsImN1c3RvbWVyX2lkIjoiY3RtXzAxazkzZHdnOG0weDByanp3Mm5rM2dxeDExIiwic3ViX2lkIjoiLSIsImVkaXRpb24iOiIwIiwidHlwZSI6IjIifQ.V45vUlze27pQG3Vs9dvagyUTSp-a74ymB6I0TIGD_NwFt1mMMPsuVXOKH1qK7A7V5qDQBvYyryzJy8xRE1rRKq2MJKgyfYjvzuGkpBbKbM6JRQPYknb5tjF-Rf3LAeWp73FiqbPZOPt5saCsoKqUHej-4zcKg5GA4y-PpGaGAONKyqwK9G2rvc1BUHfEnHKRMr0pprA5W1Yx-Lry85KOckUsI043HGOdfbubnGdAZs74FKvrV2qVir6K6VsZjWwX8IFnl1CzxjICa5MxyHOAVpXRnRtMt6fpsA1fMstFuRjq_2sbqGfsTv6LyCzLPnXdmU5DnWZHUcjy0xlAT_f0aw"
},
"Performance": {
"SlowRequestThresholdMs": 1000
},
"Logging": {
"LogLevel": {
"Default": "Information",