| 545 | // Check if there are active connections |
| 546 | activeConns := atomic.LoadInt64(&s.activeConns) |
| 547 | if activeConns > 0 { |
| 548 | slog.Info("Waiting for active connections to finish.", "count", activeConns) |
| 549 | } |
| 550 | |
| 551 | // If process isolation is enabled, signal children to terminate |
| 552 | if s.cfg.ProcessIsolation && s.childTracker != nil { |
| 553 | childCount := s.childTracker.Count() |
| 554 | if childCount > 0 { |
| 555 | slog.Info("Signaling child processes to terminate.", "count", childCount) |
| 556 | s.childTracker.SignalAll(syscall.SIGTERM) |
| 557 | } |
| 558 | } |
| 559 | |
| 560 | // Wait for connections with timeout |
| 561 | done := make(chan struct{}) |
| 562 | go func() { |
| 563 | s.wg.Wait() |
| 564 | // Also wait for child processes if isolation is enabled |
| 565 | if s.cfg.ProcessIsolation && s.childTracker != nil { |
| 566 | <-s.childTracker.WaitAll() |
| 567 | } |
| 568 | close(done) |
| 569 | }() |
| 570 | |
| 571 | select { |
| 572 | case <-done: |
| 573 | slog.Info("All connections closed gracefully.") |
| 574 | case <-time.After(s.cfg.ShutdownTimeout): |
| 575 | slog.Warn("Shutdown timeout exceeded, force closing remaining connections.", "timeout", s.cfg.ShutdownTimeout) |
| 576 | // Force kill remaining children |
| 577 | if s.cfg.ProcessIsolation && s.childTracker != nil { |
| 578 | s.childTracker.SignalAll(syscall.SIGKILL) |
| 579 | } |
| 580 | } |
| 581 | |
| 582 | // Shut down ACME managers if active |
| 583 | if s.acmeManager != nil { |
| 584 | if err := s.acmeManager.Close(); err != nil { |
| 585 | slog.Warn("ACME manager shutdown error.", "error", err) |
| 586 | } |
| 587 | } |
| 588 | if s.acmeDNSManager != nil { |
| 589 | if err := s.acmeDNSManager.Close(); err != nil { |
| 590 | slog.Warn("ACME DNS manager shutdown error.", "error", err) |
| 591 | } |
| 592 | } |
| 593 | |
| 594 | // Stop query logger (drains remaining entries) |
| 595 | queryLogTimeout := s.cfg.ShutdownTimeout |
| 596 | if queryLogTimeout <= 0 { |
| 597 | queryLogTimeout = 30 * time.Second |
| 598 | } |
| 599 | queryLogCtx, queryLogCancel := context.WithTimeout(context.Background(), queryLogTimeout) |
| 600 | if err := s.StopQueryLogging(queryLogCtx); err != nil { |
| 601 | slog.Warn("Query log shutdown deadline exceeded.", "error", err) |
| 602 | } |
| 603 | queryLogCancel() |
| 604 | |