Skip to content

Commit

Permalink
add app runner options (manual shutdown, timeout, & quit signal)
Browse files Browse the repository at this point in the history
  • Loading branch information
agungdwiprasetyo committed Jul 1, 2024
1 parent e512eaa commit 5590bae
Showing 1 changed file with 53 additions and 17 deletions.
70 changes: 53 additions & 17 deletions codebase/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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{})
Expand All @@ -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())
}
}

Expand Down

0 comments on commit 5590bae

Please sign in to comment.