fix: improve HTTP server startup with better error detection
- Added better error detection for HTTP server startup - Added connectivity check to verify server is actually listening - Increased wait time to 500ms for better error detection - Added warning log if server connectivity check fails (may still be starting) - Improved logging messages for server startup This should help diagnose why the HTTP server isn't starting and provide better visibility into the startup process.
This commit is contained in:
@@ -145,7 +145,7 @@ func ProvideErrorBus() fx.Option {
|
|||||||
func ProvideHealthRegistry() fx.Option {
|
func ProvideHealthRegistry() fx.Option {
|
||||||
return fx.Provide(func(dbClient *database.Client) (*health.Registry, error) {
|
return fx.Provide(func(dbClient *database.Client) (*health.Registry, error) {
|
||||||
registry := health.NewRegistry()
|
registry := health.NewRegistry()
|
||||||
|
|
||||||
// Register database health checker
|
// Register database health checker
|
||||||
registry.Register("database", health.NewDatabaseChecker(dbClient))
|
registry.Register("database", health.NewDatabaseChecker(dbClient))
|
||||||
|
|
||||||
@@ -237,19 +237,54 @@ func ProvideHTTPServer() fx.Option {
|
|||||||
host = "0.0.0.0"
|
host = "0.0.0.0"
|
||||||
}
|
}
|
||||||
addr := fmt.Sprintf("%s:%d", host, port)
|
addr := fmt.Sprintf("%s:%d", host, port)
|
||||||
|
|
||||||
|
log.Info("HTTP server starting",
|
||||||
|
logger.String("addr", addr),
|
||||||
|
)
|
||||||
|
|
||||||
// Start server in a goroutine
|
// Start server in a goroutine
|
||||||
|
// ListenAndServe blocks, so we need to start it async
|
||||||
|
// If there's an immediate error (like port in use), it will return quickly
|
||||||
|
errChan := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
log.Info("HTTP server starting",
|
|
||||||
logger.String("addr", addr),
|
|
||||||
)
|
|
||||||
if err := srv.Start(); err != nil && err != http.ErrServerClosed {
|
if err := srv.Start(); err != nil && err != http.ErrServerClosed {
|
||||||
log.Error("HTTP server error",
|
log.Error("HTTP server failed",
|
||||||
logger.String("error", err.Error()),
|
logger.String("error", err.Error()),
|
||||||
)
|
)
|
||||||
|
select {
|
||||||
|
case errChan <- err:
|
||||||
|
default:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return nil
|
|
||||||
|
// Wait a short time to detect immediate binding errors
|
||||||
|
// If ListenAndServe fails immediately (e.g., port in use), it will return quickly
|
||||||
|
select {
|
||||||
|
case err := <-errChan:
|
||||||
|
return fmt.Errorf("HTTP server failed to start: %w", err)
|
||||||
|
case <-time.After(500 * time.Millisecond):
|
||||||
|
// If no error after 500ms, verify server is actually listening
|
||||||
|
// by attempting a connection
|
||||||
|
client := &http.Client{Timeout: 1 * time.Second}
|
||||||
|
checkURL := fmt.Sprintf("http://localhost:%d/healthz", port)
|
||||||
|
resp, err := client.Get(checkURL)
|
||||||
|
if err != nil {
|
||||||
|
// Server might still be starting, but log the attempt
|
||||||
|
log.Warn("Could not verify HTTP server is listening (may still be starting)",
|
||||||
|
logger.String("url", checkURL),
|
||||||
|
logger.String("error", err.Error()),
|
||||||
|
)
|
||||||
|
// Continue anyway - server might still be starting
|
||||||
|
} else {
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("HTTP server started successfully",
|
||||||
|
logger.String("addr", addr),
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
},
|
},
|
||||||
OnStop: func(ctx context.Context) error {
|
OnStop: func(ctx context.Context) error {
|
||||||
return srv.Shutdown(ctx)
|
return srv.Shutdown(ctx)
|
||||||
|
|||||||
@@ -123,9 +123,23 @@ func registerRoutes(
|
|||||||
|
|
||||||
// Start starts the HTTP server.
|
// Start starts the HTTP server.
|
||||||
func (s *Server) Start() error {
|
func (s *Server) Start() error {
|
||||||
|
// ListenAndServe will block until the server is closed
|
||||||
|
// If there's an immediate error (like port in use), it will return immediately
|
||||||
return s.httpServer.ListenAndServe()
|
return s.httpServer.ListenAndServe()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartAsync starts the HTTP server in a goroutine and returns a channel that signals when it's ready or errored.
|
||||||
|
func (s *Server) StartAsync() <-chan error {
|
||||||
|
errChan := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
if err := s.Start(); err != nil && err != http.ErrServerClosed {
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
close(errChan)
|
||||||
|
}()
|
||||||
|
return errChan
|
||||||
|
}
|
||||||
|
|
||||||
// Shutdown gracefully shuts down the HTTP server.
|
// Shutdown gracefully shuts down the HTTP server.
|
||||||
func (s *Server) Shutdown(ctx context.Context) error {
|
func (s *Server) Shutdown(ctx context.Context) error {
|
||||||
return s.httpServer.Shutdown(ctx)
|
return s.httpServer.Shutdown(ctx)
|
||||||
|
|||||||
Reference in New Issue
Block a user