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

Pager rename and refactor #1836

Merged
merged 10 commits into from
Oct 10, 2024

Conversation

analogrelay
Copy link
Member

In working on pageable APIs, I found that we needed to return Response<T> values instead of just T values. Rather than explode the generic type (Result<Pager<Response<T>>>) it felt reasonable to have Pager<T> yield a stream of Result<Response<T>> itself. In addition, Pager<T> depends on T implementing a trait, Continuable, to extract the continuation value. If we want to return Response<T> (so that users can read response headers for each page), that gets very difficult and likely requires newtypes since both Response<T> and Continuable would be foreign types to service client crates and you can't implement a foreign trait on a foreign type.

So, here are a few refactors to Pageable that I think will make it easier to use:

  1. Renamed to Pager, to align with the guidelines
  2. The callback expects a Result<(Response<T>, Option<C>)> (where C is any Sendable owned value). The expectation here is that the callback will return Ok((response, Some(continuation))) to indicate a continuation and Ok((response, None)) to indicate the end of the pagination. This avoids the need for the Continuable trait entirely.
  3. The Pager yields Result<Response<T>> values. This is really just shorthand. If we don't want to limit ourselves to HTTP here, Pager<T> could become Pager<Response<T>> without any issues, except that writing the type gets more verbose.

There's a full example in the doc comments to see how you would call this. Also azure_data_cosmos::clients::ContainerClient::query_items has been rewritten to use it.
 
I tried to see if we could get rid of the boxing by turning Pager into a trait and returning a gnarly (but largely inferred) generic type. That didn't work out, but we had boxing here before anyway and are network bound so I'm not too worried about it.

In addition, I was able to remove the declare! macro by using conditional compilation to include/exclude the Send bound. It makes for a slightly gnarly function definition (where clauses and individual trait bounds cannot be conditionally compiled, but <T: Bound> syntax can). I also removed the inner module trick since it doesn't seem like I'm getting the dead-code error on project_ref (which makes sense since it's private to the struct, so I'm not sure why it was happening at all)

@heaths
Copy link
Member

heaths commented Oct 7, 2024

Note: should see if this would close #1668

@analogrelay
Copy link
Member Author

So, I believe #1668 is tied to #1796. Requiring that Pager<T> be Send + Sync also means requiring that the futures it captures be Send + Sync, which requires that any futures they capture and await be Send + Sync. Because of #1796, our trait async fns don't have clear bounds for that. At least that how I think things are playing out when I try to add Send + Sync bounds.

@heaths
Copy link
Member

heaths commented Oct 7, 2024

Renamed to Pager, to align with the guidelines

Cool. That's what most other languages use anyway, I believe. I know Go and JS do off hand. The "pageable" was certainly inspired by my time on the Azure SDK for .NET team.

The callback expects a Result<(Response, Option)> (where C is any Sendable owned value). The expectation here is that the callback will return Ok((response, Some(continuation))) to indicate a continuation and Ok((response, None)) to indicate the end of the pagination. This avoids the need for the Continuable trait entirely.

Could we use an opaque type if public instead? It's open to change if needed - why the REST API board doesn't allow top-level JSON array responses - and more obvious what the fields are.

The Pager yields Result<Response> values. This is really just shorthand. If we don't want to limit ourselves to HTTP here, Pager could become Pager<Response> without any issues, except that writing the type gets more verbose.

Our paging is already pretty HTTP-specific across most - if not all - languages, so I think this is fine.

Copy link
Member

@heaths heaths left a comment

Choose a reason for hiding this comment

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

Overall it looks great and the thoughts and design here are welcome! As noted, I really just have one outstanding question. We could discuss over Teams if it'd be easier or lengthy, or whatever.

sdk/typespec/typespec_client_core/src/http/pager.rs Outdated Show resolved Hide resolved
sdk/cosmos/azure_data_cosmos/src/constants.rs Outdated Show resolved Hide resolved
@heaths
Copy link
Member

heaths commented Oct 8, 2024

Renamed to Pager, to align with the guidelines

Cool. That's what most other languages use anyway, I believe. I know Go and JS do off hand. The "pageable" was certainly inspired by my time on the Azure SDK for .NET team.

Actually, I remembered this morning they use Poller, but I think since we're doing the polling internally, Pager makes more sense. It's doing what a pager does as opposed to making the consumer poll the poller in a loop, which isn't idiomatic. I think if range-over-funcs existed earlier, the Go SDK would've been using that.

@analogrelay
Copy link
Member Author

Actually, I remembered this morning they use Poller, but I think since we're doing the polling internally, Pager makes more sense.

I think Poller might be something else. The Rust guidelines refer to Pager and Poller separately. The Go SDK certainly has a Pager type

@heaths
Copy link
Member

heaths commented Oct 8, 2024

You're right. I'm getting my wires cross. Poller is for LROs. Too many balls in the air right now. 🤹

Copy link
Member

@heaths heaths left a comment

Choose a reason for hiding this comment

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

Just a nit about naming, but otherwise LG(reat)TM!

sdk/typespec/typespec_client_core/src/http/pager.rs Outdated Show resolved Hide resolved
@analogrelay
Copy link
Member Author

Just need a code review from some Cosmos owners to merge now, since part of this PR updates the Cosmos client to use the refactored Pager (ping @FabianMeiswinkel @Pilchie @kirankumarkolli @kundadebdatta @tvaron3 , let me know if you have any questions! Happy to go over anything)

Copy link
Member

@Pilchie Pilchie left a comment

Choose a reason for hiding this comment

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

I looked at it before, and it looked fine to me, I just didn't want to sign-off before the broader discussion happened.

@analogrelay analogrelay merged commit 3f9148d into Azure:feature/track2 Oct 10, 2024
28 checks passed
@RickWinter
Copy link
Member

RickWinter commented Oct 10, 2024 via email

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