Skip to content
Graceful process restarts in Go
Go
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.travis.yml
LICENSE Add 3-clause BSD license Oct 4, 2018
README.md Document that Windows is not supported Feb 13, 2019
child.go
child_test.go Refactor the upgrader into a goroutine Feb 1, 2019
doc.go
dup_file.go Use os.File.SyscallConn from Go 1.12 Jan 11, 2019
dup_file_legacy.go Use os.File.SyscallConn from Go 1.12 Jan 11, 2019
env.go Don't panic on parent connectivity problems during upgrade Oct 17, 2018
env_test.go
fds.go
fds_test.go
go.mod chore(mod): add go.mod Jul 16, 2019
http_example_test.go Add an HTTP server example Oct 4, 2018
parent.go
parent_test.go Refactor the upgrader into a goroutine Feb 1, 2019
process.go
process_test.go
tcp_example_test.go
upgrader.go Return an error if getting the initial working dir failed May 21, 2019
upgrader_test.go

README.md

Graceful process restarts in Go

It is sometimes useful to update the running code and / or configuration of a network service, without disrupting existing connections. Usually, this is achieved by starting a new process, somehow transferring clients to it and then exiting the old process.

There are many ways to implement graceful upgrades. They vary wildly in the trade-offs they make, and how much control they afford the user. This library has the following goals:

  • No old code keeps running after a successful upgrade
  • The new process has a grace period for performing initialisation
  • Crashing during initialisation is OK
  • Only a single upgrade is ever run in parallel

tableflip does not work on Windows.

It's easy to get started:

upg, err := tableflip.New(tableflip.Options{})
if err != nil {
	panic(err)
}
defer upg.Stop()

go func() {
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGHUP)
	for range sig {
		err := upg.Upgrade()
		if err != nil {
			log.Println("Upgrade failed:", err)
			continue
		}

		log.Println("Upgrade succeeded")
	}
}()

ln, err := upg.Fds.Listen("tcp", "localhost:8080")
if err != nil {
	log.Fatalln("Can't listen:", err)
}

var server http.Server
go server.Serve(ln)

if err := upg.Ready(); err != nil {
	panic(err)
}
<-upg.Exit()

time.AfterFunc(30*time.Second, func() {
	os.Exit(1)
})

_ = server.Shutdown(context.Background())
You can’t perform that action at this time.