srvlifecycle

srvlifecycle runs a network server in the background and shuts it down gracefully on a signal, on context cancelation, or on an explicit call.

Part of nurago, a collection of independent Go packages for backend services.

import "github.com/tecnickcom/nurago/pkg/srvlifecycle"

The start and shutdown state machine shared by httpserver and grpcserver, exported for any other network server.

life, err := srvlifecycle.New(srv, listener,
    srvlifecycle.WithName("admin"),
    srvlifecycle.WithShutdownTimeout(5*time.Second),
    srvlifecycle.WithShutdownWaitGroup(wg),
    srvlifecycle.WithShutdownSignalChan(sc),
)

life.Start(ctx)

Any server with Serve and Shutdown

A Server needs Serve(net.Listener) error and Shutdown(context.Context) error. *http.Server satisfies it as is; a gRPC server, an SMTP server or a custom TCP protocol needs a few lines of adapter.

The point is that a custom server then stops exactly like the built-in ones, on the same wait group and signal channel, without reimplementing the corner cases below.

The corner cases it handles

  • Start runs once. Later calls, and calls after a shutdown, are ignored, and the wait group is incremented before Start returns, so a Wait right after it cannot miss the server.
  • Shutdown is idempotent and concurrent-safe. The wait group is decremented exactly once, and only for a server that was started.
  • An unstarted listener is closed. Only Serve takes ownership of the listener, so shutting down a server that never started would otherwise leak the socket.
  • A failed Serve triggers the shutdown. Any error that does not denote a normal stop is published on ServeError and starts the shutdown, so the application is not left waiting on a server that has already died.

WithServerClosedFunc decides which Serve errors count as a normal stop, since http.ErrServerClosed is specific to net/http.

Three triggers, one shutdown

The shutdown starts on a value received on the signal channel, the channel being closed, the context passed to Start being canceled, or a direct Shutdown call. The first two run it under a context bounded by WithShutdownTimeout.

Close the signal channel rather than sending a value when several servers share it: a send wakes one receiver, a close wakes all of them.

Done is closed once the first shutdown has released the wait group or the listener.

Dependencies

This package reaches no external module: it uses only the Go standard library. make depcheck fails the build if it ever does.