package main import ( "context" "cpu-benchmark-server/lib/config" "cpu-benchmark-server/lib/store" "cpu-benchmark-server/lib/web" "errors" "log" "net/http" "os" "os/signal" "sync" "syscall" "time" ) func main() { logger := log.New(os.Stdout, "", log.LstdFlags|log.LUTC) if err := run(logger); err != nil { logger.Printf("server error: %v", err) os.Exit(1) } } func run(logger *log.Logger) error { cfg := config.Load() if err := os.MkdirAll(cfg.BadgerDir, 0o755); err != nil { return err } benchmarkStore, err := store.Open(cfg.BadgerDir) if err != nil { return err } var closeOnce sync.Once closeStore := func() { if err := benchmarkStore.Close(); err != nil { logger.Printf("close store: %v", err) } } defer closeOnce.Do(closeStore) app, err := web.New(benchmarkStore, cfg.PageSize) if err != nil { return err } server := &http.Server{ Addr: cfg.Addr, Handler: app.Routes(), ReadHeaderTimeout: 5 * time.Second, ReadTimeout: 15 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 60 * time.Second, } ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() go func() { <-ctx.Done() logger.Printf("shutdown signal received") shutdownCtx, cancel := context.WithTimeout(context.Background(), cfg.ShutdownTimeout) defer cancel() if err := server.Shutdown(shutdownCtx); err != nil { logger.Printf("http shutdown: %v", err) } closeOnce.Do(closeStore) }() logger.Printf("listening on %s", cfg.Addr) err = server.ListenAndServe() closeOnce.Do(closeStore) if err != nil && !errors.Is(err, http.ErrServerClosed) { return err } return nil }