Responsive Ad Area

Share This Post

test

How to use SingleFlight to share downloaded large size file?

I’m proxying a bunch of http GET calls through singleflight. But returned response is only seen by the first request.

I also noticed a problem in my test. If the first request times out, the response will be lost.

Let’s say r1,r2,r3 are requests that come in order. They are all grouped in one groupKey. If r1 time out , r2 and r3 will wait until the shared HTTP call returns or until their own timeout.

proxy code

// add auth to the requst and proxy to target host
var serveReverseProxy = func(target string, res http.ResponseWriter, req *http.Request) {
    log.Println("new request!")
    requestURL, _ := url.Parse(target)
    proxy := httputil.NewSingleHostReverseProxy(requestURL)
    req1, _ := http.NewRequest(req.Method, req.RequestURI, req.Body)
    for k, v := range req.Header {
        for _, vv := range v {
            req1.Header.Add(k, vv)
        }
    }
    req1.Header.Set("Authorization", "Bearer "+"some token")
    req1.Host = requestURL.Host

    proxy.ServeHTTP(res, req1)
}

var requestGroup singleflight.Group
mockBackend := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
    groupKey := req.Host + req.RequestURI
    name := req.Header.Get("From")
    ch := requestGroup.DoChan(groupKey, func() (interface{}, error) {
        //increase key retention to 20s to make sure r1,r2,r3 are all in one group
        go func() {
            time.Sleep(20 * time.Second)
            requestGroup.Forget(groupKey)
            log.Println("Key deleted :", groupKey)
        }()

        // proxy to some host and expect the result to be written in res
        serveReverseProxy("https://somehost.com", res, req)
        return nil, nil
    })

    timeout := time.After(15 * time.Second)

    var result singleflight.Result
    select {
    case <-timeout: // Timeout elapsed, send a timeout message (504)
        log.Println(name, " timed out")
        http.Error(res, "request timed out", http.StatusGatewayTimeout)
        return
    case result = <-ch: // Received result from channel
    }

    if result.Err != nil {
        http.Error(res, result.Err.Error(), http.StatusInternalServerError)
        return
    }

    if result.Shared {
        log.Println(name, " is shared")
    } else {
        log.Println(name, " not shared")
    }
}))

I’d expect r2,r3 to either

  1. at least see the result from their own reponseWriter
  2. timeout along with r1

How to use SingleFlight to share downloaded large size file?
How to use SingleFlight to share downloaded large size file?
test
{$excerpt:n}

Share This Post

Leave a Reply

Your email address will not be Publishedd. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Skip to toolbar