-
Notifications
You must be signed in to change notification settings - Fork 0
Description
There is a question of what should happen when an action is added after Shutdown has been called or a signal has been received.
Example scenario:
A microservice is starting up and initialising databases, HTTP/gRPC servers, etc. Part way through this initialising process an interrupt signal is received.
Current approach:
Once the interrupt signal has been received all the actions that have been added will be called in the prescribed order. Actions added after this will be ignored. It is this way because the order is usually set to first in, last done (first instantiated, last torn down) and there is no way to ensure this order is followed after the signal has been received meaning it will likely not be graceful anyway.
There are two different situations to be considered:
- An action is added while the shutdown actions previously added are being performed.
- An action is added after all the shutdown actions previously added have been performed.
A few different questions come to mind - the answer may differ depending on the situation being considered.
- Should the actions be performed or not?
- Should the actions be performed immediately or added to a queue of other actions being performed while following the order as closely as possible.
- Should adding the actions be a blocking method which waits until the actions have been performed.
- Should this behaviour be fixed or an option?
- Does this matter?
This example scenario can be demonstrated using the main below. In this case the main will never finish.
package main
import (
"context"
"fmt"
"os"
"syscall"
"time"
"github.com/Graphmasters/safedown"
)
func main() {
sa := safedown.NewShutdownActions(safedown.FirstInLastDone, syscall.SIGTERM)
// Pretend database
ctxA, cancelA := context.WithCancel(context.Background())
sa.AddActions(cancelA)
// This mimics the service receiving a termination signal
process := os.Process{Pid: os.Getpid()}
if err := process.Signal(syscall.SIGTERM); err != nil {
fmt.Printf("error sending signal: %s", err)
}
time.Sleep(time.Second) // Time to ensure the signal reached safedown and shutdown was completed.
// Pretend HTTP server
ctxB, cancelB := context.WithCancel(context.Background())
sa.AddActions(cancelB)
<-ctxB.Done() // This waits for the pretend HTTP server to be closed
<-ctxA.Done() // This waits for the pretend database to be close
}