From 42811407a902749214ec6e8e7e1d88c1067f1771 Mon Sep 17 00:00:00 2001 From: Florent Messa Date: Mon, 25 Sep 2023 16:30:42 +0200 Subject: [PATCH] feat: free memory periodically --- cmd/picfit/main.go | 4 +--- config/config.go | 8 ++++++-- server/http.go | 26 +++++++++++++++++++++++--- server/server.go | 40 +++++++++++++++++++++++----------------- 4 files changed, 53 insertions(+), 25 deletions(-) diff --git a/cmd/picfit/main.go b/cmd/picfit/main.go index 728145be..9de3558c 100644 --- a/cmd/picfit/main.go +++ b/cmd/picfit/main.go @@ -88,9 +88,7 @@ func main() { os.Exit(1) } - err := server.Run(context.Background(), config) - - if err != nil { + if err := server.Run(context.Background(), config); err != nil { fmt.Fprint(os.Stderr, err) os.Exit(1) } diff --git a/config/config.go b/config/config.go index 558da45a..1fe8ead7 100644 --- a/config/config.go +++ b/config/config.go @@ -39,6 +39,7 @@ type Options struct { EnableUpload bool `mapstructure:"enable_upload"` EnablePrometheus bool `mapstructure:"enable_prometheus"` MimetypeDetector string `mapstructure:"mimetype_detector"` + FreeMemoryInterval int `mapstructure:"free_memory_interval"` } // Sentry is a struct to configure sentry using a dsn @@ -125,11 +126,14 @@ func load(content string, isPath bool) (*Config, error) { } } - err = viper.Unmarshal(&config) - if err != nil { + if err = viper.Unmarshal(&config); err != nil { return nil, err } + if config.Options.FreeMemoryInterval == 0 { + config.Options.FreeMemoryInterval = 10 + } + return config, nil } diff --git a/server/http.go b/server/http.go index 00c404a0..f3b7eeb0 100644 --- a/server/http.go +++ b/server/http.go @@ -1,6 +1,7 @@ package server import ( + "context" "fmt" "net/http" "net/http/pprof" @@ -39,6 +40,10 @@ func NewHTTPServer(cfg *config.Config, processor *picfit.Processor) (*HTTPServer return server, nil } +func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { + s.engine.ServeHTTP(w, req) +} + func (s *HTTPServer) Init() error { var ( router = gin.New() @@ -223,8 +228,23 @@ func prometheusHandler() gin.HandlerFunc { } // Run loads a new http server -func (s *HTTPServer) Run() error { - s.engine.Run(fmt.Sprintf(":%s", strconv.Itoa(s.config.Port))) +func (s *HTTPServer) Run(ctx context.Context) error { + addr := fmt.Sprintf(":%s", strconv.Itoa(s.config.Port)) + srv := &http.Server{ + Addr: addr, + Handler: s.engine.Handler(), + } - return nil + cerr := make(chan error) + go func() { + s.processor.Logger.Debug(fmt.Sprintf("Starting HTTP server on %s", addr)) + cerr <- srv.ListenAndServe() + }() + select { + case err := <-cerr: + return err + case <-ctx.Done(): + s.processor.Logger.Debug("Shutdown HTTP server") + return srv.Shutdown(context.Background()) + } } diff --git a/server/server.go b/server/server.go index 23a2f5fb..2929862b 100644 --- a/server/server.go +++ b/server/server.go @@ -2,17 +2,16 @@ package server import ( "context" - "net/http" + "os/signal" + "runtime/debug" + "syscall" + "time" "github.com/thoas/picfit" "github.com/thoas/picfit/config" ) -type Server struct { - http *HTTPServer -} - -func New(ctx context.Context, cfg *config.Config) (*Server, error) { +func New(ctx context.Context, cfg *config.Config) (*HTTPServer, error) { processor, err := picfit.NewProcessor(ctx, cfg) if err != nil { return nil, err @@ -23,16 +22,7 @@ func New(ctx context.Context, cfg *config.Config) (*Server, error) { return nil, err } - server := &Server{http: httpServer} - return server, nil -} - -func (s *Server) Run() error { - return s.http.Run() -} - -func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { - s.http.engine.ServeHTTP(w, req) + return httpServer, nil } // Run runs the application and launch servers @@ -46,6 +36,22 @@ func Run(ctx context.Context, path string) error { if err != nil { return err } + ctx, stop := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) + defer stop() + if err := server.Run(ctx); err != nil { + return err + } + + go func() { + for range time.Tick(time.Duration(cfg.Options.FreeMemoryInterval) * time.Second) { + debug.FreeOSMemory() + } + }() + + select { // nolint:gosimple + case <-ctx.Done(): + stop() + } - return server.Run() + return nil }