Question

I need to apply some tests to each request and fire responce based on result of tests. If one of the test fail, I need to send responce imediatelly, otherwise I wait when all tests are done succesfully. I want to do that tests with concurrency.

Now, I do that like this (simplified):

func handler_request_checker(w http.ResponseWriter, r *http.Request) {
    done := make(chan bool)
    quit := make(chan bool)
    counter := 0

    go TestOne(r,done,quit)
    go TestTwo(r,done,quit)
    ..............
    go TestTen(r,done,quit)


    for {
        select {
            case <- quit:
                fmt.Println("got quit signal")
                return
            case <- done:
                counter++
                if counter == 10 {
                    fmt.Println("All checks passed succesfully")
                    return
                }
        }
    }

func main() {
    http.HandleFunc("/", handler_request_checker)
    http.ListenAndServe()
}

Example of one of goroutine:

func TestOne(r *http.Request, done,quit chan bool) {
    ip,_,ok := net.SplitHostPort(r.RemoteAddr)
    if ok == nil {
        for _,item := range BAD_IP_LIST {
            if strings.Contains(ip,item) {
                quit <- true
                return
            }
        }
        done <- true
        return
    } else {
        quit <- true
        return
    }
}

Problem is that goroutines doesnt' free memory after I go t quit signal. I suppose that happens because there is something in done chanel. I'm completely new in GO, so maybe I use them wrong way?

For example, when I start load check http_load -parallel 10 -seconds 10, that goroutine easily eats 100+ MB of RAM and dont'g give it back to the system. On next check, it eat 100+ MB more, and so on.

If I do that tests without go (step-by-step), program takes no more than 10-15mb with any load checks.

Was it helpful?

Solution

Your guess is right. You are using synchronous channels, which means that both the sender and the receiver must be available in order to transmit a value.

Once your handler_request_checker function gets a quit signal, it stops retrieving any values of the done and the quit channel. Further TestOne, TestTwo, etc. goroutines will be blocked when they try to send their result. Even worse, they will stay in memory forever because they are still running since they haven't transmitted their result yet.

One way to solve the problem would be to use buffered (asynchronous) channels for done and quit. For example:

done := make(chan bool, 10)
quit := make(chan bool, 10)

If you use a buffer size of 10 for your 10 tests, then all goroutines are able to send their results, even when there is no reader available anymore. All goroutines will exit cleanly and the channels (that might contain some unread results) will get garbage collected once all goroutines have quit.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top