package main import ( "database/sql" "flag" "fmt" "log/slog" "os" "github.com/grafana/pyroscope-go" "github.com/martinlehoux/biking_home/config" "github.com/martinlehoux/biking_home/mountain_pass" "github.com/martinlehoux/biking_home/ride" "github.com/martinlehoux/biking_home/web" "github.com/martinlehoux/kagamigo/kcore" _ "github.com/mattn/go-sqlite3" ) var ( configFile = flag.String("config", "config.yaml", "path to the YAML configuration file") enablePprof = flag.Bool("pprof", false, "expose pprof endpoints at /debug/pprof") ) func main() { flag.Parse() appConfig, err := config.Load(*configFile) kcore.Expect(err, "failed to load configuration") stopProfiler, err := startPyroscope(appConfig.Pyroscope) if err != nil { kcore.Expect(err, "failed to start pyroscope") } if stopProfiler != nil { defer stopProfiler() } db, err := sql.Open("sqlite3", appConfig.Database.Path) kcore.Expect(err, "failed to open database") defer db.Close() runCommand(db, *configFile, appConfig, flag.Args(), *enablePprof) } func runCommand(db *sql.DB, configPath string, appConfig config.Config, args []string, enablePprof bool) { if len(args) == 0 { web.ServeCmd(db, configPath, appConfig, enablePprof) return } switch args[0] { case "fetch-osm": mountain_pass.FetchOSM() case "extract-osm": if len(args) < 2 { fmt.Fprintln(os.Stderr, "extract-osm requires a PBF file path argument") printUsage() os.Exit(2) } mountain_pass.ExtractOSM(db, args[1]) case "backfill": ride.BackfillCmd(db) case "backtest": ride.BacktestCmd(db) case "elevation": ride.ElevationCmd(db, args[1:]) case "serve": for _, arg := range args[1:] { if arg == "--pprof" || arg == "-pprof" || arg == "pprof" { enablePprof = true } } web.ServeCmd(db, configPath, appConfig, enablePprof) case "help", "-h", "--help": printUsage() default: fmt.Fprintf(os.Stderr, "unknown command %q\n", args[0]) printUsage() os.Exit(2) } } func printUsage() { fmt.Fprintf(os.Stderr, `Usage: biking_home [global flags] [args] Global flags: -config path to YAML configuration file (default "config.yaml") -pprof expose pprof endpoints at /debug/pprof (also: "serve --pprof") Commands: serve start web server (default when no command given) fetch-osm download the France OSM PBF into france-latest.osm.pbf extract-osm extract mountain passes from an OSM PBF file backfill recompute all stored ride values backtest run data benchmarks comparing computed values against stored reference values elevation analyse detect elevation outliers (tunnel spikes) elevation fix linearly fix elevation between anchors `) } // startPyroscope starts the continuous profiler when enabled in the config and // returns a stop function to flush and detach the profiler on shutdown, or nil // when pyroscope is disabled. func startPyroscope(appConfig config.PyroscopeConfig) (func(), error) { if !appConfig.Enabled { return nil, nil } profiler, err := pyroscope.Start(pyroscope.Config{ ApplicationName: appConfig.ApplicationName, ServerAddress: appConfig.ServerAddress, Logger: pyroscope.StandardLogger, ProfileTypes: []pyroscope.ProfileType{ pyroscope.ProfileCPU, pyroscope.ProfileAllocSpace, pyroscope.ProfileAllocObjects, pyroscope.ProfileInuseSpace, pyroscope.ProfileInuseObjects, pyroscope.ProfileGoroutines, }, }) if err != nil { return nil, err } slog.Info("pyroscope enabled", "server", appConfig.ServerAddress, "application", appConfig.ApplicationName) return func() { _ = profiler.Stop() }, nil }