Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: profilecli query-blocks series #3610

Merged
merged 2 commits into from
Oct 8, 2024
Merged

feat: profilecli query-blocks series #3610

merged 2 commits into from
Oct 8, 2024

Conversation

alsoba13
Copy link
Contributor

@alsoba13 alsoba13 commented Oct 7, 2024

In this PR we introduce a new profilecli command: query-blocks. With this command, you can execute queries directly to single or multiple blocks hosted in your localhost or a remote bucket.

Partially solves #3559, profile query-blocks merge will come in a later PR.

Capabilities

This feature gives similar capabilities as profilecli query seriesbut for a specified local/remote blocks:

  • It has support for checking label values with --label-names or specify the query with --query.
  • You may choose to use it locally (--local-path) or remotely (--bucket-name, --tenant-id and --object-store-type - only gcs supported right now).
  • You specify queried blocks with the --block-ids flag.
  • Time ranges (to and from) are not needed: it will query the whole blocks instead.

doc

profilecl query-blocks series --help
usage: profilecli query-blocks series [<flags>]

Request series labels on a local/remote block

Flags:
  -h, --help                     Show context-sensitive help (also try --help-long and --help-man).
      --version                  Show application version.
  -v, --verbose                  Enable verbose logging.
      --local-path="./data/anonymous/local"
                                 Path to blocks directory.
      --bucket-name=BUCKET-NAME  The name of the object storage bucket.
      --object-store-type="gcs"  The type of the object storage (e.g., gcs).
      --block-ids=BLOCK-IDS ...  List of blocks ids to query on
      --tenant-id=TENANT-ID      Tenant id of the queried block for remote bucket
      --query="{}"               Label selector to query.
      --label-names=LABEL-NAMES ...
                                 Filter returned labels to the supplied label names. Without any filter all labels are returned.

Usage example:

Querying series on a local block, and filter on service_name

profilecli query-blocks series --local-path=./data/anonymous/local --block-ids=01J9K65Z1RP3YJG66GSR9FVDQ0 --query='{service_name="ride-sharing-app"}'
level=info msg="query-block series" labelNames=[] blockIds=[01J9K65Z1RP3YJG66GSR9FVDQ0] localPath=./data/anonymous/local bucketName= tenantId=
{"__name__":"memory","__period_type__":"space","__period_unit__":"bytes","__profile_type__":"memory:alloc_objects:count:space:bytes","__service_name__":"ride-sharing-app","__type__":"alloc_objects","__unit__":"count","hostname":"6faf2cfcb5e8","pyroscope_spy":"gospy","region":"us-east","service_git_ref":"HEAD","service_name":"ride-sharing-app","service_repository":"https://github.com/grafana/pyroscope","service_root_path":"examples/language-sdk-instrumentation/golang-push/rideshare"}
{"__name__":"memory","__period_type__":"space","__period_unit__":"bytes","__profile_type__":"memory:alloc_objects:count:space:bytes","__service_name__":"ride-sharing-app","__type__":"alloc_objects","__unit__":"count","hostname":"bf91795602f5","pyroscope_spy":"gospy","region":"eu-north","service_git_ref":"HEAD","service_name":"ride-sharing-app","service_repository":"https://github.com/grafana/pyroscope","service_root_path":"examples/language-sdk-instrumentation/golang-push/rideshare"}
...

Querying series on two remote blocks, just checking at __profile_type__ label:

profilecli query-blocks series --bucket-name='dev-us-central-0-profiles-dev-001-data' --tenant-id=1218 --block-ids=01J9NBD1V2BJQWCMDQGMNGC4W5 --block-ids=01J9NBD1THECVDYVWFJVYWWX7M --label-names=__profile_type__
level=info msg="query-block series" labelNames=[__profile_type__] blockIds="[01J9NBD1V2BJQWCMDQGMNGC4W5 01J9NBD1THECVDYVWFJVYWWX7M]" localPath=./data/anonymous/local bucketName=dev-us-central-0-profiles-dev-001-data tenantId=1218
{"__profile_type__":"block:contentions:count:contentions:count"}
{"__profile_type__":"block:delay:nanoseconds:contentions:count"}
{"__profile_type__":"goroutine:goroutine:count:goroutine:count"}
{"__profile_type__":"goroutines:goroutine:count:goroutine:count"}
{"__profile_type__":"memory:alloc_objects:count:space:bytes"}
{"__profile_type__":"memory:alloc_space:bytes:space:bytes"}
{"__profile_type__":"memory:inuse_objects:count:space:bytes"}
{"__profile_type__":"memory:inuse_space:bytes:space:bytes"}
{"__profile_type__":"mutex:contentions:count:contentions:count"}
{"__profile_type__":"mutex:delay:nanoseconds:contentions:count"}
{"__profile_type__":"process_cpu:cpu:nanoseconds:cpu:nanoseconds"}
{"__profile_type__":"process_cpu:samples:count::milliseconds"}
{"__profile_type__":"process_cpu:samples:count:cpu:nanoseconds"}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extracting some common logic from query.go

@@ -159,6 +159,23 @@ func (b *BlockQuerier) BlockMetas(ctx context.Context) (metas []*block.Meta, _ e
return metas[0 : pos+1], nil
}

func (b *BlockQuerier) BlockMeta(ctx context.Context, name string) (meta *block.Meta, _ error) {
Copy link
Contributor Author

@alsoba13 alsoba13 Oct 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to introduce a direct way to read a single meta, otherwise, using BlockMetas would load every block in the bucket (potentially thousands of them)

@alsoba13 alsoba13 marked this pull request as ready for review October 7, 2024 14:32
@alsoba13 alsoba13 requested a review from a team as a code owner October 7, 2024 14:32
@alsoba13 alsoba13 force-pushed the alsoba13/query-blocks branch 3 times, most recently from 3b727d3 to 40b3777 Compare October 7, 2024 14:37
Copy link
Contributor

@aleks-p aleks-p left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me!

Could you expand the PR description with examples of successful calls? One with local and one with remote blocks, to make it more clear on how this can be used.

Copy link
Collaborator

@kolesnikovae kolesnikovae left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! Thanks for implementing this @alsoba13!

Comment on lines 14 to 16
for k := range m {
delete(m, k)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for k := range m {
delete(m, k)
}
clear(m)

Comment on lines +82 to +85
queryBlocksCmd := app.Command("query-blocks", "Query on local/remote blocks")
queryBlocksSeriesCmd := queryBlocksCmd.Command("series", "Request series labels on local/remote blocks")
queryBlocksSeriesParams := addQueryBlocksSeriesParams(queryBlocksSeriesCmd)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT about moving this to admin blocks sub-commands?

profilecli admin blocks --help

usage: profilecli admin blocks [<flags>] <command> [<args> ...]

Operate on Grafana Pyroscope's blocks.

Flags:
  -h, --help                 Show context-sensitive help (also try --help-long and --help-man).
      --version              Show application version.
  -v, --verbose              Enable verbose logging.
      --path="./data/local"  Path to blocks directory

Subcommands:
  admin blocks list [<flags>]
    List blocks.

  admin blocks compact [<flags>] <from> <dest>
    Compact blocks.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not directly related to this PR, but if you think it makes sense we can improve the storage config handling (in all subcommands)

I think that we should use the standard client config (that supports all backends). You can register the flags as follows:

	blocksCmd := adminCmd.Command("blocks", "Operate on Grafana Pyroscope's blocks.")
	blocksCmd.Flag("path", "Path to blocks directory").Default(defaultLocalBlockPath).StringVar(&cfg.blocks.path)
	var f flag.FlagSet
	var c client.StorageBackendConfig
	c.RegisterFlags(&f, logger)
	f.VisitAll(func(flag *flag.Flag) {
		blocksCmd.Flag(flag.Name, flag.Usage).Hidden().Default(flag.DefValue).SetValue(flag.Value)
	})

This will probably require handling of the default path and fs backend. This way we could create our Bucket object for all blocks subcommands automatically.

Copy link
Contributor Author

@alsoba13 alsoba13 Oct 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the flags of the storage, I'll take a look and maybe implement separately.

About making query a subcommand of admin blocks, I discarded that idea at first because I thought that profilecli admin blocks needed a pyroscope server to work. Now I see it works directly on blocks, and makes sense to merge commands here. I may implement this as well in a following PR.

Copy link
Contributor

@marcsanmi marcsanmi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just a couple minor things.

cmd/profilecli/query-blocks.go Outdated Show resolved Hide resolved
ctx := context.Background()
blockQuerier := NewBlockQuerier(ctx, bucket)
metas, err := blockQuerier.BlockMetas(ctx)
require.NoError(t, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add something like this to ensure there's actually data to test.

Suggested change
require.NoError(t, err)
require.NoError(t, err)
require.NotEmpty(t, metas)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good one as well, fixed

@alsoba13 alsoba13 merged commit 426515a into main Oct 8, 2024
18 checks passed
@alsoba13 alsoba13 deleted the alsoba13/query-blocks branch October 8, 2024 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants