From 5590bae34442be1febfb5c9aeda46c9268fee782 Mon Sep 17 00:00:00 2001 From: agungdwiprasetyo Date: Mon, 1 Jul 2024 23:51:29 +0700 Subject: [PATCH] add app runner options (manual shutdown, timeout, & quit signal) --- codebase/app/app.go | 70 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/codebase/app/app.go b/codebase/app/app.go index 4390acc..72c0886 100644 --- a/codebase/app/app.go +++ b/codebase/app/app.go @@ -13,29 +13,64 @@ import ( "github.com/golangid/candi/codebase/factory" ) +// Option app option +type Option func(*App) + +// SetManualShutdown set manual shutdown app with app.Shutdown() +func SetManualShutdown() Option { + return func(a *App) { + a.manualShutdown = true + } +} + +// SetShutdownTimeout set timeout for graceful shutdown +func SetShutdownTimeout(shutdownTimeout time.Duration) Option { + return func(a *App) { + a.shutdownTimeout = shutdownTimeout + } +} + +// SetQuitSignalTrigger option +func SetQuitSignalTrigger(quitSignalTriggers []os.Signal) Option { + return func(a *App) { + a.quitSignalTriggers = quitSignalTriggers + } +} + // App service type App struct { - service factory.ServiceFactory + manualShutdown bool + shutdownTimeout time.Duration + quitSignal chan os.Signal + quitSignalTriggers []os.Signal + service factory.ServiceFactory } -// New service app -func New(service factory.ServiceFactory) *App { - return &App{ - service: service, +// New init new service app +func New(service factory.ServiceFactory, opts ...Option) *App { + app := &App{ + service: service, + shutdownTimeout: 1 * time.Minute, + quitSignal: make(chan os.Signal, 1), + quitSignalTriggers: []os.Signal{os.Interrupt, syscall.SIGTERM}, + } + for _, opt := range opts { + opt(app) } + return app } // Run start app func (a *App) Run() { if err := a.checkRequired(); err != nil { - panic(err) + log.Panic(err) } errServe := make(chan error) checkExist := make(map[string]struct{}) for _, app := range a.service.GetApplications() { if _, ok := checkExist[app.Name()]; ok { - panic("Register application: " + app.Name() + " has been registered") + log.Panicf("Register application: %s has been registered", app.Name()) } checkExist[app.Name()] = struct{}{} go func(srv factory.AppServerFactory) { @@ -48,24 +83,25 @@ func (a *App) Run() { }(app) } - quitSignal := make(chan os.Signal, 1) - signal.Notify(quitSignal, os.Interrupt, syscall.SIGTERM) + signal.Notify(a.quitSignal, a.quitSignalTriggers...) log.Printf("Application \x1b[32;1m%s\x1b[0m ready to run\n\n", a.service.Name()) select { case e := <-errServe: - panic(e) - case <-quitSignal: - a.shutdown(quitSignal) + log.Panic(e) + case <-a.quitSignal: + if !a.manualShutdown { + a.Shutdown() + } } } -// graceful shutdown all server, panic if there is still a process running when the request exceed given timeout in context -func (a *App) shutdown(forceShutdown chan os.Signal) { +// Shutdown graceful shutdown all server, panic if there is still a process running when the request exceed given timeout in context +func (a *App) Shutdown() { fmt.Println("\x1b[34;1mGracefully shutdown... (press Ctrl+C again to force)\x1b[0m") - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + ctx, cancel := context.WithTimeout(context.Background(), a.shutdownTimeout) defer cancel() done := make(chan struct{}) @@ -80,11 +116,11 @@ func (a *App) shutdown(forceShutdown chan os.Signal) { select { case <-done: log.Println("\x1b[32;1mSuccess shutdown all server & worker\x1b[0m") - case <-forceShutdown: + case <-a.quitSignal: log.Println("\x1b[31;1mForce shutdown server & worker\x1b[0m") cancel() case <-ctx.Done(): - log.Println("\x1b[31;1mContext timeout\x1b[0m") + log.Printf("\x1b[31;1mShutdown timeout after %s\x1b[0m", a.shutdownTimeout.String()) } }