From ac2b458990ebf417b57ec902b6296217cee26240 Mon Sep 17 00:00:00 2001 From: Den Date: Sun, 21 Jul 2024 02:50:43 +0300 Subject: [PATCH] graceful shutdown implemented --- cmd/server/main.go | 16 +++------ internal/server/server.go | 71 +++++++++++++++++++++++++-------------- 2 files changed, 50 insertions(+), 37 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 3902c06..8d72b26 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -10,20 +10,12 @@ import ( func main() { cfg := config.MustLoad() - log := prettylog.SetupLogger(cfg.Env) - log.Info("starting librakeeper srv", slog.String("env", cfg.Env), slog.Int("port", cfg.Server.Port)) - - // Create and initialize the srv - srv := server.NewServer(cfg, log) - if err := srv.Initialize(); err != nil { - log.Error("failed to initialize srv", slog.Any("error", err)) - os.Exit(1) - } + log.Info("starting librakeeper server", slog.String("env", cfg.Env), slog.Int("port", cfg.Server.Port)) - // Run the srv - if err := srv.Run(); err != nil { - log.Error("failed to run srv", slog.Any("error", err)) + // Create and run the server + if err := server.NewServer(cfg, log).Run(); err != nil { + log.Error("server error", slog.Any("error", err)) os.Exit(1) } } diff --git a/internal/server/server.go b/internal/server/server.go index c8b1901..2d350ca 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -38,12 +38,51 @@ func NewServer(config *config.Config, log *slog.Logger) *Server { return &Server{ config: config, log: log, - router: gin.New(), } } -// Initialize initializes the server components. -func (s *Server) Initialize() error { +// Run initializes and starts the HTTP server and handles graceful shutdown. +func (s *Server) Run() error { + if err := s.initialize(); err != nil { + return fmt.Errorf("failed to initialize server: %w", err) + } + + // Graceful Shutdown + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + go func() { + quit := make(chan os.Signal, 1) + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + s.log.Info("received shutdown signal") + cancel() + }() + + return s.runHTTPServer(ctx) +} + +func (s *Server) runHTTPServer(ctx context.Context) error { + go func() { + if err := s.httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + s.log.Error("failed to start server", slog.Any("error", err)) + } + }() + + s.log.Info("server started successfully, press Ctrl+C to stop") + + <-ctx.Done() // Block until the context is canceled (shutdown signal) + + s.log.Info("shutting down server...") + + shutdownCtx, done := context.WithTimeout(ctx, 5*time.Second) + defer done() + + return s.httpServer.Shutdown(shutdownCtx) // Graceful shutdown +} + +// initialize initializes the server components. +func (s *Server) initialize() error { // Initialize Firebase err := auth.InitializeFirebase(s.config.Auth.ConfigPath) if err != nil { @@ -60,7 +99,7 @@ func (s *Server) Initialize() error { grpc.WithTransportCredentials(insecure.NewCredentials()), ) if err != nil { - panic(err) + return fmt.Errorf("failed to connect to gRPC server: %w", err) } bookRepo := mongo.NewBookRepo(db, s.log, "user_books") @@ -80,11 +119,13 @@ func (s *Server) Initialize() error { // Configure CORS corsConfig := cors.Config{ - AllowOrigins: s.config.Server.AllowedOrigins, // Get origins from config + AllowOrigins: s.config.Server.AllowedOrigins, AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowHeaders: []string{"Origin", "Content-Type", "Authorization"}, AllowCredentials: true, } + + s.router = gin.New() s.router.Use(gin.Logger(), gin.Recovery(), cors.New(corsConfig)) routes.SetupRoutes(s.router, h) @@ -95,23 +136,3 @@ func (s *Server) Initialize() error { return nil } - -// Run starts the HTTP server and handles graceful shutdown. -func (s *Server) Run() error { - go func() { - if err := s.httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { - s.log.Error("failed to start server", slog.Any("error", err)) - } - }() - - quit := make(chan os.Signal, 1) - signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) - <-quit - - s.log.Info("shutting down server...") - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - return s.httpServer.Shutdown(ctx) -}