router/cmd/plan_generator.go (92 lines of code) (raw):
package cmd
import (
"context"
"flag"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"github.com/KimMachineGun/automemlimit/memlimit"
"github.com/dustin/go-humanize"
"github.com/wundergraph/cosmo/router/core"
"github.com/wundergraph/cosmo/router/pkg/logging"
"github.com/wundergraph/cosmo/router/pkg/plan_generator"
"go.uber.org/automaxprocs/maxprocs"
"go.uber.org/zap"
)
func PlanGenerator(args []string) {
var planHelp bool
cfg := plan_generator.QueryPlanConfig{}
f := flag.NewFlagSet("router "+args[0], flag.ExitOnError)
f.BoolVar(&planHelp, "help", false, "Prints the help message")
f.StringVar(&cfg.ExecutionConfig, "execution-config", "", "required, execution config file location")
f.StringVar(&cfg.SourceDir, "operations", "", "required, source operations folder location")
f.StringVar(&cfg.OutDir, "plans", "", "required, output plans folder location")
f.StringVar(&cfg.Filter, "filter", "", "operation filter file location which should contain file names of operations to include")
f.StringVar(&cfg.Timeout, "timeout", "30s", "timeout")
f.IntVar(&cfg.Concurrency, "concurrency", 0, "how many query plan run concurrently")
f.BoolVar(&cfg.OutputFiles, "print-per-file", true, "write a file for each query, with inside the plan or the query plan error")
f.BoolVar(&cfg.OutputReport, "print-report", false, "write a report.json file, with all the query plans and errors sorted by file name")
f.BoolVar(&cfg.FailOnPlanError, "fail-on-error", false, "if at least one plan fails, the command exit code will be 1")
f.BoolVar(&cfg.FailFast, "fail-fast", false, "stop as soon as possible if a plan fails")
f.StringVar(&cfg.LogLevel, "log-level", "warning", "log level to use (debug, info, warning, error, panic, fatal)")
f.UintVar(&cfg.MaxDataSourceCollectorsConcurrency, "max-collectors", 0, "max number of concurrent data source collectors, if unset or 0, no limit will be enforced")
if err := f.Parse(args[1:]); err != nil {
f.PrintDefaults()
log.Fatalf("Failed to parse flags: %v", err)
}
if planHelp {
f.PrintDefaults()
return
}
if cfg.ExecutionConfig == "" || cfg.SourceDir == "" || cfg.OutDir == "" {
f.PrintDefaults()
log.Fatalf("missing required flags")
}
ctxNotify, stop := signal.NotifyContext(context.Background(), os.Interrupt,
syscall.SIGHUP, // process is detached from terminal
syscall.SIGTERM, // default for kill
syscall.SIGKILL,
syscall.SIGQUIT, // ctrl + \
syscall.SIGINT, // ctrl+c
)
defer stop()
logLevel, err := logging.ZapLogLevelFromString(cfg.LogLevel)
if err != nil {
log.Fatalf("Could not parse log level: %s", err)
}
logger := logging.New(false, false, logLevel).
With(
zap.String("service", "@wundergraph/query-plan"),
zap.String("service_version", core.Version),
)
cfg.Logger = logger
// Automatically set GOMAXPROCS to avoid CPU throttling on containerized environments
_, err = maxprocs.Set(maxprocs.Logger(func(msg string, args ...interface{}) {
logger.Info(fmt.Sprintf(msg, args...))
}))
if err != nil {
logger.Fatal("could not set max GOMAXPROCS", zap.Error(err))
}
if os.Getenv("GOMEMLIMIT") != "" {
logger.Info("GOMEMLIMIT set by user", zap.String("gomemlimit", os.Getenv("GOMEMLIMIT")))
} else {
// Automatically set GOMEMLIMIT to 90% of the available memory.
// This is an effort to prevent the router from being killed by OOM (Out Of Memory)
// when the system is under memory pressure e.g. when GC is not able to free memory fast enough.
// More details: https://tip.golang.org/doc/gc-guide#Memory_limit
mLimit, err := memlimit.SetGoMemLimitWithOpts(
memlimit.WithRatio(0.9),
// FromCgroupHybrid retrieves the memory limit from the cgroup v2 and v1 controller sequentially
memlimit.WithProvider(memlimit.FromCgroupHybrid),
)
if err == nil {
logger.Info("GOMEMLIMIT set automatically", zap.String("size", humanize.Bytes(uint64(mLimit))))
} else {
logger.Info(
"GOMEMLIMIT was not set. Please set it manually to around 90%% of the available memory to "+
"prevent OOM kills",
zap.Error(err),
)
}
}
err = plan_generator.PlanGenerator(ctxNotify, cfg)
if err != nil {
logger.Fatal("Error during command plan-generator: %s", zap.Error(err))
}
}