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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 [Bug]: Cache mutex will blocks other requests if any of the cached API is slow. #2057

Open
3 tasks done
harsh-98 opened this issue Aug 28, 2022 · 4 comments
Open
3 tasks done

Comments

@harsh-98
Copy link

harsh-98 commented Aug 28, 2022

Bug Description

https://github.com/gofiber/fiber/blob/master/middleware/cache/cache.go#L57 The refactored mutex logic in the cache still blocks other requests. The same mutex is used for retrieving cached values and saving the response of cached API. So, if a slow API is cached, it will block the retrieval of cached values for other APIs.

I don't think the mutex is required here. The memory manager itself has rwMutex lock to prevent race conditions. The only behavioral change without mutex is that for the same key, if multiple requests are made simultaneously the response of the last request will override the first request. But since cache implementation only allows saving GET method responses. I don't think saving first response is of importance.

Expected Behavior

Don't block other requests if cached API is slow.

Fiber Version

v2.36.0

Checklist:

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

welcome bot commented Aug 28, 2022

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

@harsh-98 harsh-98 changed the title 🐛 [Bug]: New cache mutex will blocks other request if any of the cached API is slow. 🐛 [Bug]: Cache mutex will blocks other request if any of the cached API is slow. Aug 28, 2022
@harsh-98 harsh-98 changed the title 🐛 [Bug]: Cache mutex will blocks other request if any of the cached API is slow. 🐛 [Bug]: Cache mutex will blocks other requests if any of the cached API is slow. Aug 28, 2022
@ReneWerner87
Copy link
Member

b9efc76

@apoq can you help here ?

@ReneWerner87
Copy link
Member

can you provide a pull request with some tests that will solve the problem

@apoq
Copy link
Contributor

apoq commented Aug 30, 2022

@ReneWerner87 @harsh-98 this been manually tested, after #1764 MR the problematic lock has been fixed, please see below:

// make sure we're not blocking concurrent requests - do unlock
mux.Unlock()

// Continue stack, return err to Fiber if exist
if err := c.Next(); err != nil {
   return err
}

// lock entry back and unlock on finish
mux.Lock()

Also did an automated test with this:

func Test_Cache_Concurrent_Read(t *testing.T) {
	app := fiber.New()
	app.Use(New(Config{Expiration: time.Minute, StoreResponseHeaders: true, CacheHeader: "X-Cache"}))

	app.Get("/fast", func(c *fiber.Ctx) error {
		return c.Status(fiber.StatusOK).SendString("fast")
	})

	app.Get("/slow", func(c *fiber.Ctx) error {
		time.Sleep(time.Millisecond * 300)
		return c.Status(fiber.StatusOK).SendString("slow")
	})
	start := time.Now()

	// first: /fast miss
	rsp, err := app.Test(httptest.NewRequest("GET", "/fast", nil))
	utils.AssertEqual(t, nil, err)
	utils.AssertEqual(t, "miss", rsp.Header.Get("X-Cache"))

	// second: /slow miss cache, check if it blocks further request to /fast
	var slowRespCacheHeaderChan = make(chan string)
	go func(ch chan string) {
		srsp, _ := app.Test(httptest.NewRequest("GET", "/slow", nil), 350)
		ch <- srsp.Header.Get("X-Cache")
	}(slowRespCacheHeaderChan)

	// third: /fast hit, check if it went in less than 10ms (slow one could potentially block it for 300ms)
	rsp, err = app.Test(httptest.NewRequest("GET", "/fast", nil))
	utils.AssertEqual(t, nil, err)
	utils.AssertEqual(t, "hit", rsp.Header.Get("X-Cache"))
	timeSpentForFast := time.Since(start)
	utils.AssertEqual(t, true, timeSpentForFast < time.Millisecond*10)

	slowHeader := <-slowRespCacheHeaderChan
	timeSpentForSlow := time.Since(start)
	utils.AssertEqual(t, true, timeSpentForSlow >= time.Millisecond*300)
	utils.AssertEqual(t, "miss", slowHeader)
}

Good point about redundant use of mutex here, that's true, when actual manager does it on it's own, but there's no mentioned bug here (ie slow routes when cached, do not affect other routes). That was the point of #1764

I'll do some tests later to remove redundant mutex. Thanks

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

No branches or pull requests

3 participants