Skip to content

Deadlock when calling GetStatusCode() in SetOnLimitReached() #106

Description

@jarv

Calling GetStatusCode() which will acquire a RLock in the function called by ExecOnLimitReached() will cause a deadlock on concurrent requests.

The deadlock is fairly easy to reproduce using the code below and a request generator like ab.

My use-case for calling GetStatusCode() in the function passed to SetOnLimitReached() is to increment a prometheus counter that had a label value for the status code being used.

code to reproduce:

package main

import (
    "fmt"
    "log"
    "net/http"
    _ "net/http/pprof"
    "runtime"

    "github.com/didip/tollbooth/v7"
)

func testHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "OK\n")
}


func main() {
    runtime.SetBlockProfileRate(1)
    runtime.SetMutexProfileFraction(5)
    listenStr := fmt.Sprintf(":%d", 6060)

    lmt := tollbooth.NewLimiter(float64(1), nil)
    log.Println("Setting rate limit for 1req/sec")
    lmt.SetIPLookups([]string{"RemoteAddr"})
    lmt.SetMessage("Your are sending requests too fast, slow down!")
    lmt.SetOnLimitReached(func(w http.ResponseWriter, r *http.Request) {
        log.Printf("Rate limit reached StatusCode: %d\n", lmt.GetStatusCode())
    })

    http.Handle("/", tollbooth.LimitFuncHandler(lmt, testHandler))

    log.Printf("Server started %s\n", listenStr)
    log.Fatal(http.ListenAndServe(listenStr, nil))
}

and the following to generate load to cause the deadlock:

ab -n 2000 -c 50 http://localhost:6060/'

Full backtrace (condensed using panicparse):

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions