From a8c8656537df3e98927b7f63758df2320564840a Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez Date: Tue, 16 Jul 2024 23:48:08 -0400 Subject: [PATCH 1/4] Improve performance of adaptor middleware by over 50% --- middleware/adaptor/adaptor.go | 41 +++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/middleware/adaptor/adaptor.go b/middleware/adaptor/adaptor.go index 82059cf158..f45701dad2 100644 --- a/middleware/adaptor/adaptor.go +++ b/middleware/adaptor/adaptor.go @@ -5,6 +5,7 @@ import ( "net" "net/http" "reflect" + "sync" "unsafe" "github.com/gofiber/fiber/v3" @@ -13,6 +14,18 @@ import ( "github.com/valyala/fasthttp/fasthttpadaptor" ) +var reqPool = sync.Pool{ + New: func() any { + return new(fasthttp.Request) + }, +} + +var ctxPool = sync.Pool{ + New: func() any { + return new(fasthttp.RequestCtx) + }, +} + // HTTPHandlerFunc wraps net/http handler func to fiber handler func HTTPHandlerFunc(h http.HandlerFunc) fiber.Handler { return HTTPHandler(h) @@ -82,12 +95,13 @@ func HTTPMiddleware(mw func(http.Handler) http.Handler) fiber.Handler { return func(c fiber.Ctx) error { var next bool nextHandler := http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { - next = true // Convert again in case request may modify by middleware + next = true c.Request().Header.SetMethod(r.Method) c.Request().SetRequestURI(r.RequestURI) c.Request().SetHost(r.Host) c.Request().Header.SetHost(r.Host) + for key, val := range r.Header { for _, v := range val { c.Request().Header.Set(key, v) @@ -124,9 +138,10 @@ func FiberApp(app *fiber.App) http.HandlerFunc { func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - // New fasthttp request - req := fasthttp.AcquireRequest() - defer fasthttp.ReleaseRequest(req) + req := reqPool.Get().(*fasthttp.Request) //nolint:forcetypeassert,errcheck // overlinting + req.Reset() + defer reqPool.Put(req) + // Convert net/http -> fasthttp request if r.Body != nil { n, err := io.Copy(req.BodyWriter(), r.Body) @@ -141,26 +156,34 @@ func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc { req.SetRequestURI(r.RequestURI) req.SetHost(r.Host) req.Header.SetHost(r.Host) + for key, val := range r.Header { for _, v := range val { req.Header.Set(key, v) } } + if _, _, err := net.SplitHostPort(r.RemoteAddr); err != nil && err.(*net.AddrError).Err == "missing port in address" { //nolint:errorlint, forcetypeassert // overlinting r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80") } + remoteAddr, err := net.ResolveTCPAddr("tcp", r.RemoteAddr) if err != nil { http.Error(w, utils.StatusMessage(fiber.StatusInternalServerError), fiber.StatusInternalServerError) return } - // New fasthttp Ctx - var fctx fasthttp.RequestCtx + // New fasthttp Ctx from pool + fctx := ctxPool.Get().(*fasthttp.RequestCtx) //nolint:forcetypeassert,errcheck // overlinting + fctx.Response.Reset() + defer ctxPool.Put(fctx) fctx.Init(req, remoteAddr, nil) + if len(h) > 0 { // New fiber Ctx - ctx := app.AcquireCtx(&fctx) + ctx := app.AcquireCtx(fctx) + defer app.ReleaseCtx(ctx) + // Execute fiber Ctx err := h[0](ctx) if err != nil { @@ -168,10 +191,10 @@ func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc { } } else { // Execute fasthttp Ctx though app.Handler - app.Handler()(&fctx) + app.Handler()(fctx) } - // Convert fasthttp Ctx > net/http + // Convert fasthttp Ctx -> net/http fctx.Response.Header.VisitAll(func(k, v []byte) { w.Header().Add(string(k), string(v)) }) From 14e5c5bfc601b818180cc4eb0a1d62054426f0af Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez Date: Tue, 16 Jul 2024 23:57:34 -0400 Subject: [PATCH 2/4] Update whats_new documentation --- docs/whats_new.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/whats_new.md b/docs/whats_new.md index 9155792f6b..26343490fd 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -254,6 +254,34 @@ DRAFT section ## 🧬 Middlewares +### Adaptor + +We are excited to announced that the adaptor middleware has been significantly improved to provide a faster and more efficient experience. Users will notice a nearly ~50% reduction in response times when handling requests through the middleware. Memory usage has been drastically reduced by about ~90%, which means the system runs leaner and more smoothly. Additionally, the middleware now uses fewer resources, with a ~70% reduction in memory allocations. These enhancements ensure a more reliable and efficient service, capable of handling higher loads with less strain on your system. These enhacements will allow you to benefit from the performance of fasthttp while using widely available net/http middlewares. + +| Payload Size | Metric | V2 | V3 | Percent Change | +|--------------|------------------|-----------|----------|-------------------| +| 100KB | Execution Time | 1056 ns/op| 588.6 ns/op | -44.25% | +| | Memory Usage | 2644 B/op | 254 B/op | -90.39% | +| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% | +| 500KB | Execution Time | 1061 ns/op| 562.9 ns/op | -46.94% | +| | Memory Usage | 2644 B/op | 248 B/op | -90.62% | +| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% | +| 1MB | Execution Time | 1080 ns/op| 629.7 ns/op | -41.68% | +| | Memory Usage | 2646 B/op | 267 B/op | -89.91% | +| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% | +| 5MB | Execution Time | 1093 ns/op| 540.3 ns/op | -50.58% | +| | Memory Usage | 2654 B/op | 254 B/op | -90.43% | +| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% | +| 10MB | Execution Time | 1044 ns/op| 533.1 ns/op | -48.94% | +| | Memory Usage | 2665 B/op | 258 B/op | -90.32% | +| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% | +| 25MB | Execution Time | 1069 ns/op| 540.7 ns/op | -49.42% | +| | Memory Usage | 2706 B/op | 289 B/op | -89.32% | +| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% | +| 50MB | Execution Time | 1137 ns/op| 554.6 ns/op | -51.21% | +| | Memory Usage | 2734 B/op | 298 B/op | -89.10% | +| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% | + ### Cache We are excited to introduce a new option in our caching middleware: Cache Invalidator. This feature provides greater control over cache management, allowing you to define a custom conditions for invalidating cache entries. From 5e582109a36f1b03c17ac0f2c0ba8d1af81724a4 Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez Date: Wed, 17 Jul 2024 00:02:26 -0400 Subject: [PATCH 3/4] Remove fasthttp.Request pool --- middleware/adaptor/adaptor.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/middleware/adaptor/adaptor.go b/middleware/adaptor/adaptor.go index f45701dad2..539aa3e24b 100644 --- a/middleware/adaptor/adaptor.go +++ b/middleware/adaptor/adaptor.go @@ -14,12 +14,6 @@ import ( "github.com/valyala/fasthttp/fasthttpadaptor" ) -var reqPool = sync.Pool{ - New: func() any { - return new(fasthttp.Request) - }, -} - var ctxPool = sync.Pool{ New: func() any { return new(fasthttp.RequestCtx) @@ -138,9 +132,8 @@ func FiberApp(app *fiber.App) http.HandlerFunc { func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - req := reqPool.Get().(*fasthttp.Request) //nolint:forcetypeassert,errcheck // overlinting - req.Reset() - defer reqPool.Put(req) + req := fasthttp.AcquireRequest() + defer fasthttp.ReleaseRequest(req) // Convert net/http -> fasthttp request if r.Body != nil { @@ -176,6 +169,7 @@ func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc { // New fasthttp Ctx from pool fctx := ctxPool.Get().(*fasthttp.RequestCtx) //nolint:forcetypeassert,errcheck // overlinting fctx.Response.Reset() + fctx.Request.Reset() defer ctxPool.Put(fctx) fctx.Init(req, remoteAddr, nil) From d5f7648ec692b4141d443eea1cb1772a3df0655f Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Wed, 17 Jul 2024 00:16:43 -0400 Subject: [PATCH 4/4] Update whats_new.md --- docs/whats_new.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/whats_new.md b/docs/whats_new.md index 26343490fd..b2e318c4ca 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -256,7 +256,7 @@ DRAFT section ### Adaptor -We are excited to announced that the adaptor middleware has been significantly improved to provide a faster and more efficient experience. Users will notice a nearly ~50% reduction in response times when handling requests through the middleware. Memory usage has been drastically reduced by about ~90%, which means the system runs leaner and more smoothly. Additionally, the middleware now uses fewer resources, with a ~70% reduction in memory allocations. These enhancements ensure a more reliable and efficient service, capable of handling higher loads with less strain on your system. These enhacements will allow you to benefit from the performance of fasthttp while using widely available net/http middlewares. +The adaptor middleware has been significantly optimized for performance and efficiency. Key improvements include reduced response times, lower memory usage, and fewer memory allocations. These changes make the middleware more reliable and capable of handling higher loads effectively. Enhancements include the introduction of a `sync.Pool` for managing `fasthttp.RequestCtx` instances and better HTTP request and response handling between net/http and fasthttp contexts. | Payload Size | Metric | V2 | V3 | Percent Change | |--------------|------------------|-----------|----------|-------------------|