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(orm)!: implement gRPC query support #14965

Closed
wants to merge 15 commits into from
Closed

feat(orm)!: implement gRPC query support #14965

wants to merge 15 commits into from

Conversation

aaronc
Copy link
Member

@aaronc aaronc commented Feb 9, 2023

Description

Ref #11774
This follows up on #13438 and actually implements the query servers as well as making the following changes:

  • queries are renamed to follow the patterns more commonly encountered in SDK queries and CLI commands, ex. Balance and Balances instead of GetBalance and ListBalance. This should play more nicely with autocli
  • there is a separate list method per index which matches how query servers are structured now (ex. Groups, and GroupsByMember are separate query methods)
  • range queries are removed (for now) - I haven't found any instances of range queries in our gRPC query servers or CLI commands, so I consider this advanced usage. We should support it but it can be dealt with later and maybe generically through the API in feat(orm): specify generic gRPC query service #11791
  • gRPC gateway REST annotations have been added - these follow the convention of using plurals (ex. users/{id}). Indexed are separated using a . which I'm not sure about but seems like the best generic solution so far (ex. balances.by-denom?denom=foo)

The most important files for reviewers to look at are bank_query.proto and test_schema_query.proto


Author Checklist

All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.

I have...

  • included the correct type prefix in the PR title
  • added ! to the type prefix if API or client breaking change
  • targeted the correct branch (see PR Targeting)
  • provided a link to the relevant issue or specification
  • followed the guidelines for building modules
  • included the necessary unit and integration tests
  • added a changelog entry to CHANGELOG.md
  • included comments for documenting Go code
  • updated the relevant documentation or specification
  • reviewed "Files changed" and left comments if necessary
  • confirmed all CI checks have passed

Reviewers Checklist

All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.

I have...

  • confirmed the correct type prefix in the PR title
  • confirmed ! in the type prefix if API or client breaking change
  • confirmed all author checklist items have been addressed
  • reviewed state machine logic
  • reviewed API design and naming
  • reviewed documentation is accurate
  • reviewed tests and test coverage
  • manually tested (if applicable)

@github-actions github-actions bot added the C:orm label Feb 9, 2023
@@ -56,7 +61,7 @@
continue
}

out, err := os.OpenFile(fmt.Sprintf("%s_query.proto", f.GeneratedFilenamePrefix), os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0o644)
out, err := os.OpenFile(queryProtoFilename(f), os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0o644)

Check failure

Code scanning / gosec

Expect file permissions to be 0600 or less

Expect file permissions to be 0600 or less
Copy link
Contributor

@ryanchristo ryanchristo left a comment

Choose a reason for hiding this comment

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

Looks like a good start. I'm wondering about the following though:

  • converting addresses stored in state as bytes (as well as any other values that might need to be converted)
  • return embedded information in response (in regen ledger we often store the primary key of another state object and need to return specific information from the other state object, e.g. a project needs to return the class id stored in a class state object, a sell order needs to return the ask price denom stored in the market state object, etc.)

}
t.P(")")
t.P("if err != nil { return nil, err }")
t.P("return &", name, "Response{Value: res}, nil")
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be specific? e.g. Balances rather than Values

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure

// BalancesByDenom queries the Balance table using the primary key index.
rpc BalancesByDenom(BalancesByDenomRequest) returns (BalancesByDenomResponse) {
option (google.api.http) = {
get: "/testpb/balances.denom"
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this conventional? Can this be more user friendly? e.g. balances-by-denom

Copy link
Contributor

Choose a reason for hiding this comment

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

Also worth noting the sdk uses _ but as far as I understand - is more conventional.

Copy link
Member Author

Choose a reason for hiding this comment

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

No it's not conventional. Initially I was thinking an HTTP matrix parameter would be nice (balances;index=denom) but that doesn't play so nicely with grpc gateway.

The thing that bothers me about balances-by-denom is that it isn't actually a domain type but rather an index, but maybe that's okay. I also considered balances/by-denom but that conflicts with the actual balances GET query sort of.

Copy link
Contributor

Choose a reason for hiding this comment

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

Matrix parameters look interesting but also not yet widely adopted, at least as far as I've seen.

Yea, might be nice to use the actual domain type with an index. A nice thing about balances-by-denom is that the CLI command is balance-by-denom and the name of the query is BalancesByDenom.

We had inconsistent endpoint names in Regen Ledger, which we resolved by adding alternatives, each endpoint reflects the name of the query method and then we added more conventional endpoints. We might have chosen one or the other but wanted to limit breaking changes so we chose to support both in the meantime. For example:

    option (google.api.http) = {
      get : "/regen/ecocredit/v1/projects-by-class/{class_id}"
      additional_bindings : [
        {get : "/regen/ecocredit/v1/projects/class/{class_id}"},
        {get : "/regen/ecocredit/v1/classes/{class_id}/projects"}
      ]
    };

@aaronc
Copy link
Member Author

aaronc commented Feb 16, 2023

Thanks for the feedback @ryanchristo

  • converting addresses stored in state as bytes (as well as any other values that might need to be converted)

So I think this is important even though it's kind of hard.

Would it be sufficient to just include address string parameters in the query parameters and still return the objects as they are stored in state (i.e. as bytes)? That would be much simpler. Otherwise, we need to generate a whole new set of types which include the address strings. Alternatively, we can have some sort of wrapper type (ex. BalanceResult) which includes the stored type (ex. Balance) + the address string. I'm thinking also that it's good to support both address string and bytes in queries and responses in these cases. There may be cases where people want to also query raw bytes.

  • return embedded information in response (in regen ledger we often store the primary key of another state object and need to return specific information from the other state object, e.g. a project needs to return the class id stored in a class state object, a sell order needs to return the ask price denom stored in the market state object, etc.)

I was originally thinking a graphql API would be best for ORM queries. That would also play more nicely with address string/bytes conversions. The reason I'm going with the grpc approach is because that's whats' supported everywhere else in the SDK and seems like it's needed at a minimum. I think it'd be hard to traverse relationships with grpc, however. So would it be fine to consider the graphql layer an addon to be developed later? Or would it maybe be preferable to skip grpc and just do graphql? Personally, I'd rather use graphql for queries from a usability perspetive. but that would be at odds with the way everything else is done currently.

@ryanchristo
Copy link
Contributor

Would it be sufficient to just include address string parameters in the query parameters and still return the objects as they are stored in state (i.e. as bytes)? That would be much simpler. Otherwise, we need to generate a whole new set of types which include the address strings. Alternatively, we can have some sort of wrapper type (ex. BalanceResult) which includes the stored type (ex. Balance) + the address string. I'm thinking also that it's good to support both address string and bytes in queries and responses in these cases. There may be cases where people want to also query raw bytes.

I'm not in favor of returning bytes alone. As a webapp developer, I would rather not convert account bytes to an address in my web application. As a blockchain application developer, I would rather not convert when trying to debug. Maybe in some cases it would be good to support both but I haven't come across the need for bytes personally.

@ryanchristo
Copy link
Contributor

I was originally thinking a graphql API would be best for ORM queries. That would also play more nicely with address string/bytes conversions. The reason I'm going with the grpc approach is because that's whats' supported everywhere else in the SDK and seems like it's needed at a minimum. I think it'd be hard to traverse relationships with grpc, however. So would it be fine to consider the graphql layer an addon to be developed later?

I think it would be ok to consider as an add-on to be developed later. We probably wouldn't use this feature in Regen Ledger until we have graphql support but new developers could make use of the feature.

Or would it maybe be preferable to skip grpc and just do graphql? Personally, I'd rather use graphql for queries from a usability perspetive. but that would be at odds with the way everything else is done currently.

Yea, I'm not sure about this and this might be something worth exploring and discussing with other developers. Seems like a pretty big pivot. I agree though that from a usability perspective, graphql would be preferred.

@aaronc
Copy link
Member Author

aaronc commented Feb 23, 2023

So I'm thinking that what I will do based on the feedback is:

  • continue with this gRPC support even though graphql would be better because not having gRPC support doesn't seem quite right (it would give us out of the box autocli support + ADR 033 inter module queries
  • handle address bytes <-> string conversion on both request and response types, generating wrapper types when needed
  • encode secondary indexes as balances-by-denom as opposed to balances.denom

How does that sound? Any other opinions?

@ryanchristo
Copy link
Contributor

That all sounds good to me.

@github-actions
Copy link
Contributor

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@github-actions
Copy link
Contributor

github-actions bot commented May 4, 2023

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@github-actions github-actions bot added the Stale label May 4, 2023
@aaronc aaronc closed this May 4, 2023
@tac0turtle tac0turtle deleted the aaronc/orm-query branch February 21, 2024 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

3 participants