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

Add client support for SwiftGRPCNIO #354

Closed
4 tasks done
glbrntt opened this issue Jan 9, 2019 · 3 comments
Closed
4 tasks done

Add client support for SwiftGRPCNIO #354

glbrntt opened this issue Jan 9, 2019 · 3 comments

Comments

@glbrntt
Copy link
Collaborator

glbrntt commented Jan 9, 2019

Question Checklist

Question Subject

SwiftGRPCNIO

Question Description

(Not a question as such.) It would be great to have client support for the NIO implementation. I've been playing around this recently and wanted to start a discussion on requirements/ideas for this; particularly around how the API looks.

@MrMage
Copy link
Collaborator

MrMage commented Jan 9, 2019

Agreed; I just haven't gotten around to this. Happy to provide guidance on this, however, once I have capacity in mid-Feb.

Some general thoughts:

  • unary calls: Could be fairly simple — take in a request proto and return a future of the response proto type, with errors provided through that future as well.
  • client streaming calls: return an object that exposes a send() method and the response proto future
  • server streaming calls: take in a request proto and a response handler that is called for each response proto, end-of-stream and error event. (No return value?)
  • bidi streaming: take in a request proto and a response handler that is called for each response proto, end-of-stream and error event. return an object that exposes a send() method.

Not sure yet how we should handle sending request metadata and receiving initial/trailing, metadata, though, especially in the non-error case (when the future is not fulfilled with a Status but with the response proto). We could add metadata handler blocks as extra arguments to the request methods, but that could bloat the generated code and get unwieldy quickly. Another alternative could be passing one extra {Unary,ClientStreaming,ServerStreaming,Bidirectional}CallOptions argument to each call that can hold all these blocks as well as e.g. request metadata (and other options of course). Another option would be to have the generated methods for the calls return a CallBuilder object. On that object, one could call setters for all these options and finish with e.g. .run(), which would then provide the return values as described in the enumeration above.

Let me know what you think

@glbrntt
Copy link
Collaborator Author

glbrntt commented Jan 10, 2019

Thanks for the insight @MrMage!

That makes sense. I like the idea of call options; it keeps the generation and client API fairly straightforward. I was thinking returning some kind of {CallType}Call object (similar to the CallBuilder you described) containing futures for metadata and status as well as a response or request providing method (for client- and bidi-streaming). This could also include a cancel method.

The calls would then be something like:

unary(request: Request, options: UnaryCallOptions) -> UnaryCall<Response>
clientStreaming(options: ClientStreamingCallOptions) -> ClientStreamingCall<Response>
serverStreaming(request: Request, options: ServerStreamingCallOptions, handler: (ResponseOrError<Response>) -> Void) -> ServerStreamingCall<Response>
bidirectionalStreaming(options: BiderctionalStreamingCallOptions, handler: (ResponseOrError<Response>) -> Void) -> BiderctionalStreamingCall<Response>

It could make for a more consistent API if unary and server streaming requests were also via a send method on the Call object:

unary(options: UnaryCallOptions) -> UnaryCall<Response>
serverStreaming(options: ServerStreamingCallOptions, handler: (ResponseOrError<Response>) -> Void) -> ServerStreamingCall<Response>

client.unary().send(request: request).whenSuccess { ... }

That feels a quite Java-esque to me though. I suppose this could be the underlying implementation with an extension provided which allows you to provide the request in the unary/serverStreaming call.

@MrMage
Copy link
Collaborator

MrMage commented Jan 10, 2019

Love the idea of having futures for {initial/trailing} metadata and status, I didn't think of those!

I understand the idea of consistent API, but for client-unary calls, the client should not be able to send more than one message. I think that's better enforced through just taking the one-and-only request message as a call argument, and makes for much more readable unary calls (which are probably 95%) of the cases. However, not sure how we could attach request metadata to those calls; we could either provide another "Options" argument or have a second method that does return a Call object. Then the variant with the "easy" API could simply call through to call .run() on the call object (reading through this more, this is probably what you meant with the last paragraph :-) ).

FYI, make sure to take a look at StreamingResponseCallContext and UnaryResponseCallContext; we might do something similar for sharing logic between streaming and bidirectional calls on the client side :-)

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

No branches or pull requests

2 participants