cc-metric-store/cmd/cc-metric-store/main.go

180 lines
4.9 KiB
Go
Raw Normal View History

// Copyright (C) NHR@FAU, University Erlangen-Nuremberg.
// All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2021-06-09 06:03:31 +02:00
package main
import (
2021-09-08 09:08:51 +02:00
"context"
"crypto/tls"
2021-12-15 10:58:03 +01:00
"flag"
2024-05-06 15:10:45 +02:00
"fmt"
2021-06-09 06:03:31 +02:00
"log"
"net"
"net/http"
2021-06-09 06:03:31 +02:00
"os"
2021-08-20 11:45:34 +02:00
"os/signal"
2022-03-09 14:21:03 +01:00
"runtime"
"runtime/debug"
2021-08-20 12:54:11 +02:00
"sync"
2021-08-20 11:45:34 +02:00
"syscall"
"time"
2022-02-21 10:00:29 +01:00
2024-05-03 21:08:01 +02:00
"github.com/ClusterCockpit/cc-metric-store/internal/api"
"github.com/ClusterCockpit/cc-metric-store/internal/config"
"github.com/ClusterCockpit/cc-metric-store/internal/memorystore"
"github.com/ClusterCockpit/cc-metric-store/internal/runtimeEnv"
2022-02-21 10:00:29 +01:00
"github.com/google/gops/agent"
httpSwagger "github.com/swaggo/http-swagger"
2021-06-09 06:03:31 +02:00
)
2024-05-06 15:10:45 +02:00
var (
date string
commit string
version string
)
2021-08-20 11:45:34 +02:00
func main() {
2021-12-15 10:58:03 +01:00
var configFile string
var enableGopsAgent, flagVersion, flagDev bool
2021-12-15 10:58:03 +01:00
flag.StringVar(&configFile, "config", "./config.json", "configuration file")
2022-02-21 10:00:29 +01:00
flag.BoolVar(&enableGopsAgent, "gops", false, "Listen via github.com/google/gops/agent")
flag.BoolVar(&flagDev, "dev", false, "Enable development Swagger UI component")
2024-05-06 15:10:45 +02:00
flag.BoolVar(&flagVersion, "version", false, "Show version information and exit")
2021-12-15 10:58:03 +01:00
flag.Parse()
2024-05-06 15:10:45 +02:00
if flagVersion {
fmt.Printf("Version:\t%s\n", version)
fmt.Printf("Git hash:\t%s\n", commit)
fmt.Printf("Build time:\t%s\n", date)
os.Exit(0)
}
2022-04-01 14:01:43 +02:00
startupTime := time.Now()
2024-05-06 14:20:43 +02:00
config.Init(configFile)
memorystore.Init(config.Keys.Metrics)
ms := memorystore.GetMemoryStore()
2022-04-01 14:01:43 +02:00
2024-05-06 14:20:43 +02:00
if enableGopsAgent || config.Keys.Debug.EnableGops {
2022-02-21 10:00:29 +01:00
if err := agent.Listen(agent.Options{}); err != nil {
log.Fatal(err)
}
}
2024-05-06 14:20:43 +02:00
d, err := time.ParseDuration(config.Keys.Checkpoints.Restore)
if err != nil {
log.Fatal(err)
}
restoreFrom := startupTime.Add(-d)
log.Printf("Loading checkpoints newer than %s\n", restoreFrom.Format(time.RFC3339))
2024-05-06 14:20:43 +02:00
files, err := ms.FromCheckpoint(config.Keys.Checkpoints.RootDir, restoreFrom.Unix())
loadedData := ms.SizeInBytes() / 1024 / 1024 // In MB
if err != nil {
log.Fatalf("Loading checkpoints failed: %s\n", err.Error())
} else {
2022-03-24 10:31:11 +01:00
log.Printf("Checkpoints loaded (%d files, %d MB, that took %fs)\n", files, loadedData, time.Since(startupTime).Seconds())
2021-06-09 06:03:31 +02:00
}
2022-03-24 10:31:11 +01:00
// Try to use less memory by forcing a GC run here and then
// lowering the target percentage. The default of 100 means
// that only once the ratio of new allocations execeds the
// previously active heap, a GC is triggered.
// Forcing a GC here will set the "previously active heap"
// to a minumum.
2022-03-09 14:21:03 +01:00
runtime.GC()
2022-03-24 10:31:11 +01:00
if loadedData > 1000 && os.Getenv("GOGC") == "" {
debug.SetGCPercent(10)
}
2021-09-08 09:08:51 +02:00
ctx, shutdown := context.WithCancel(context.Background())
var wg sync.WaitGroup
2024-05-06 14:20:43 +02:00
wg.Add(3)
memorystore.Retention(&wg, ctx)
memorystore.Checkpointing(&wg, ctx)
memorystore.Archiving(&wg, ctx)
r := http.NewServeMux()
api.MountRoutes(r)
2021-08-20 12:54:11 +02:00
if flagDev {
log.Print("Enable Swagger UI!")
r.HandleFunc("GET /swagger/", httpSwagger.Handler(
httpSwagger.URL("http://"+config.Keys.HttpConfig.Address+"/swagger/doc.json")))
}
server := &http.Server{
Handler: r,
Addr: config.Keys.HttpConfig.Address,
WriteTimeout: 30 * time.Second,
ReadTimeout: 30 * time.Second,
}
// Start http or https server
listener, err := net.Listen("tcp", config.Keys.HttpConfig.Address)
if err != nil {
log.Fatalf("starting http listener failed: %v", err)
}
if config.Keys.HttpConfig.CertFile != "" && config.Keys.HttpConfig.KeyFile != "" {
cert, err := tls.LoadX509KeyPair(config.Keys.HttpConfig.CertFile, config.Keys.HttpConfig.KeyFile)
if err != nil {
log.Fatalf("loading X509 keypair failed: %v", err)
}
listener = tls.NewListener(listener, &tls.Config{
Certificates: []tls.Certificate{cert},
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
},
MinVersion: tls.VersionTLS12,
PreferServerCipherSuites: true,
})
fmt.Printf("HTTPS server listening at %s...", config.Keys.HttpConfig.Address)
} else {
fmt.Printf("HTTP server listening at %s...", config.Keys.HttpConfig.Address)
}
wg.Add(1)
go func() {
defer wg.Done()
if err = server.Serve(listener); err != nil && err != http.ErrServerClosed {
log.Fatalf("starting server failed: %v", err)
}
}()
wg.Add(1)
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
defer wg.Done()
<-sigs
runtimeEnv.SystemdNotifiy(false, "Shutting down ...")
server.Shutdown(context.Background())
shutdown()
memorystore.Shutdown()
2021-08-20 11:45:34 +02:00
}()
2024-05-06 14:20:43 +02:00
if config.Keys.Nats != nil {
for _, natsConf := range config.Keys.Nats {
2022-02-22 14:03:45 +01:00
// TODO: When multiple nats configs share a URL, do a single connect.
wg.Add(1)
nc := natsConf
go func() {
// err := ReceiveNats(conf.Nats, decodeLine, runtime.NumCPU()-1, ctx)
err := api.ReceiveNats(nc, ms, 1, ctx)
2022-02-22 14:03:45 +01:00
if err != nil {
log.Fatal(err)
}
wg.Done()
}()
}
2021-10-12 13:26:54 +02:00
}
2021-08-20 12:54:11 +02:00
runtimeEnv.SystemdNotifiy(true, "running")
2021-08-20 12:54:11 +02:00
wg.Wait()
log.Print("Graceful shutdown completed!")
2021-06-09 06:03:31 +02:00
}