elixir-grpc / grpc

An Elixir implementation of gRPC
https://hex.pm/packages/grpc
Apache License 2.0
1.38k stars 212 forks source link

Add Mint client adapter #242

Closed polvalente closed 1 year ago

polvalente commented 2 years ago

After #199, we now have a contract defined for the HTTP client, so we can now implement an adapter using the Mint library and use that as a default instead of the Gun adapter

hkrutzer commented 2 years ago

I extracted the great work done in Spear into a standalone gRPC client while this repo was inactive here. It might be of help.

polvalente commented 2 years ago

@hkrutzer thanks for pointing that out! Do you want to submit it as a pull request, or should I extract the relevant code?

hkrutzer commented 2 years ago

I probably won't submit a PR anytime soon; streaming with that client can be done by sending messages, which I need for my own use case, and that doesn't look compatible with https://github.com/elixir-grpc/grpc/blob/d15bdb36e238d4352c87da4fb4bf056dc1960453/lib/grpc/client/adapter.ex

polvalente commented 2 years ago

@hkrutzer no worries! We can leave this up for grabs, then :)

tony612 commented 2 years ago

I want to give some of my previous ideas about HTTP/2 library. One reason I didn't add Mint or other HTTP/2 client in early days is I want to let client and service share the same underlying HTTP/2 code like cowlib in Cowboy and Gun. In HTTP/2 protocol, the difference between the client and server roles is small and the protocol of them is almost the same. Code will be simpler and more stable if we can do this. Unfortunately, Mint has only client now, Cowboy and Gun are not active as Mint, and there's no other better choice in Elixir community. My ideal HTTP/2 library should like:

But I didn't mean we can't add Mint or others. We can do this now given Mint is active and good. I just hope we can move to a better target slowly in the future.

polvalente commented 2 years ago

These are great points for us to keep in mind!

beligante commented 2 years ago

@polvalente

While looking close to this (took me some time to get full context about the lib mechanics lol)

The way Mint vs Gun works are a bit different.

[Tony I'm simplifying what you did in the explanation for brevity] The Gun approach that Tony took while implementing the adapter was basically to use a the :gun.await function to synchronously wait for a message to be received and process contents of the request with a recursive function until the end of the request

For Mint things are a bit different, when sending a request, the caller process starts to receive tcp messages and should be responsible for deal with them.


I would like some opinions here

To implement the adapter, I'm thinking in spawn a short lived gen_server to deal with the requests.

Thoughts on that approach? Based on the code that @hkrutzer shared here he followed the same/similar approach.

polvalente commented 2 years ago

Thanks for the follow-up investigation!

We can start by having a GenServer per connection. This might be the case where message processing serialization is not an issue, because if we're talking about the same connection, it means that we can't send/receive two different request at the same time anyway.

sleipnir commented 2 years ago

...it means that we can't send/receive two different request at the same time anyway.

Is this also true for bidirectional streams?

beligante commented 2 years ago

...it means that we can't send/receive two different request at the same time anyway.

Is this also true for bidirectional streams?

You kinda can, that's where the state management comes in. When we receive a request, I was thinking in store the information about the information about the caller process in a sort of map with the request_ref

When I receive the messages from tcp, that message comes with the request_ref so, I know which process I should interact with.

BTW, I'm talking about process here, but it might be another type of communication. Like use Stream.unfold/2 (the elixir Stream here lol - lot's Stream in the same context)

polvalente commented 2 years ago

A process will always process the messages it receives sequentially. What @sleipnir is worried about is that this could mean that a bidirectional system, which would be full-duplex in the best case, would be ensured to become half-duplex.

I'm not entirely familiar with HTTP/2, but from a connection socket point of view, this could indeed be the case.

However, I wouldn't worry about this too much at first, at least for a proof of concept implementation with Mint. In the worst case scenario we would have half the throughput for a given connection, but we can think of ways to optimize this afterwards (for instance, a handler process that splits tx and rx for a given connection into two different handlers)