7

How do I cancel further processing if the connection is closed before 10 seconds?

There is c.Request.Context().Done() but coudn't find an example on how to use it.

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        time.Sleep(10 * time.Second) // or some db operation
        log.Print("Processing")
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run()
}
blackgreen
  • 34,072
  • 23
  • 111
  • 129
iamvinitk
  • 165
  • 3
  • 15

1 Answers1

7

You can run the long-running operation asynchronously and have it send on a channel to signal completion.

Then you block on that completion channel and c.Request.Context().Done() with a select statement:

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        signal := make(chan struct{}, 1)

        go longRunningOperation(signal)

        select {
            case <-signal:
                close(signal) // remember to clean up after yourself
                // move on, will print "Processing"
    
            case <-c.Request.Context().Done():
                // abort
                return
        }

        log.Print("Processing")
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run()
}


func longRunningOperation(signal chan<- struct{}) {
    time.Sleep(10 * time.Second)
    signal <- struct{}{} // signal that this operation has finished
}

The disadvantage with this approach is that, as-is, the long-running operation itself would keep executing.

Goroutines exit when the main function of your program returns, which is not the case in an actual gin server. So this might not be what you want.

In the case of a database operation, most APIs require a context.Context parameter, which can be used to detect request cancellation. So you could pass the c.Request.Context() down the call chain to make sure the async long-running operation also terminates when the client disconnects.

func Handler(c *gin.Context) {
    signal := make(chan struct{}, 1)
    go longRunningOperation(c.Request.Context(), signal)
    ...
}

func longRunningOperation(ctx context.Context, signal chan<- struct{}) {
    if err := doSomethingContext(ctx); err != nil {
        return
    }
    signal <- struct{}{} // signal that this operation has finished (successfully)
}
blackgreen
  • 34,072
  • 23
  • 111
  • 129