Description

https://gin-gonic.com/docs/examples/graceful-restart-or-stop/

As in the above example, I found that it always timeout.

How to reproduce

package main

import (
    "context"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()
    router.GET("/", func(c *gin.Context) {
        time.Sleep(5 * time.Second)
        c.String(http.StatusOK, "Welcome Gin Server")
    })

    srv := &http.Server{
        Addr:    ":8080",
        Handler: router,
    }

    go func() {
        // service connections
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("listen: %s\n", err)
        }
    }()

    // Wait for interrupt signal to gracefully shutdown the server with
    // a timeout of 5 seconds.
    quit := make(chan os.Signal)
    // kill (no param) default send syscanll.SIGTERM
    // kill -2 is syscall.SIGINT
    // kill -9 is syscall. SIGKILL but can"t be catch, so don't need add it
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    log.Println("Shutdown Server ...")

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    if err := srv.Shutdown(ctx); err != nil {
        log.Fatal("Server Shutdown:", err)
    }
    // catching ctx.Done(). timeout of 5 seconds.
    select {
    case <-ctx.Done():
        log.Println("timeout of 5 seconds.")
    }
    log.Println("Server exiting")
}

Expectations

$ go build -o app   
$ ./app
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
^C2023/01/16 16:22:06 Shutdown Server ...
2023/01/16 16:22:06 Server exiting.    <----------- showdown immediately because it is no active connection

Actual result

$ go build -o app   
$ ./app
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
^C2023/01/16 16:20:47 Shutdown Server ...
2023/01/16 16:20:52 timeout of 5 seconds. <----------- always timeout
2023/01/16 16:20:52 Server exiting

Environment

  • go version: 1.19
  • gin version (or commit ref): v1.8.2
  • operating system: Mac

Comment From: jialechan

        ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
        defer cancel()
        if err := srv.Shutdown(ctx); err != nil {
-               log.Fatal("Server Shutdown:", err)
-       }
-       // catching ctx.Done(). timeout of 5 seconds.
-       select {
-       case <-ctx.Done():
-               log.Println("timeout of 5 seconds.")
+               if err == context.DeadlineExceeded {
+                       log.Println("timeout of 5 seconds.")
+               } else {
+                       log.Fatal("Server Shutdown:", err)
+               }
        }
        log.Println("Server exiting")

It is works for me