-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
144 lines (121 loc) · 3.4 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package main
import (
"context"
"database/sql"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/putdotio/pas/internal/analytics"
"github.com/putdotio/pas/internal/config"
"github.com/putdotio/pas/internal/handler"
"github.com/rs/cors"
"golang.org/x/sync/errgroup"
)
var (
version = "v0.0.0"
)
var (
versionFlag = flag.Bool("version", false, "version")
configPath = flag.String("config", "config.toml", "config file path")
)
func main() {
flag.Parse()
if *versionFlag {
fmt.Println(version)
return
}
log.Printf("Starting PAS version: %s\n", version)
config, err := config.NewConfig(*configPath)
if err != nil {
log.Fatal(err)
}
db, err := sql.Open("mysql", config.MySQLDSN)
if err != nil {
log.Fatal(err)
}
defer func() {
err = db.Close()
if err != nil {
log.Fatal(err)
}
}()
// main server
analytics := analytics.New(db, config.Secret, config.User, config.Events)
handler := handler.New(analytics)
server := http.Server{
Addr: config.ListenAddress,
Handler: cors.Default().Handler(handler),
}
// metrics server
metricsMux := http.NewServeMux()
metricsMux.Handle("/metrics", promhttp.Handler())
metricsServer := http.Server{
Addr: config.ListenAddressForMetrics,
Handler: metricsMux,
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Create root errgroup with cancellable context
g, ctx := errgroup.WithContext(ctx)
// Run main server
g.Go(func() error {
log.Printf("Starting HTTP server on %v", server.Addr)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return fmt.Errorf("main server error: %w", err)
}
return nil
})
// Run metrics server
g.Go(func() error {
log.Printf("Starting metrics server on %v", metricsServer.Addr)
if err := metricsServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return fmt.Errorf("metrics server error: %w", err)
}
return nil
})
// Handle shutdown signal
g.Go(func() error {
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
select {
case <-stop:
log.Println("Shutdown signal received")
cancel() // Cancel context to trigger shutdown
case <-ctx.Done():
// Context was cancelled by server error
}
return nil
})
// Handle context cancellation and server shutdown
g.Go(func() error {
<-ctx.Done() // Wait for cancellation (either from signal or error)
shutdownTimeout := time.Duration(config.ShutdownTimeout) * time.Millisecond
// Shutdown main server with its own timeout
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
log.Printf("Shutting down HTTP server with timeout: %v", shutdownTimeout)
if err := server.Shutdown(shutdownCtx); err != nil {
log.Printf("Main server shutdown error: %v", err)
}
cancel()
// Shutdown metrics server with fresh timeout
shutdownCtx, cancel = context.WithTimeout(context.Background(), shutdownTimeout)
log.Printf("Shutting down metrics server with timeout: %v", shutdownTimeout)
if err := metricsServer.Shutdown(shutdownCtx); err != nil {
log.Printf("Metrics server shutdown error: %v", err)
}
cancel()
return nil
})
// Wait for all goroutines to complete or for an error
if err := g.Wait(); err != nil {
log.Printf("Server error: %v", err)
os.Exit(1)
}
log.Println("Servers stopped")
}