Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

馃 [Question]: Timeout is not work in Fiber v2.42.0 #2364

Closed
3 tasks done
codingkits opened this issue Mar 10, 2023 · 18 comments 路 Fixed by #2367
Closed
3 tasks done

馃 [Question]: Timeout is not work in Fiber v2.42.0 #2364

codingkits opened this issue Mar 10, 2023 · 18 comments 路 Fixed by #2367

Comments

@codingkits
Copy link

Question Description

Timeout is not work in Fiber v2.42.0

For Example
In Code Snippet
It should return a "request timeout" , if the handler costs more than 1000 Millisecond
But it does not work

Code Snippet (optional)

package main

import "github.com/gofiber/fiber/v2"
import "log"

func main() {
  app := fiber.New()

  // Like this , the timeout does not work
  app.Get("/abc", timeout.New(xxHandler, time.Millisecond*1000))

  log.Fatal(app.Listen(":3000"))
}

Checklist:

  • I agree to follow Fiber's Code of Conduct.
  • I have checked for existing issues that describe my questions prior to opening this one.
  • I understand that improperly formatted questions may be closed without explanation.
@welcome
Copy link

welcome bot commented Mar 10, 2023

Thanks for opening your first issue here! 馃帀 Be sure to follow the issue template! If you need help or want to chat with us, join us on Discord https://gofiber.io/discord

@ReneWerner87
Copy link
Member

ReneWerner87 commented Mar 10, 2023

@hakankutluay pls help here ?

we probably produced a breaking change in the last amendment after all

because the behavior has changed #2090

before the usage was possible without using the context in the handler

please test it yourself

package main

import (
	"log"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/timeout"
)

func main() {
	app := fiber.New()

	// Like this , the timeout does not work
	app.Get("/abc", timeout.New(func(ctx *fiber.Ctx) error {
		time.Sleep(2 * time.Second)
		return nil
	}, time.Millisecond*1000))

	log.Fatal(app.Listen(":3000"))
}
curl --location 'localhost:3000/abc' -I

Current state (after Fiber v2.38.1):

HTTP/1.1 200 OK
Date: Fri, 10 Mar 2023 08:44:49 GMT

Expectation (works in Fiber v2.37.1):

HTTP/1.1 408 Request Timeout
Date: Fri, 10 Mar 2023 08:50:44 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 15

@ReneWerner87
Copy link
Member

@hakankutluay would like to restore the original functionality and provide a separate function for working with contexts as mentioned in the old pull request

@hakankutluay
Copy link
Contributor

hakankutluay commented Mar 12, 2023

@ReneWerner87 sure, but the original functionality has race conditions and is not safe to use on production.

The PR was for a new timeout functionality but we have decided to replace it after a discussion.

If it's ok to have the original one and add new timeout as like timeout.NewWithContext, I would send a PR for it.

@hakankutluay
Copy link
Contributor

hakankutluay commented Mar 12, 2023

@ReneWerner87 we have a workaround to make it work with time.Sleep

func main() {
	app := fiber.New()

	// Like this , the timeout does not work
	app.Get("/abc", timeout.New(func(ctx *fiber.Ctx) error {
		time.Sleep(2 * time.Second)
		return nil
	}, time.Millisecond*1000))

	log.Fatal(app.Listen(":3000"))
}

Sleep with context timeout

func main() {
	app := fiber.New()

	// Like this , the timeout does not work
	app.Get("/abc", timeout.New(func(ctx *fiber.Ctx) error {
		if err := sleepWithContext(c.UserContext(), 2 * time.Second, context.DeadlineExceeded); err != nil {
			return fmt.Errorf("%w: execution error", err)
		}
		return nil
	}, time.Millisecond*1000))

	log.Fatal(app.Listen(":3000"))
}

func sleepWithContext(ctx context.Context, d time.Duration, te error) error {
	timer := time.NewTimer(d)
	select {
	case <-ctx.Done():
		if !timer.Stop() {
			<-timer.C
		}
		return te
	case <-timer.C:
	}
	return nil
}

@ReneWerner87
Copy link
Member

Can we provide something so that the old functionality behavior is restored without the consumers having to change anything (otherwise it is a breaking change if the behavior changes)?

@hakankutluay
Copy link
Contributor

hakankutluay commented Mar 12, 2023

@ReneWerner87 I couldn't find a way to implement race condition-free timeout middleware that supports time.Sleep or long-running golang code execution without context.Context supported.

@ReneWerner87
Copy link
Member

@hakankutluay can you create an example of the race conditions in code so i can recreate them

@hakankutluay
Copy link
Contributor

hakankutluay commented Mar 14, 2023

@ReneWerner87 sure,

server
use previous fiber versions that has original timeout middleware. like v2.37.1

func main() {
	app := fiber.New()

	app.Use(recover.New(recover.Config{
		EnableStackTrace: true,
	}))
	app.Use(logger.New())

	// Like this , the timeout does not work
	app.Get("/abc", timeout.New(func(ctx *fiber.Ctx) error {
		time.Sleep(time.Millisecond * 2000)
		return ctx.SendStatus(http.StatusNoContent)
	}, time.Millisecond*1000))

	log.Fatal(app.Listen(":3200"))
}

run with -race flag. docs

 go run -race ./main.go

call in parallel

seq 1 200 | xargs -n1 -P20  curl "http://localhost:3200/abc"

some of them will introduce race conditions

==================
WARNING: DATA RACE
Read at 0x00c0005a6018 by goroutine 30:
  github.com/valyala/fasthttp.(*Response).bodyBytes()
      /Users/hakan.kutluay/go/pkg/mod/github.com/valyala/fasthttp@v1.41.0/http.go:362 +0x274
  github.com/valyala/fasthttp.(*Response).Body()
      /Users/hakan.kutluay/go/pkg/mod/github.com/valyala/fasthttp@v1.41.0/http.go:352 +0x230
  github.com/gofiber/fiber/v2.(*Ctx).SendStatus()
      /Users/hakan.kutluay/go/pkg/mod/github.com/gofiber/fiber/v2@v2.37.1/ctx.go:1527 +0x80
  main.main.func1()
      /Users/hakan.kutluay/Projects/go/fiber-middleware-test/original/main.go:25 +0x38
  github.com/gofiber/fiber/v2/middleware/timeout.New.func2.1()
      /Users/hakan.kutluay/go/pkg/mod/github.com/gofiber/fiber/v2@v2.37.1/middleware/timeout/timeout.go:31 +0x60

Previous write at 0x00c0005a6018 by goroutine 22:
  github.com/valyala/bytebufferpool.(*ByteBuffer).Reset()
      /Users/hakan.kutluay/go/pkg/mod/github.com/valyala/bytebufferpool@v1.0.0/bytebuffer.go:110 +0xb4
  github.com/valyala/fasthttp.(*Response).ResetBody()
      /Users/hakan.kutluay/go/pkg/mod/github.com/valyala/fasthttp@v1.41.0/http.go:595 +0x94
  github.com/valyala/fasthttp.(*Response).resetSkipHeader()
      /Users/hakan.kutluay/go/pkg/mod/github.com/valyala/fasthttp@v1.41.0/http.go:1073 +0xc0
  github.com/valyala/fasthttp.(*Response).Reset()
      /Users/hakan.kutluay/go/pkg/mod/github.com/valyala/fasthttp@v1.41.0/http.go:1065 +0xb8
  github.com/valyala/fasthttp.(*RequestCtx).reset()
      /Users/hakan.kutluay/go/pkg/mod/github.com/valyala/fasthttp@v1.41.0/server.go:795 +0x44
  github.com/valyala/fasthttp.(*Server).releaseCtx()
      /Users/hakan.kutluay/go/pkg/mod/github.com/valyala/fasthttp@v1.41.0/server.go:2751 +0x44
  github.com/valyala/fasthttp.(*Server).serveConn()
      /Users/hakan.kutluay/go/pkg/mod/github.com/valyala/fasthttp@v1.41.0/server.go:2449 +0x1e5c
  github.com/valyala/fasthttp.(*Server).serveConn-fm()
      <autogenerated>:1 +0x44
  github.com/valyala/fasthttp.(*workerPool).workerFunc()
      /Users/hakan.kutluay/go/pkg/mod/github.com/valyala/fasthttp@v1.41.0/workerpool.go:224 +0x98
  github.com/valyala/fasthttp.(*workerPool).getCh.func1()
      /Users/hakan.kutluay/go/pkg/mod/github.com/valyala/fasthttp@v1.41.0/workerpool.go:196 +0x48

@ReneWerner87
Copy link
Member

ReneWerner87 commented Mar 14, 2023

Then let's mark the old function deprecated with reference to the new one and an example for the usage

@ReneWerner87
Copy link
Member

@hakankutluay
https://github.com/valyala/fasthttp/blob/7846101dc65ee5e02cea672af4594e3a80048632/server.go#L455-L487

back then the timeout middleware was a copy of the timeout handler, maybe i had forgotten something in the adaption ?

or is this timeouthandler in use also with race conditions ? can you check this

@ReneWerner87
Copy link
Member

maybe the part with the check for the concurrency request count

@hakankutluay
Copy link
Contributor

hakankutluay commented Mar 15, 2023

@ReneWerner87 I have checked TimeoutHandler on fasthttp, and it doesn't raise race conditions.

Probably concurrencyCh on the server limits concurrent requests by , It may handle race condition issues. we couldn't access this channel since it's private.

Or context has special field for timeout response as timeoutResponse, It writes and uses timeout response on that field and this may avoid race conditions.

@leonklingele
Copy link
Member

I don't think we need to provide an old broken way and a new way which is broken as well.

Why can't we use fasthttp.TimeoutHandler here?

https://github.com/valyala/fasthttp/blob/0be5a4150cb0e35605cffd09e922707355dbdec3/server.go#L437-L446

@hakankutluay
Copy link
Contributor

hakankutluay commented Mar 22, 2023

fasthttp.TimeoutHandler returns fasthttp.RequestHandler which we can't use on routing directly. It could be implemented on Fiber too, but as I mentioned before, it uses some fasthttp internal calls which we can't access.

By the way, the new implementation supports context cancelation, which is useful to cancel underlying operations like DB calls, http calls etc. that are not supported directly on fasthttp.TimeoutHandler. Only needs some workaround with direct usage of time.Sleep.

@ReneWerner87
Copy link
Member

@hakankutluay Can you create a pull request in fasthttp which allows this?

@ReneWerner87
Copy link
Member

@hakankutluay can you take a look at this ?

@hakankutluay
Copy link
Contributor

@ReneWerner87 not yet, I'll do whenever I have a time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants