Graceful fixes (#8645)

* Only attempt to kill parent once

* Apply suggestions from code review

Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com>

* Add waitgroup for running servers
This commit is contained in:
zeripath 2019-10-23 16:32:19 +01:00 committed by GitHub
parent 7d1a7c05db
commit f067e12859
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 35 additions and 7 deletions

View file

@ -13,6 +13,7 @@ import (
"os" "os"
"strings" "strings"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/routers" "code.gitea.io/gitea/routers"
@ -226,6 +227,7 @@ func runWeb(ctx *cli.Context) error {
log.Critical("Failed to start server: %v", err) log.Critical("Failed to start server: %v", err)
} }
log.Info("HTTP Listener: %s Closed", listenAddr) log.Info("HTTP Listener: %s Closed", listenAddr)
graceful.WaitForServers()
log.Close() log.Close()
return nil return nil
} }

View file

@ -9,3 +9,8 @@ package graceful
// This file contains shims for windows builds // This file contains shims for windows builds
const IsChild = false const IsChild = false
// WaitForServers waits for all running servers to finish
func WaitForServers() {
}

View file

@ -12,8 +12,24 @@ import (
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"sync"
"syscall"
) )
var killParent sync.Once
// KillParent sends the kill signal to the parent process if we are a child
func KillParent() {
killParent.Do(func() {
if IsChild {
ppid := syscall.Getppid()
if ppid > 1 {
_ = syscall.Kill(ppid, syscall.SIGTERM)
}
}
})
}
// RestartProcess starts a new process passing it the active listeners. It // RestartProcess starts a new process passing it the active listeners. It
// doesn't fork, but starts a new process using the same environment and // doesn't fork, but starts a new process using the same environment and
// arguments as when it was originally started. This allows for a newly // arguments as when it was originally started. This allows for a newly

View file

@ -31,6 +31,7 @@ const (
var ( var (
// RWMutex for when adding servers or shutting down // RWMutex for when adding servers or shutting down
runningServerReg sync.RWMutex runningServerReg sync.RWMutex
runningServerWG sync.WaitGroup
// ensure we only fork once // ensure we only fork once
runningServersForked bool runningServersForked bool
@ -47,6 +48,7 @@ var (
func init() { func init() {
runningServerReg = sync.RWMutex{} runningServerReg = sync.RWMutex{}
runningServerWG = sync.WaitGroup{}
DefaultMaxHeaderBytes = 0 // use http.DefaultMaxHeaderBytes - which currently is 1 << 20 (1MB) DefaultMaxHeaderBytes = 0 // use http.DefaultMaxHeaderBytes - which currently is 1 << 20 (1MB)
} }
@ -69,6 +71,11 @@ type Server struct {
OnShutdown func() OnShutdown func()
} }
// WaitForServers waits for all running servers to finish
func WaitForServers() {
runningServerWG.Wait()
}
// NewServer creates a server on network at provided address // NewServer creates a server on network at provided address
func NewServer(network, address string) *Server { func NewServer(network, address string) *Server {
runningServerReg.Lock() runningServerReg.Lock()
@ -110,9 +117,7 @@ func (srv *Server) ListenAndServe(serve ServeFunction) error {
srv.listener = newWrappedListener(l, srv) srv.listener = newWrappedListener(l, srv)
if IsChild { KillParent()
_ = syscall.Kill(syscall.Getppid(), syscall.SIGTERM)
}
srv.BeforeBegin(srv.network, srv.address) srv.BeforeBegin(srv.network, srv.address)
@ -156,9 +161,7 @@ func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFun
wl := newWrappedListener(l, srv) wl := newWrappedListener(l, srv)
srv.listener = tls.NewListener(wl, tlsConfig) srv.listener = tls.NewListener(wl, tlsConfig)
if IsChild { KillParent()
_ = syscall.Kill(syscall.Getppid(), syscall.SIGTERM)
}
srv.BeforeBegin(srv.network, srv.address) srv.BeforeBegin(srv.network, srv.address)
return srv.Serve(serve) return srv.Serve(serve)
@ -175,10 +178,12 @@ func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFun
func (srv *Server) Serve(serve ServeFunction) error { func (srv *Server) Serve(serve ServeFunction) error {
defer log.Debug("Serve() returning... (PID: %d)", syscall.Getpid()) defer log.Debug("Serve() returning... (PID: %d)", syscall.Getpid())
srv.setState(stateRunning) srv.setState(stateRunning)
runningServerWG.Add(1)
err := serve(srv.listener) err := serve(srv.listener)
log.Debug("Waiting for connections to finish... (PID: %d)", syscall.Getpid()) log.Debug("Waiting for connections to finish... (PID: %d)", syscall.Getpid())
srv.wg.Wait() srv.wg.Wait()
srv.setState(stateTerminate) srv.setState(stateTerminate)
runningServerWG.Done()
// use of closed means that the listeners are closed - i.e. we should be shutting down - return nil // use of closed means that the listeners are closed - i.e. we should be shutting down - return nil
if err != nil && strings.Contains(err.Error(), "use of closed") { if err != nil && strings.Contains(err.Error(), "use of closed") {
return nil return nil

View file

@ -48,7 +48,7 @@ func (srv *Server) handleSignals() {
if setting.GracefulRestartable { if setting.GracefulRestartable {
log.Info("PID: %d. Received SIGHUP. Forking...", pid) log.Info("PID: %d. Received SIGHUP. Forking...", pid)
err := srv.fork() err := srv.fork()
if err != nil { if err != nil && err.Error() != "another process already forked. Ignoring this one" {
log.Error("Error whilst forking from PID: %d : %v", pid, err) log.Error("Error whilst forking from PID: %d : %v", pid, err)
} }
} else { } else {