Golang Tips & Tricks #3 - graceful shutdown

In the microservices’ world, one thing what’s worth considering is a graceful shutdown. This is important to not lose data while shutting down a container. The container orchestrator like Kubernetes can restart the container by sending SIGTERM or SIGINT signal. Those signals can be handled to safely close all connections and finish background tasks.

Signals are propagated using os.Signal channel. You can add the above code to your main.

var gracefulStop = make(chan os.Signal)
signal.Notify(gracefulStop, syscall.SIGTERM)
signal.Notify(gracefulStop, syscall.SIGINT)

Then, we need a goroutine to handle signals.

go func() {
       sig := <-gracefulStop
       // handle it
       os.Exit(0)
}()

If we serve the HTTP server, the first thing we can do is shutdowning the server.

go func() {
       sig := <-gracefulStop
       server.Shutdown(ctx)
       // handle it
       os.Exit(0)
}()

It will shut down the server without interrupting any active connections. But what about the background tasks? There are, at least, 3 approaches I found which solves the problem.

Wait!

You can add a time.Sleep(2*time.Second) statement and just exit. I personally don’t like the solution because some tasks may need more than X seconds. On the other hand, setting to high sleep time is not a good idea eather. This is definitely the easiest way of doing it.

Use channels

An another way you can achieve the goal is using channels. Here’s how it works: you create two channels. The first one will communicate tell goroutines that it’s time to stop and the second that time’s up and we’re exiting.

var closing = make(chan struct{})
var done = make(chan struct{})

// pass both channels to background processes

go func() {
       sig := <-gracefulStop
       closing <- struct{}
       time.Sleep(2*time.Second)
       done <- struct{}
       os.Exit(0)
}()

Thank’s to this, all background tasks have 2 seconds to finish up their work and then we exit.

Wait groups

The sync package has the WaitGroup. The WaitGroup waits for a collection of goroutines to finish. The idea behind it is to create the WaitGroup in the main file and pass it to background tasks. And after that, we call Wait() function after receiving the closing signal.

wg := sync.WaitGroup{}

// pass both wait group to background channels

go func() {
       sig := <-gracefulStop
       wg.Wait()
       os.Exit(0)
}()

If you kwno other approaches, just leave a message in the comment’s section.

golang   tipstricks
Published in: Golang
Did you like the post?
or

About the author

Hi! My name is Bartek. I'm a developer since 2011. I love to learn by sharing the knowledge. You can find me on Twitter or Linkedin. I'm a backend engineer at Brainly.

Recent posts

I want to help you become a developer

That's why I can let you know about new blogposts on your email

You can find me

You May Also Enjoy

Golang Tips & Tricks #6 - the _test package 14 Aug 2019

Testing is one of the hardest stuff in programming. Today trick will help you organize your tests and the production code. Let's assume you have a package called `orders`. When you want to separat...
golang   tipstricks   tests
read more...

Golang Tips & Tricks #5 - blank identifier in structs 22 Jul 2019

While working with structures, there's a possibility to initialize the structure without providing the keys of fields. ```go type SomeSturct struct { FirstField string SecondField bool } // ....
golang   tipstricks
read more...

Golang Tips & Tricks #4 - internal folders 25 Mar 2019

While developing a library, we create a directory structure to keep the code organized. However, some exported functions or struct should not be used by users of the library. The achieve that, call...
golang   tipstricks
read more...