1

I'm trying to develop a simple API for Slack and I want to return something to the user right away to avoid the 3000 ms timeout.

Here are my questions:

  1. Why the This should be printed to Slack first doesn't get printed right away, instead I only got the last message which is The long and blocking process completed? But it appears in ngrok log though.

  2. Why is my function still reaching the 3000 ms limit even though I'm already using a go routine? Is it because of the done channel?

func testFunc(w http.ResponseWriter, r *http.Request) {
    // Return to the user ASAP to avoid 3000ms timeout.
    // But this doesn't work. Nothing is returned but
    // the message appeared in ngrok log.
    fmt.Fprintln(w, "This should be printed to Slack first!")

    // Get the response URL.
    r.ParseForm()
    responseURL := r.FormValue("response_url")

    done := make(chan bool)

    go func() {
        fmt.Println("Warning! This is a long and blocking process.")
        time.Sleep(5 * time.Second)

        done <- true
    }()

    // This works! I received this message. But I still reached the 3000ms timeout.
    func(d bool) {
        if d == true {
            payload := map[string]string{"text": "The long and blocking process completed!"}
            j, err := json.Marshal(payload)

            if err != nil {
                w.WriteHeader(http.StatusInternalServerError)
            }

            http.Post(responseURL, "application/json", bytes.NewBuffer(j))
        }
    }(<-done)
}
Erik Kalkoken
  • 30,467
  • 8
  • 79
  • 114
devxvda1
  • 442
  • 2
  • 8
  • 18
  • 1
    FYI: there should be a `return` after `w.WriteHeader(http.StatusInternalServerError)` – colm.anseo Feb 20 '19 at 17:19
  • 1
    You are explicitly sleeping in your goroutine for 5 seconds before sending on the channel, which is 5000ms, and 2000ms longer than said timeout. – Gavin Feb 20 '19 at 18:54

2 Answers2

2

http.ResponseWriter streams are buffered by default. If you want data to be sent to a client in realtime (e.g. HTTP SSE), you need to flush the stream after each 'event':

wf, ok := w.(http.Flusher)

if !ok {
    http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
    return
}

fmt.Fprintln(w, "This should be printed to Slack first!")

wf.Flush()

Flushing is expensive - so take advantage of go's buffering. There will always be an implicit flush once your handler finally exits (hence why you saw your output 'late').

colm.anseo
  • 19,337
  • 4
  • 43
  • 52
1

I am learning stage of golang. Here is my understanding: 1. any operations with channel is blocking 2. You are writing on the channel in the

    go func() {
        fmt.Println("Warning! This is a long and blocking process.")
        time.Sleep(5 * time.Second)

        done <- true
    }()
  1. The scheduler is still moving in the main function and trying to read from the channel but it is waiting to see, something has written on the channel or not. So it got blocked, when the above function is done with writing on the channel, controls came back and main start executing again.

Note: Experts will be able to explain better.

Omar Faroque Anik
  • 2,531
  • 1
  • 29
  • 42