diff --git a/cmd/web/main.go b/cmd/web/main.go index d4d64b3..461caba 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -5,39 +5,37 @@ import ( "crypto/tls" "errors" "fmt" + "github.com/mikestefanello/pagoda/pkg/handlers" + "github.com/mikestefanello/pagoda/pkg/services" "github.com/mikestefanello/pagoda/pkg/tasks" "log" "net/http" "os" "os/signal" - "sync" - "time" - - "github.com/mikestefanello/pagoda/pkg/handlers" - "github.com/mikestefanello/pagoda/pkg/services" ) func main() { - // Start a new container + // Start a new container. c := services.NewContainer() defer func() { + // Gracefully shutdown all services. if err := c.Shutdown(); err != nil { log.Fatal(err) } }() - // Build the router + // Build the router. if err := handlers.BuildRouter(c); err != nil { log.Fatalf("failed to build the router: %v", err) } - // Register all task queues + // Register all task queues. tasks.Register(c) - // Start the task runner to execute queued tasks + // Start the task runner to execute queued tasks. c.Tasks.Start(context.Background()) - // Start the server + // Start the server. go func() { srv := http.Server{ Addr: fmt.Sprintf("%s:%d", c.Config.HTTP.Hostname, c.Config.HTTP.Port), @@ -63,30 +61,9 @@ func main() { } }() - // Wait for interrupt signal to gracefully shut down the server with a timeout of 10 seconds. + // Wait for interrupt signal to gracefully shut down the web server and task runner. quit := make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt) signal.Notify(quit, os.Kill) <-quit - - // Shutdown both the task runner and web server - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - wg := sync.WaitGroup{} - wg.Add(2) - - go func() { - defer wg.Done() - c.Tasks.Stop(ctx) - }() - - go func() { - defer wg.Done() - if err := c.Web.Shutdown(ctx); err != nil { - log.Fatal(err) - } - }() - - wg.Wait() } diff --git a/config/config.go b/config/config.go index 1a8d7d7..45e6e46 100644 --- a/config/config.go +++ b/config/config.go @@ -63,12 +63,13 @@ type ( // HTTPConfig stores HTTP configuration HTTPConfig struct { - Hostname string - Port uint16 - ReadTimeout time.Duration - WriteTimeout time.Duration - IdleTimeout time.Duration - TLS struct { + Hostname string + Port uint16 + ReadTimeout time.Duration + WriteTimeout time.Duration + IdleTimeout time.Duration + ShutdownTimeout time.Duration + TLS struct { Enabled bool Certificate string Key string @@ -109,6 +110,7 @@ type ( Goroutines int ReleaseAfter time.Duration CleanupInterval time.Duration + ShutdownTimeout time.Duration } // MailConfig stores the mail configuration diff --git a/config/config.yaml b/config/config.yaml index e743a09..9ad6695 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -4,6 +4,7 @@ http: readTimeout: "5s" writeTimeout: "10s" idleTimeout: "2m" + shutdownTimeout: "10s" tls: enabled: false certificate: "" @@ -35,6 +36,7 @@ tasks: goroutines: 1 releaseAfter: "15m" cleanupInterval: "1h" + shutdownTimeout: "10s" mail: hostname: "localhost" diff --git a/pkg/services/container.go b/pkg/services/container.go index fd043f9..972b6f9 100644 --- a/pkg/services/container.go +++ b/pkg/services/container.go @@ -71,15 +71,31 @@ func NewContainer() *Container { return c } -// Shutdown shuts the Container down and disconnects all connections. -// If the task runner was started, cancel the context to shut it down prior to calling this. +// Shutdown gracefully shuts the Container down and disconnects all connections. func (c *Container) Shutdown() error { + // Shutdown the web server. + webCtx, webCancel := context.WithTimeout(context.Background(), c.Config.HTTP.ShutdownTimeout) + defer webCancel() + if err := c.Web.Shutdown(webCtx); err != nil { + return err + } + + // Shutdown the task runner. + taskCtx, taskCancel := context.WithTimeout(context.Background(), c.Config.Tasks.ShutdownTimeout) + defer taskCancel() + c.Tasks.Stop(taskCtx) + + // Shutdown the ORM. if err := c.ORM.Close(); err != nil { return err } + + // Shutdown the database. if err := c.Database.Close(); err != nil { return err } + + // Shutdown the cache. c.Cache.Close() return nil