From e463d717e6b21101cc4dcd39f3ffa1fef319f008 Mon Sep 17 00:00:00 2001 From: Christian Simon Date: Wed, 12 Jun 2024 15:21:53 +0100 Subject: [PATCH] profilecli: Add a label values cardinality subcommand --- cmd/profilecli/main.go | 7 ++++ cmd/profilecli/query.go | 76 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/cmd/profilecli/main.go b/cmd/profilecli/main.go index 124b160cff..7fa6c563e4 100644 --- a/cmd/profilecli/main.go +++ b/cmd/profilecli/main.go @@ -70,6 +70,8 @@ func main() { queryMergeParams := addQueryMergeParams(queryMergeCmd) querySeriesCmd := queryCmd.Command("series", "Request series labels.") querySeriesParams := addQuerySeriesParams(querySeriesCmd) + queryLabelValuesCardinalityCmd := queryCmd.Command("label-values-cardinality", "Request label values cardinality.") + queryLabelValuesCardinalityParams := addQueryLabelValuesCardinalityParams(queryLabelValuesCardinalityCmd) queryTracerCmd := app.Command("query-tracer", "Analyze query traces.") queryTracerParams := addQueryTracerParams(queryTracerCmd) @@ -116,6 +118,11 @@ func main() { os.Exit(checkError(err)) } + case queryLabelValuesCardinalityCmd.FullCommand(): + if err := queryLabelValuesCardinality(ctx, queryLabelValuesCardinalityParams); err != nil { + os.Exit(checkError(err)) + } + case queryTracerCmd.FullCommand(): if err := queryTracer(ctx, queryTracerParams); err != nil { os.Exit(checkError(err)) diff --git a/cmd/profilecli/query.go b/cmd/profilecli/query.go index aff60f16a3..e986e67550 100644 --- a/cmd/profilecli/query.go +++ b/cmd/profilecli/query.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "os" + "sort" "strings" "time" @@ -26,6 +27,7 @@ import ( "github.com/klauspost/compress/gzip" "github.com/mattn/go-isatty" "github.com/pkg/errors" + "golang.org/x/sync/errgroup" ) const ( @@ -261,3 +263,77 @@ func querySeries(ctx context.Context, params *querySeriesParams) (err error) { return nil } + +type queryLabelValuesCardinalityParams struct { + *queryParams + TopN uint64 +} + +func addQueryLabelValuesCardinalityParams(queryCmd commander) *queryLabelValuesCardinalityParams { + params := new(queryLabelValuesCardinalityParams) + params.queryParams = addQueryParams(queryCmd) + queryCmd.Flag("top-n", "Show the top N high cardinality label values").Default("20").Uint64Var(¶ms.TopN) + return params +} + +func queryLabelValuesCardinality(ctx context.Context, params *queryLabelValuesCardinalityParams) (err error) { + from, to, err := params.parseFromTo() + if err != nil { + return err + } + + level.Info(logger).Log("msg", "query label names", "url", params.URL, "from", from, "to", to, "labelNames", fmt.Sprintf("%q")) + + qc := params.phlareClient.queryClient() + resp, err := qc.LabelNames(ctx, connect.NewRequest(&typesv1.LabelNamesRequest{ + Start: from.UnixMilli(), + End: to.UnixMilli(), + Matchers: []string{params.Query}, + })) + if err != nil { + return errors.Wrap(err, "failed to query") + } + + level.Info(logger).Log("msg", fmt.Sprintf("received %d label names", len(resp.Msg.Names))) + + g, gctx := errgroup.WithContext(ctx) + g.SetLimit(16) + result := make([]int, len(resp.Msg.Names)) + + for idx := range resp.Msg.Names { + idx := idx + g.Go(func() error { + name := resp.Msg.Names[idx] + resp, err := qc.LabelValues(gctx, connect.NewRequest(&typesv1.LabelValuesRequest{ + Name: name, + Start: from.UnixMilli(), + End: to.UnixMilli(), + Matchers: []string{params.Query}, + })) + if err != nil { + return fmt.Errorf("failed to query label values for %s: %w", name, err) + } + + result[idx] = len(resp.Msg.Names) + + return nil + }) + } + if err := g.Wait(); err != nil { + return err + } + + // sort the result + sort.Slice(resp.Msg.Names, func(i, j int) bool { + return result[i] > result[j] + }) + sort.Slice(result, func(i, j int) bool { + return result[i] > result[j] + }) + + for idx := range resp.Msg.Names[:params.TopN] { + fmt.Println(resp.Msg.Names[idx], result[idx]) + } + + return nil +}