mirror of
https://github.com/mostlygeek/llama-swap.git
synced 2026-06-09 06:46:34 +02:00
llama-swap.go: remove debounce, replace fmt.Printlns (#731)
Linux CI / run-tests (push) Successful in 3m22s
Build Unified Docker Image / setup (push) Successful in 4s
Build Containers / build-and-push (cpu) (push) Failing after 10s
Build Containers / build-and-push (cuda) (push) Failing after 10s
Build Containers / build-and-push (cuda13) (push) Failing after 10s
Build Containers / build-and-push (intel) (push) Failing after 10s
Build Containers / build-and-push (musa) (push) Failing after 10s
Build Containers / build-and-push (rocm) (push) Failing after 10s
Build Containers / build-and-push (vulkan) (push) Failing after 10s
Build Containers / delete-untagged-containers (push) Has been skipped
Build Unified Docker Image / build (push) Failing after 10s
Close inactive issues / close-issues (push) Successful in 5s
Windows CI / run-tests (push) Has been cancelled
Linux CI / run-tests (push) Successful in 3m22s
Build Unified Docker Image / setup (push) Successful in 4s
Build Containers / build-and-push (cpu) (push) Failing after 10s
Build Containers / build-and-push (cuda) (push) Failing after 10s
Build Containers / build-and-push (cuda13) (push) Failing after 10s
Build Containers / build-and-push (intel) (push) Failing after 10s
Build Containers / build-and-push (musa) (push) Failing after 10s
Build Containers / build-and-push (rocm) (push) Failing after 10s
Build Containers / build-and-push (vulkan) (push) Failing after 10s
Build Containers / delete-untagged-containers (push) Has been skipped
Build Unified Docker Image / build (push) Failing after 10s
Close inactive issues / close-issues (push) Successful in 5s
Windows CI / run-tests (push) Has been cancelled
small fixes to clean up the main(): - remove the debounced config reload - replace fmt.Println with a proxy.LogMonitor for consistency
This commit is contained in:
+74
-72
@@ -4,12 +4,12 @@ import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -34,24 +34,40 @@ func main() {
|
||||
keyFile := flag.String("tls-key-file", "", "TLS key file")
|
||||
showVersion := flag.Bool("version", false, "show version of build")
|
||||
watchConfig := flag.Bool("watch-config", false, "Automatically reload config file on change")
|
||||
mainLogger := proxy.NewLogMonitor()
|
||||
|
||||
flag.Parse() // Parse the command-line flags
|
||||
|
||||
if *showVersion {
|
||||
fmt.Printf("version: %s (%s), built at %s\n", version, commit, date)
|
||||
fmt.Printf("version: %s (%s), built at %s", version, commit, date)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
conf, err := config.LoadConfig(*configPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error loading config: %v\n", err)
|
||||
mainLogger.Errorf("Error loading config: %", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(conf.Profiles) > 0 {
|
||||
fmt.Println("WARNING: Profile functionality has been removed in favor of Groups. See the README for more information.")
|
||||
mainLogger.Warn("Profile functionality has been removed in favor of Groups. See the README for more information.")
|
||||
}
|
||||
|
||||
switch strings.ToLower(strings.TrimSpace(conf.LogLevel)) {
|
||||
case "debug":
|
||||
mainLogger.SetLogLevel(proxy.LevelDebug)
|
||||
case "info":
|
||||
mainLogger.SetLogLevel(proxy.LevelInfo)
|
||||
case "warn":
|
||||
mainLogger.SetLogLevel(proxy.LevelWarn)
|
||||
case "error":
|
||||
mainLogger.SetLogLevel(proxy.LevelError)
|
||||
default:
|
||||
mainLogger.SetLogLevel(proxy.LevelInfo)
|
||||
}
|
||||
|
||||
mainLogger.Debugf("PID: %d", os.Getpid())
|
||||
|
||||
if mode := os.Getenv("GIN_MODE"); mode != "" {
|
||||
gin.SetMode(mode)
|
||||
} else {
|
||||
@@ -78,15 +94,7 @@ func main() {
|
||||
// Setup channels for server management
|
||||
exitChan := make(chan struct{})
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
// Reload signals (SIGHUP on POSIX, none on Windows — Windows does not
|
||||
// deliver SIGHUP). Always wired up so `kill -HUP` works regardless of
|
||||
// --watch-config.
|
||||
reloadChan := make(chan os.Signal, 1)
|
||||
if runtime.GOOS != "windows" {
|
||||
signal.Notify(reloadChan, syscall.SIGHUP)
|
||||
}
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
|
||||
|
||||
// Context that bounds the lifetime of background watcher goroutines.
|
||||
watcherCtx, watcherCancel := context.WithCancel(context.Background())
|
||||
@@ -97,20 +105,36 @@ func main() {
|
||||
}
|
||||
|
||||
// Support for watching config and reloading when it changes
|
||||
reloading := false
|
||||
var reloadMutex sync.Mutex
|
||||
reloadProxyManager := func() {
|
||||
reloadMutex.Lock()
|
||||
if reloading {
|
||||
reloadMutex.Unlock()
|
||||
return
|
||||
}
|
||||
reloading = true
|
||||
reloadMutex.Unlock()
|
||||
defer func() {
|
||||
reloadMutex.Lock()
|
||||
reloading = false
|
||||
reloadMutex.Unlock()
|
||||
}()
|
||||
|
||||
mainLogger.Info("Reloading Configuration")
|
||||
if currentPM, ok := srv.Handler.(*proxy.ProxyManager); ok {
|
||||
conf, err = config.LoadConfig(*configPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Warning, unable to reload configuration: %v\n", err)
|
||||
mainLogger.Warnf("Unable to reload configuration: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Configuration Changed")
|
||||
mainLogger.Debug("Configuration Changed")
|
||||
currentPM.Shutdown()
|
||||
newPM := proxy.New(conf)
|
||||
newPM.SetVersion(date, commit, version)
|
||||
srv.Handler = newPM
|
||||
fmt.Println("Configuration Reloaded")
|
||||
mainLogger.Debug("Configuration Reloaded")
|
||||
|
||||
// wait a few seconds and tell any UI to reload
|
||||
time.AfterFunc(3*time.Second, func() {
|
||||
@@ -121,7 +145,7 @@ func main() {
|
||||
} else {
|
||||
conf, err = config.LoadConfig(*configPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error, unable to load configuration: %v\n", err)
|
||||
mainLogger.Errorf("Unable to load configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
newPM := proxy.New(conf)
|
||||
@@ -132,94 +156,72 @@ func main() {
|
||||
|
||||
// load the initial proxy manager
|
||||
reloadProxyManager()
|
||||
debouncedReload := debounce(time.Second, reloadProxyManager)
|
||||
|
||||
// Listen for ConfigFileChangedEvent unconditionally so SIGHUP and the
|
||||
// poll-based watcher both feed the same debounced reload pipeline. The
|
||||
// UI also listens for the matching ReloadingStateEnd emitted from
|
||||
// reloadProxyManager.
|
||||
defer event.On(func(e proxy.ConfigFileChangedEvent) {
|
||||
if e.ReloadingState == proxy.ReloadingStateStart {
|
||||
debouncedReload()
|
||||
}
|
||||
})()
|
||||
|
||||
// SIGHUP (or platform-equivalent) → reload. Back-to-back signals collapse
|
||||
// to one reload via the debounce window, which is the desired behavior.
|
||||
go func() {
|
||||
for range reloadChan {
|
||||
fmt.Println("Received reload signal, reloading configuration")
|
||||
event.Emit(proxy.ConfigFileChangedEvent{
|
||||
ReloadingState: proxy.ReloadingStateStart,
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
||||
if *watchConfig {
|
||||
go func() {
|
||||
absConfigPath, err := filepath.Abs(*configPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting absolute path for watching config file: %v\n", err)
|
||||
mainLogger.Errorf("watch-config unable to determine absolute path for watching config file: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Println("Watching configuration for changes (poll-based, 2s interval)")
|
||||
mainLogger.Info("Watching configuration for changes (poll-based, 2s interval)")
|
||||
(&configwatcher.Watcher{
|
||||
Path: absConfigPath,
|
||||
Interval: configwatcher.DefaultInterval,
|
||||
OnChange: func() {
|
||||
event.Emit(proxy.ConfigFileChangedEvent{
|
||||
ReloadingState: proxy.ReloadingStateStart,
|
||||
})
|
||||
reloadProxyManager()
|
||||
},
|
||||
}).Run(watcherCtx)
|
||||
}()
|
||||
}
|
||||
|
||||
// shutdown on signal
|
||||
// Signal handling
|
||||
go func() {
|
||||
sig := <-sigChan
|
||||
fmt.Printf("Received signal %v, shutting down...\n", sig)
|
||||
watcherCancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
for {
|
||||
sig := <-sigChan
|
||||
switch sig {
|
||||
case syscall.SIGHUP:
|
||||
mainLogger.Debug("Received SIGHUP")
|
||||
reloadProxyManager()
|
||||
case syscall.SIGINT, syscall.SIGTERM:
|
||||
mainLogger.Debugf("Received signal %v, shutting down...", sig)
|
||||
watcherCancel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
|
||||
if pm, ok := srv.Handler.(*proxy.ProxyManager); ok {
|
||||
pm.Shutdown()
|
||||
} else {
|
||||
fmt.Println("srv.Handler is not of type *proxy.ProxyManager")
|
||||
}
|
||||
if pm, ok := srv.Handler.(*proxy.ProxyManager); ok {
|
||||
pm.Shutdown()
|
||||
} else {
|
||||
mainLogger.Error("srv.Handler is not of type *proxy.ProxyManager")
|
||||
}
|
||||
|
||||
if err := srv.Shutdown(ctx); err != nil {
|
||||
fmt.Printf("Server shutdown error: %v\n", err)
|
||||
if err := srv.Shutdown(ctx); err != nil {
|
||||
mainLogger.Errorf("Server shutdown: %v", err)
|
||||
}
|
||||
close(exitChan)
|
||||
return
|
||||
default:
|
||||
// do nothing on other signals
|
||||
}
|
||||
}
|
||||
close(exitChan)
|
||||
}()
|
||||
|
||||
// Start server
|
||||
go func() {
|
||||
var err error
|
||||
if useTLS {
|
||||
fmt.Printf("llama-swap listening with TLS on https://%s\n", *listenStr)
|
||||
mainLogger.Infof("llama-swap listening with TLS on https://%s", *listenStr)
|
||||
err = srv.ListenAndServeTLS(*certFile, *keyFile)
|
||||
} else {
|
||||
fmt.Printf("llama-swap listening on http://%s\n", *listenStr)
|
||||
mainLogger.Infof("llama-swap listening on http://%s", *listenStr)
|
||||
err = srv.ListenAndServe()
|
||||
}
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("Fatal server error: %v\n", err)
|
||||
mainLogger.Errorf("Fatal server error: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for exit signal
|
||||
<-exitChan
|
||||
}
|
||||
|
||||
func debounce(interval time.Duration, f func()) func() {
|
||||
var timer *time.Timer
|
||||
return func() {
|
||||
if timer != nil {
|
||||
timer.Stop()
|
||||
}
|
||||
timer = time.AfterFunc(interval, f)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user