go get -u github.com/maxbolgarin/contem
contem is a zero-dependency drop-in context.Context
replacement for graceful shutdown. It is lightweight and easy to use: just create a func run(Context) error { ... }
, where you should Add
your shutdown methods to the Context
, and then call Start
function. contem will graceful shutdown and release all added resources with error handling.
- Graceful shutdown: your application will process all incoming requests, close all allocated resources and save data from the buffer before exiting.
- Ctrl+C support: you can catch
Ctrl+C
signals and gracefully shutdown the application out of the box without remebering how to usesignal.Notify
. - Error handling: you should handle
defer db.Close()
errors to prevent from an unexpected behaviour. With contem you justAddClose
your closer instead of writingdefer func() {...}
. - Less code: you cannot use
log.Fatal()
, because it callsos.Exit()
and ignores all defer functions. You should write a shutdown code in everyif err != nil {...}
in the main function. With contem you can exit right after a shutdown by usingExit()
option. - Handle file close: how to close a file when an application stops, if it was opened in the internals of your code? Should you return it right to the main or open at the beginning and propogate throught the app? contem allows you to add a
File
to theContext
, than sync and close it during global shutdown.
You can find some examples here
func main() {
contem.Start(run, slog.Default())
}
func run(ctx contem.Context) error {
srv := http.Server{Addr: ":8080"}
ctx.Add(srv.Shutdown)
// TODO: add some code
return nil
}
You should write an application logic in the run
function. It should be non blocking. It should init application, start wotrkers in a separate goroutines and returns error in case of initialization failure. contem will wait for interrupt signals and then calls Context.Shutdown
. Run function accepts Context
as an argument, so you can add shutdown and cancel methods to it.
Here is a full example: click me
// Step 1. Create context and defer Shutdown
var err error
ctx := contem.New(contem.WithLogger(slog.Default()), contem.Exit(&err))
defer ctx.Shutdown()
// Step 2. Create a server and add server's shutdown method to the context
var server *http.Server
srv, err = server.Start(ctx)
if err != nil {
slog.Error("failed to create server", "error", err)
return
}
ctx.Add(srv.Shutdown)
// Step 3. Wait for the interruption signal
ctx.Wait()
What is going on in this snippet of code?
- Create a
contem.Context
and deferShutdown
:- Pass an error to
Exit
to exit with1
code if there will be errors in future (you should not use:=
because it will reassign the error variable and it will exit with0
code) - Add
slog
as a logger. It will printInfo
message at the start of shutdown andError
message in case of shutdown error. It is useful withExit
, because you won't be able to handle error fromShutdown
in this case.
- Pass an error to
- Create a server and add server's shutdown method to the context. You can return from the main without concerns because you have defered
Shutdown
earlier. - Wait for the interruption signal. It will block code until
SIGTERM
orSIGINT
signal is received. After that it will callShutdown
and exit the application.
If you'd like to contribute to contem, make a fork and submit a pull request. You also can open an issue or text me on Telegram.
This project is licensed under the MIT License. See the LICENSE file for details.