repo move + timeout

This commit is contained in:
Naitik Shah 2014-04-02 11:52:43 -07:00
parent 2d7fb4c06a
commit 0a7f677b4a
6 changed files with 71 additions and 31 deletions

View File

@ -18,10 +18,13 @@ import (
var ( var (
// This error is returned by Inherits() when we're not inheriting any fds. // This error is returned by Inherits() when we're not inheriting any fds.
ErrNotInheriting = errors.New("no inherited listeners") ErrNotInheriting = errors.New("grace: no inherited listeners")
// This error is returned by Listener.Accept() when Close is in progress. // This error is returned by Listener.Accept() when Close is in progress.
ErrAlreadyClosed = errors.New("already closed") ErrAlreadyClosed = errors.New("grace: already closed")
errRestartListeners = errors.New("grace: restart must be given listeners")
errTermTimeout = errors.New("grace: TERM timeout in closing listeners")
// Time in the past to trigger immediate deadline. // Time in the past to trigger immediate deadline.
timeInPast = time.Now() timeInPast = time.Now()
@ -142,35 +145,71 @@ func (l *listener) Accept() (net.Conn, error) {
return &conn{Conn: c, wg: &l.wg}, nil return &conn{Conn: c, wg: &l.wg}, nil
} }
// Process configures the restart process.
type Process struct { type Process struct {
// TermTimeout if set will determine how long we'll wait for listeners when
// we're sent the TERM signal.
TermTimeout time.Duration
}
func (p *Process) term(listeners []Listener) error {
// shutdown all listeners in parallel
errs := make(chan error, len(listeners))
wg := sync.WaitGroup{}
wg.Add(len(listeners))
for _, l := range listeners {
go func(l Listener) {
defer wg.Done()
if err := l.Close(); err != nil {
errs <- err
}
}(l)
}
if p.TermTimeout.Nanoseconds() == 0 {
// no timeout, wait indefinitely
wg.Wait()
} else {
// wait in background to allow for implementing a timeout
done := make(chan struct{})
go func() {
defer close(done)
wg.Wait()
}()
// wait for graceful termination or timeout
select {
case <-done:
case <-time.After(p.TermTimeout):
return errTermTimeout
}
}
// if any errors occurred, return the first one
if len(errs) > 0 {
return <-errs
}
return nil
} }
// Wait for signals to gracefully terminate or restart the process. // Wait for signals to gracefully terminate or restart the process.
func (p *Process) Wait(listeners []Listener) (err error) { func (p *Process) Wait(listeners []Listener) error {
ch := make(chan os.Signal, 2) ch := make(chan os.Signal, 2)
signal.Notify(ch, syscall.SIGTERM, syscall.SIGUSR2) signal.Notify(ch, syscall.SIGTERM, syscall.SIGUSR2)
for { for {
sig := <-ch sig := <-ch
switch sig { switch sig {
case syscall.SIGTERM: case syscall.SIGTERM:
// this ensures a subsequent TERM will trigger standard go behaviour of
// terminating.
signal.Stop(ch) signal.Stop(ch)
var wg sync.WaitGroup return p.term(listeners)
wg.Add(len(listeners))
for _, l := range listeners {
go func(l Listener) {
defer wg.Done()
cErr := l.Close()
if cErr != nil {
err = cErr
}
}(l)
}
wg.Wait()
return
case syscall.SIGUSR2: case syscall.SIGUSR2:
rErr := Restart(listeners) // we only return here if there's an error, otherwise the new process
if rErr != nil { // will send us a TERM when it's ready to trigger the actual shutdown.
return rErr if err := p.Restart(listeners); err != nil {
return err
} }
} }
} }
@ -213,7 +252,7 @@ func (p *Process) CloseParent() error {
// Restart the process passing the given listeners to the new process. // Restart the process passing the given listeners to the new process.
func (p *Process) Restart(listeners []Listener) (err error) { func (p *Process) Restart(listeners []Listener) (err error) {
if len(listeners) == 0 { if len(listeners) == 0 {
return errors.New("restart must be given listeners.") return errRestartListeners
} }
// Extract the fds from the listeners. // Extract the fds from the listeners.

View File

@ -5,10 +5,11 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"github.com/ParsePlatform/go.grace/gracehttp"
"net/http" "net/http"
"os" "os"
"time" "time"
"github.com/facebookgo/grace/gracehttp"
) )
var ( var (

View File

@ -13,7 +13,7 @@ import (
"net/http" "net/http"
"os" "os"
"github.com/ParsePlatform/go.grace" "github.com/facebookgo/grace"
) )
var ( var (

View File

@ -16,15 +16,15 @@ import (
"testing" "testing"
"time" "time"
"github.com/ParsePlatform/go.freeport" "github.com/facebookgo/freeport"
"github.com/ParsePlatform/go.tool" "github.com/facebookgo/tool"
) )
var ( var (
// Debug logging. // Debug logging.
debugLog = flag.Bool("debug", false, "enable debug logging") debugLog = flag.Bool("debug", false, "enable debug logging")
testserverCommand = &tool.CommandBuild{ testserverCommand = &tool.CommandBuild{
ImportPath: "github.com/ParsePlatform/go.grace/gracehttp/testserver", ImportPath: "github.com/facebookgo/grace/gracehttp/testserver",
} }
) )

View File

@ -13,7 +13,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/ParsePlatform/go.grace/gracehttp" "github.com/facebookgo/grace/gracehttp"
) )
type response struct { type response struct {

View File

@ -1,4 +1,4 @@
go.grace [![Build Status](https://secure.travis-ci.org/ParsePlatform/go.grace.png)](http://travis-ci.org/ParsePlatform/go.grace) go.grace [![Build Status](https://secure.travis-ci.org/facebookgo/grace.png)](http://travis-ci.org/facebookgo/grace)
======== ========
Package grace provides a library that makes it easy to build socket Package grace provides a library that makes it easy to build socket
@ -17,11 +17,11 @@ Usage
----- -----
Demo HTTP Server with graceful termination and restart: Demo HTTP Server with graceful termination and restart:
https://github.com/ParsePlatform/go.grace/blob/master/gracedemo/demo.go https://github.com/facebookgo/grace/blob/master/gracedemo/demo.go
1. Install the demo application 1. Install the demo application
go get github.com/ParsePlatform/go.grace/gracedemo go get github.com/facebookgo/grace/gracedemo
1. Start it in the first terminal 1. Start it in the first terminal
@ -55,7 +55,7 @@ Documentation
------------- -------------
`http.Server` graceful termination and restart: `http.Server` graceful termination and restart:
http://godoc.org/github.com/ParsePlatform/go.grace/gracehttp http://godoc.org/github.com/facebookgo/grace/gracehttp
`net.Listener` graceful termination and restart: `net.Listener` graceful termination and restart:
http://godoc.org/github.com/ParsePlatform/go.grace http://godoc.org/github.com/facebookgo/grace