0

Implemented a context timeout in Go APIs using

context.WithTimeout(context,time)

Expectation is that if I set it to 10 seconds and if the API happens to run longer, it should stop and return Timeout error. However, if I add a sleep in between , the API still runs and gives 200 output. Is there any solution to this other than manually checking at random points for the time elapsed ? Looks like context.WithTimeout() doesnt solve its purpose.

5
  • Try to share some of your code, as for example the part where you add the sleep and how you are using it. It may help us to help you. Commented Nov 8, 2022 at 12:03
  • sleep can be added anywhere, the point I am trying to make is that the API doesnt timeout as it is supposed to. Sleep is just a way of reproducing a failure scenario. As for the timeout, that is set while initializing the context Commented Nov 8, 2022 at 12:13
  • That's why I mentioned to put some sample of your code. Timeout should happen on client side while the sleep must be on API side, then you can see the timeout on client side happening. Commented Nov 8, 2022 at 12:15
  • Cant put up code here due to contractual limitations but you do make a point about the client. Will test this more in that case and get back, thnks Commented Nov 8, 2022 at 12:21
  • 2
    We're not asking for your actual code. We're asking for a minimal reproducible example. Commented Nov 8, 2022 at 12:24

1 Answer 1

1

So, this is a minimum code to exemplify how the context.WithTimeout can work:

server.go

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    http.HandleFunc("/bad", badResponse)
    http.HandleFunc("/nice", niceResponse)
    
    http.ListenAndServe(":8099", nil)
}

// badResponse is a procedure that takes too much time
func badResponse(w http.ResponseWriter, r *http.Request) {
    fmt.Println("got request, it may take too long to answer")
    time.Sleep(3 * time.Second)
    fmt.Fprintf(w, "bad response!")
}

// niceResponse is a procedure that respond in time
func niceResponse(w http.ResponseWriter, r *http.Request) {
    fmt.Println("got request, will return fast")
    fmt.Fprintf(w, "nice response!")
}

client.go

package main

import (
    "context"
    "io/ioutil"
    "log"
    "net/http"
    "time"
)

func main () {
  req, err := http.NewRequest(http.MethodGet, "http://localhost:8099/bad", nil)
  if err != nil {
    log.Fatal(err)
  }
  ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
  defer cancel()

  req = req.WithContext(ctx)
  c := &http.Client{}
  res, err := c.Do(req)
  if err != nil {
    log.Fatal(err)
  }
  defer res.Body.Close()

  out, err := ioutil.ReadAll(res.Body)
  if err != nil {
    log.Fatal(err)
  }

  log.Println(string(out))
}

In the example above the client must fail because the timeout is lower than the client server delay.

Sign up to request clarification or add additional context in comments.

1 Comment

Sidenote: This is a somewhat special case b/c http.Client.Do() interally honors the timeout set from outside. In general, you need to check for timeout yourself.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.