@@ -39,9 +39,6 @@ var Version string
3939//
4040// [blog post]: https://grafana.com/blog/2024/02/09/how-i-write-http-services-in-go-after-13-years
4141func run (ctx context.Context , w io.Writer , args []string , version string ) error {
42- ctx , cancel := signal .NotifyContext (ctx , syscall .SIGINT , syscall .SIGTERM )
43- defer cancel ()
44-
4542 var port uint
4643 fs := flag .NewFlagSet (args [0 ], flag .ExitOnError )
4744 fs .SetOutput (w )
@@ -50,6 +47,22 @@ func run(ctx context.Context, w io.Writer, args []string, version string) error
5047 return err
5148 }
5249
50+ ctx , cancel := signal .NotifyContext (ctx , syscall .SIGINT , syscall .SIGTERM )
51+
52+ // NOTE: Removed `defer cancel()` since we want to control when to cancel the context
53+ // We'll call it explicitly after server shutdown
54+
55+ // Initialize your resources here, for example:
56+ // - Database connections
57+ // - Message queue clients
58+ // - Cache clients
59+ // - External API clients
60+ // Example:
61+ // db, err := sql.Open(...)
62+ // if err != nil {
63+ // return fmt.Errorf("database init: %w", err)
64+ // }
65+
5366 slog .SetDefault (slog .New (slog .NewJSONHandler (w , nil )))
5467 server := & http.Server {
5568 Addr : fmt .Sprintf (":%d" , port ),
@@ -70,11 +83,44 @@ func run(ctx context.Context, w io.Writer, args []string, version string) error
7083 return err
7184 case <- ctx .Done ():
7285 slog .InfoContext (ctx , "shutting down server" )
73- }
7486
75- ctx , cancel = context .WithTimeout (context .WithoutCancel (ctx ), 10 * time .Second )
76- defer cancel ()
77- return server .Shutdown (ctx )
87+ // Create a new context for shutdown with timeout
88+ ctx , shutdownCancel := context .WithTimeout (context .Background (), 10 * time .Second )
89+ defer shutdownCancel ()
90+
91+ // Shutdown the HTTP server first
92+ if err := server .Shutdown (ctx ); err != nil {
93+ return fmt .Errorf ("server shutdown: %w" , err )
94+ }
95+
96+ // After server is shutdown, cancel the main context to close other resources
97+ cancel ()
98+
99+ // Add cleanup code here, in reverse order of initialization
100+ // Give each cleanup operation its own timeout if needed
101+
102+ // Example cleanup sequence:
103+ // 1. Close application services that depend on other resources
104+ // if err := myService.Shutdown(ctx); err != nil {
105+ // return fmt.Errorf("service shutdown: %w", err)
106+ // }
107+
108+ // 2. Close message queue connections
109+ // if err := mqClient.Close(); err != nil {
110+ // return fmt.Errorf("mq shutdown: %w", err)
111+ // }
112+
113+ // 3. Close cache connections
114+ // if err := cacheClient.Close(); err != nil {
115+ // return fmt.Errorf("cache shutdown: %w", err)
116+ // }
117+
118+ // 4. Close database connections
119+ // if err := db.Close(); err != nil {
120+ // return fmt.Errorf("database shutdown: %w", err)
121+ // }
122+ return nil
123+ }
78124}
79125
80126// route sets up and returns an [http.Handler] for all the server routes.
0 commit comments