package gracefulshutdown import ( "context" "fmt" "os" "os/signal" "sync" "syscall" "time" log "github.com/sirupsen/logrus" "github.com/vulcanize/ipld-ethcl-indexer/pkg/loghelper" ) // operation is a clean up function on shutting down type Operation func(ctx context.Context) error // gracefulShutdown waits for termination syscalls and doing clean up operations after received it func Shutdown(ctx context.Context, timeout time.Duration, ops map[string]Operation) (<-chan struct{}, <-chan error) { waitCh := make(chan struct{}) errCh := make(chan error) go func() { s := make(chan os.Signal, 1) // add any other syscalls that you want to be notified with signal.Notify(s, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) <-s log.Info("Shutting Down your application") // set timeout for the ops to be done to prevent system hang timeoutFunc := time.AfterFunc(timeout, func() { log.Warnf("timeout %d ms has been elapsed, force exit", timeout.Milliseconds()) errCh <- fmt.Errorf("Application shutdown took too long.") return }) defer timeoutFunc.Stop() var wg sync.WaitGroup // Do the operations asynchronously to save time for key, op := range ops { wg.Add(1) innerOp := op innerKey := key go func() { defer wg.Done() log.Infof("cleaning up: %s", innerKey) if err := innerOp(ctx); err != nil { loghelper.LogError(err).Errorf("%s: clean up failed: %s", innerKey, err.Error()) return } log.Infof("%s was shutdown gracefully", innerKey) }() } wg.Wait() close(waitCh) }() return waitCh, errCh }