This looks a lot clearer. Thanks for the clarification. Sorry I misunderstood the original proposal as documented.
On Monday, 13 March 2017 17:18:57 UTC-3, [email protected] wrote: > > Ok, updated the proposal to provide a full example without builders. > > On Monday, March 13, 2017 at 12:53:15 PM UTC-7, Michael Lumish wrote: >> >> Can you show in the proposal what the trivial implementation of all of >> the interception methods would look like without the builders? >> >> On Mon, Mar 13, 2017 at 12:30 PM dduke via grpc.io < >> [email protected]> wrote: >> >>> I've updated the gRPC PR and the proposal to promote the `interceptCall` >>> function to represent the full interceptor, and also to clarify that use of >>> the builders is optional. >>> >>> >>> On Monday, March 13, 2017 at 8:11:29 AM UTC-7, [email protected] wrote: >>>> >>>> >>>> >>>> On Monday, 13 March 2017 01:19:21 UTC-3, [email protected] wrote: >>>>> >>>>> That's the same API as proposed, except with the interceptCall >>>>> function promoted to represent the interceptor. Maybe the examples are >>>>> confusing because they use the builders? This is the same example but >>>>> using >>>>> the API in the proposal: >>>>> >>>>> const interceptor = { >>>>> interceptCall: function(options) { >>>>> return new InterceptingCall(options, { >>>>> sendMessage: function(message, next) { >>>>> next(_doSomething(message)); >>>>> } >>>>> }); >>>>> } >>>>> }; >>>>> client.myCall(message, { interceptors: [interceptor] }); >>>>> >>>>> >>>>> >>>> Hello, >>>> >>>> Are you saying that this code above would work already in the current >>>> proposal? With just setting properties within creation of new >>>> InterceptingCall, and without having to use the InterceptorBuilder and >>>> CallerBuilder? Sorry, that's not how I understood it but I may have missed >>>> something. To me this code above is a lot more readable than having to use >>>> the builders. If the above code would work with the existing proposal I'm >>>> +1 :) >>>> >>>> My initial feedback was based on initial impressions when looking at >>>> the proposal and reading through the examples given. I did not find the >>>> code as readable and as intuitive as I think it could be, most of it being >>>> due to the heavy builder approach. >>>> >>>> I understand fluent interfaces (pattern of construction via with* / >>>> chained methods and builders as suggested) may make more sense for >>>> strongly >>>> typed languages; IMO in Javascript they only make sense when dealing with >>>> for DSL-y things where they help readability (like DB query builders for >>>> example) or where they may actually have execution effects (ie stream >>>> processing, and action chaining). So in this case personally I felt the >>>> whole OOP and builder approach was perhaps a bit overkill and not very >>>> intuitive. >>>> >>>> Having said that I did look into the Java documentation (I have 0 >>>> experience with gRPC in Java) and it seems that this builder approach is >>>> used extensively.It's also present in grpc-go within DialOption type. This >>>> proposal PR seems very similar to the Java API. I suppose there could be >>>> an >>>> argument made to keep consistency in the API between the platforms. If >>>> this >>>> is a goal, I am fine with that, and more platform-appropriate abstractions >>>> can be made on top if needed. I think there may already be some examples >>>> of >>>> this within Go grpc eco system. >>>> >>>> Personally I do find the approach of keeping things simple and just >>>> setting object properties more intuitive and in line with most existing >>>> Javascript API's. I suppose in some ways it comes down to on project >>>> goals. >>>> Do we want to keep API consistency between platforms, or tune the API to >>>> follow more platform-specific approaches that may add to readability and >>>> ease of entry (but go against consistency)? >>>> >>>> Anyway this is just my opinion. I am certainly not an authority on grpc >>>> and if other more knowledgeable people have provided feedback and agreed >>>> to >>>> the proposal that is cool with me. I don't want to hold anything back or >>>> enter indecision deadlock and prevent this from moving through the >>>> proposal >>>> process if everyone else is good with it. >>>> >>>> Thanks for reading, >>>> >>>> Bojan >>>> >>>> >>>>> >>>>> >>>>> On Saturday, March 11, 2017 at 4:25:40 PM UTC-8, [email protected] >>>>> wrote: >>>>>> >>>>>> Hello, >>>>>> >>>>>> +1 If that can be made to work to satisfy all the functionality that >>>>>> you're looking for, personally I think that API is MUCH better and more >>>>>> readable :) >>>>>> >>>>>> Regards, >>>>>> Bojan >>>>>> >>>>>> On Friday, 10 March 2017 18:22:15 UTC-4, [email protected] wrote: >>>>>>> >>>>>>> Bojan, >>>>>>> >>>>>>> Thanks for the feedback. I think making wider use of the RPC type >>>>>>> enums is a good idea. I don't want to add any more complexity to this >>>>>>> PR if >>>>>>> I can avoid it but I'd support a follow-up PR. >>>>>>> >>>>>>> I'm open to simplifying the API syntax if the same functionality is >>>>>>> supported (all the functionality supported by the Java client >>>>>>> interceptor >>>>>>> API). Ditching the interceptor object and making the `interceptCall` >>>>>>> function the 'interceptor' would be a simplification without removing >>>>>>> functionality. A simple intercepted call would then look like this: >>>>>>> >>>>>>> const interceptor = function(options) { >>>>>>> return new InterceptingCall(options, { >>>>>>> sendMessage: function(message, next) { >>>>>>> next(_doSomething(message)); >>>>>>> } >>>>>>> }); >>>>>>> }; >>>>>>> client.myCall(message, { interceptors: [interceptor] }); >>>>>>> >>>>>>> Thoughts? >>>>>>> >>>>>>> On Thursday, March 9, 2017 at 7:06:04 PM UTC-8, [email protected] >>>>>>> wrote: >>>>>>>> >>>>>>>> Hello, >>>>>>>> >>>>>>>> I do hope general feedback from community at large is welcome. >>>>>>>> >>>>>>>> +1 for adding const's and exports for method call types. This is >>>>>>>> something I feel has been needed and would be very useful. I think it >>>>>>>> might >>>>>>>> be better to export this as a separate package altogether, and grpc >>>>>>>> can >>>>>>>> depend on it; that way modules can require it and have access to grpc >>>>>>>> types >>>>>>>> without having to depend on the whole grpc node library, but that's >>>>>>>> somewhat a minor point and outside of the scope of this discussion. >>>>>>>> >>>>>>>> I do not have much in depth experience with gRPC in production, and >>>>>>>> just from my personal development (only with Node and a little with >>>>>>>> Go, no >>>>>>>> Java);.so maybe I am a little bit ignorant; but is there really a need >>>>>>>> for >>>>>>>> such a complicated and OOP-Y API for this? Javascript is >>>>>>>> function-first >>>>>>>> language with capability to pass around functions and objects alike >>>>>>>> simply >>>>>>>> and flexibly and most existing API's within the global Node.js >>>>>>>> ecosystem >>>>>>>> take this functional approach. We just need some way to expose an API >>>>>>>> to do >>>>>>>> pre, post hooks on events and provide means of composing middleware on >>>>>>>> client prototype functions? I really do not see why it would take 11 >>>>>>>> lines >>>>>>>> of code with 3 constructor invocations, and 2 build() calls to provide >>>>>>>> a >>>>>>>> mechanism to simply log a message being sent. What we really mean is >>>>>>>> just >>>>>>>> do this function before calling the client function. >>>>>>>> >>>>>>>> function(message, next) { >>>>>>>> logger.log(message); >>>>>>>> next(message); >>>>>>>> }) >>>>>>>> >>>>>>>> Yet we need all this extra work just to accomplish it. >>>>>>>> >>>>>>>> Similarly for the retry example, given the choice of writing >>>>>>>> something like that code, or just using a single call to async.retry, >>>>>>>> the >>>>>>>> more readable code wins, even if you need some kind of custom logic. >>>>>>>> >>>>>>>> Couldn't we just provide some mechanism of passing an object >>>>>>>> specifying the hooks we want. ie something like: >>>>>>>> >>>>>>>> { >>>>>>>> onStart: function(... whatever) { >>>>>>>> // ... >>>>>>>> }, >>>>>>>> onSend: function(message, next) { >>>>>>>> >>>>>>>> }, ... >>>>>>>> } >>>>>>>> >>>>>>>> etc... >>>>>>>> >>>>>>>> If needed we can document the interfaces the passed around objects >>>>>>>> may have to provide, if you need separate interfaces for listener, >>>>>>>> interceptor, etc... They can just be simple objects? To me having >>>>>>>> constructors and builders is adding additional noise that doesn't help >>>>>>>> readability and just makes things harder to reason about. >>>>>>>> >>>>>>>> I understand that this proposal is trying to expose something at a >>>>>>>> lower level so it doesn't have to account for different call contexts, >>>>>>>> etc, >>>>>>>> but given the ability that we can just pass around functions and >>>>>>>> arguments >>>>>>>> easily in javascript, I would think this should be doable. >>>>>>>> I haven't dug too much into it, so I might be wrong or maybe I am >>>>>>>> missing something. Even if conditional or contextual logic is needed, >>>>>>>> one >>>>>>>> could argue that that type of work falls precisely inside the >>>>>>>> implementation of the library providing the API (grpc). >>>>>>>> >>>>>>>> There are already existing examples of similar concepts of simpler >>>>>>>> functional hooks elsewhere in Node such as modules: kareem-hooks, >>>>>>>> hooks-fixed, grappling-hooks, and the hooks in mongoose.js for example. >>>>>>>> >>>>>>>> I wouldn't be surprised that if this was implemented eventually it >>>>>>>> would be abstracted into a higher level API to allow for a (IMO) saner >>>>>>>> usage. And maybe that's fine or intended. >>>>>>>> >>>>>>>> It's mentioned that a separate proposal for server interceptors >>>>>>>> using similar concepts and interfaces will be provided at a later >>>>>>>> date. >>>>>>>> Given the ubiquity of Express, and to smaller extend Restify and koa, >>>>>>>> all >>>>>>>> which provide API for composable middleware using just a simple `use` >>>>>>>> function and predetermined paradigms / idioms, I think similar API >>>>>>>> readability issues may be encountered. As a side project I've already >>>>>>>> created a simple module on top of existing grpc lib that provides >>>>>>>> similar >>>>>>>> API and functionality (https://github.com/malijs/mali). Not sure >>>>>>>> why exposing something complicated is needed. It is a personal side >>>>>>>> project >>>>>>>> that I can work on only as my life / time allows, but maybe when I get >>>>>>>> a >>>>>>>> chance I'll try and look into RFC process and try and document / >>>>>>>> propose >>>>>>>> something for server side. But again maybe the intention is to provide >>>>>>>> some >>>>>>>> kind of lower level typed interfaces, still leaving the ability to >>>>>>>> build on >>>>>>>> top to others, like what I've done. >>>>>>>> >>>>>>>> Anyway just my $0.02. Sorry I am not being negative on the proposal >>>>>>>> and since grpc is not something I use daily professionally at present >>>>>>>> I am >>>>>>>> probably somewhat ignorant and also not very invested. I am sure >>>>>>>> others in >>>>>>>> the community and this thread know better than me, but I am just >>>>>>>> trying to >>>>>>>> provide my objective opinion purely on the API based on my experience >>>>>>>> within the overall Node ecosystem. >>>>>>>> >>>>>>>> Regards, >>>>>>>> >>>>>>>> Bojan >>>>>>>> >>>>>>>> On Thursday, 9 March 2017 19:08:37 UTC-4, Michael Lumish wrote: >>>>>>>>> >>>>>>>>> It's probably fine to leave all of the options as they are now, >>>>>>>>> but options.credentials is a CallCredentials, not a >>>>>>>>> ChannelCredentials. I >>>>>>>>> think that threw me off. >>>>>>>>> >>>>>>>>> On Thu, Mar 9, 2017 at 2:38 PM dduke via grpc.io < >>>>>>>>> [email protected]> wrote: >>>>>>>>> >>>>>>>>>> The proposal says "The interceptCall method allows developers to >>>>>>>>>>> modify the call options". What are the semantics of modifying these >>>>>>>>>>> options, particularly the ones that apply to an entire client, not >>>>>>>>>>> a single >>>>>>>>>>> method (channelCredentials and host)? Is the MethodDescriptor >>>>>>>>>>> object >>>>>>>>>>> supposed to be modifiable? >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> The semantics of modifying the call options are whatever the >>>>>>>>>> semantics of modifying the options parameter to `getCall` in >>>>>>>>>> client.js are >>>>>>>>>> ( >>>>>>>>>> https://github.com/grpc/grpc/blob/master/src/node/src/client.js#L332 >>>>>>>>>> ) >>>>>>>>>> >>>>>>>>>> I could limit the options exposed to the options exposed in >>>>>>>>>> grpc-java's CallOptions class if that's preferrable: >>>>>>>>>> >>>>>>>>>> deadline >>>>>>>>>> >>>>>>>>>> authority (host) >>>>>>>>>> >>>>>>>>>> credentials >>>>>>>>>> >>>>>>>>>> customOptions >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> The MethodDescriptor should not be modified. I've added a note >>>>>>>>>> for that. >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> The three advanced examples use "StatusBuilder", but it doesn't >>>>>>>>>>> seem to be defined anywhere. >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> I've added a full example of the StatusBuilder. >>>>>>>>>> >>>>>>>>>> >>>>>>>>>>> They also only appear to work for unary calls. This should at >>>>>>>>>>> least be mentioned. >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> Each example lists the RPC types it supports: >>>>>>>>>> >>>>>>>>>> An example of a caching interceptor for *unary RPCs* which >>>>>>>>>>> stores the provided listener for later use (short-circuiting >>>>>>>>>>> the call if there is a cache hit): >>>>>>>>>>> An example retry interceptor for* unary RPCs* creates new calls >>>>>>>>>>> when the status shows a failure: >>>>>>>>>>> An example of providing fallbacks to failed requests for *unary >>>>>>>>>>> or client-streaming RPCs*: >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> I'll add comments to the example code to repeat this. >>>>>>>>>> >>>>>>>>>> >>>>>>>>>>> In the Retry example, the call is retried for any non-OK status >>>>>>>>>>> code. There are some nuances that should be considered there, like >>>>>>>>>>> idempotency and the potential to increase traffic to overwhelmed >>>>>>>>>>> servers >>>>>>>>>>> that are timing out. We already have a more complete proposal for >>>>>>>>>>> handling >>>>>>>>>>> retries in the core at >>>>>>>>>>> https://github.com/grpc/proposal/blob/master/A6.md, so I think >>>>>>>>>>> it may not be the best idea to encourage people to retry calls with >>>>>>>>>>> a >>>>>>>>>>> relatively simple implementation at a higher level. >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> I can remove the retry example if it's not useful. >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> Also, the Caching example appears to use "savedMessage" and >>>>>>>>>>> "storedMessage" interchangeably, and it does the same with >>>>>>>>>>> "savedListener" and "storedListener". And the proposal should >>>>>>>>>>> say "Implemented in: Javascript" at the top, instead of "Java, Go". >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> Fixed. >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> On Thursday, March 9, 2017 at 12:54:26 PM UTC-8, Michael Lumish >>>>>>>>>> wrote: >>>>>>>>>> >>>>>>>>>>> Overall, I think it looks better now, but I have a few nitpicks. >>>>>>>>>>> >>>>>>>>>>> The proposal says "The interceptCall method allows developers to >>>>>>>>>>> modify the call options". What are the semantics of modifying these >>>>>>>>>>> options, particularly the ones that apply to an entire client, not >>>>>>>>>>> a single >>>>>>>>>>> method (channelCredentials and host)? Is the MethodDescriptor >>>>>>>>>>> object >>>>>>>>>>> supposed to be modifiable? >>>>>>>>>>> >>>>>>>>>>> The three advanced examples use "StatusBuilder", but it doesn't >>>>>>>>>>> seem to be defined anywhere. They also only appear to work for >>>>>>>>>>> unary calls. >>>>>>>>>>> This should at least be mentioned. >>>>>>>>>>> >>>>>>>>>>> In the Retry example, the call is retried for any non-OK status >>>>>>>>>>> code. There are some nuances that should be considered there, like >>>>>>>>>>> idempotency and the potential to increase traffic to overwhelmed >>>>>>>>>>> servers >>>>>>>>>>> that are timing out. We already have a more complete proposal for >>>>>>>>>>> handling >>>>>>>>>>> retries in the core at >>>>>>>>>>> https://github.com/grpc/proposal/blob/master/A6.md, so I think >>>>>>>>>>> it may not be the best idea to encourage people to retry calls with >>>>>>>>>>> a >>>>>>>>>>> relatively simple implementation at a higher level. >>>>>>>>>>> >>>>>>>>>>> Also, the Caching example appears to use "savedMessage" and >>>>>>>>>>> "storedMessage" interchangeably, and it does the same with >>>>>>>>>>> "savedListener" >>>>>>>>>>> and "storedListener". And the proposal should say "Implemented in: >>>>>>>>>>> Javascript" at the top, instead of "Java, Go". >>>>>>>>>>> >>>>>>>>>>> On Thu, Mar 9, 2017 at 10:58 AM dduke via grpc.io < >>>>>>>>>>> [email protected]> wrote: >>>>>>>>>>> >>>>>>>>>> The proposal and the PR are now updated with the new API: >>>>>>>>>>>> https://github.com/grpc/proposal/pull/14 >>>>>>>>>>>> https://github.com/grpc/grpc/pull/9842 >>>>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>>> On Friday, February 24, 2017 at 4:16:20 PM UTC-8, >>>>>>>>>>>> [email protected] wrote: >>>>>>>>>>>>> >>>>>>>>>>>>> Looking at this more closely, I think a more flexible >>>>>>>>>>>>> outbound/inbound interceptor API will cover the use cases I >>>>>>>>>>>>> mentioned >>>>>>>>>>>>> without a separate batch-handling API. I'll work on updating the >>>>>>>>>>>>> proposal. >>>>>>>>>>>>> >>>>>>>>>>>>> On Friday, February 24, 2017 at 9:25:13 AM UTC-8, >>>>>>>>>>>>> [email protected] wrote: >>>>>>>>>>>>>> >>>>>>>>>>>>>> I agree with hiding the grpc.Call API. I'm concerned about >>>>>>>>>>>>>> the usability of the inbound/outbound interceptor API for use >>>>>>>>>>>>>> cases like >>>>>>>>>>>>>> caching. When we need to change how operations are batched or we >>>>>>>>>>>>>> want to >>>>>>>>>>>>>> short circuit the remaining interceptor stack, a purely >>>>>>>>>>>>>> operation-specific >>>>>>>>>>>>>> API is awkward and difficult to reason about. >>>>>>>>>>>>>> >>>>>>>>>>>>>> Would you consider an abstraction over the grpc.Call API like >>>>>>>>>>>>>> this? >>>>>>>>>>>>>> >>>>>>>>>>>>>> { >>>>>>>>>>>>>> interceptCall: function(callConstructor, options) { >>>>>>>>>>>>>> var call = callConstructor(options); >>>>>>>>>>>>>> return (new >>>>>>>>>>>>>> InterceptingCallBuilder(call)).withInterceptBatch(function(batch, >>>>>>>>>>>>>> next, >>>>>>>>>>>>>> responder) { >>>>>>>>>>>>>> var cachedResponse = _getCachedResponse(batch); >>>>>>>>>>>>>> if (cachedResponse) { >>>>>>>>>>>>>> var response = (new >>>>>>>>>>>>>> ResponseBuilder).withMessage(cachedResponse).withStatus(grpc.status.OK).build(); >>>>>>>>>>>>>> responder(null, response); >>>>>>>>>>>>>> } else { >>>>>>>>>>>>>> next(batch); >>>>>>>>>>>>>> } >>>>>>>>>>>>>> }).build(); >>>>>>>>>>>>>> } >>>>>>>>>>>>>> } >>>>>>>>>>>>>> >>>>>>>>>>>>>> `batch` in this example would wrap the internal client_batch >>>>>>>>>>>>>> with some accessor methods: `hasMetadata`, `getMetadata`, etc. >>>>>>>>>>>>>> The >>>>>>>>>>>>>> returned call's `interceptBatch` would have access to (and the >>>>>>>>>>>>>> ability to >>>>>>>>>>>>>> mutate) the full batch and could short circuit the remaining >>>>>>>>>>>>>> interceptor >>>>>>>>>>>>>> stack without exposing the internal API. >>>>>>>>>>>>>> >>>>>>>>>>>>>> >>>>>>>>>>>>>> On Thursday, February 23, 2017 at 1:20:38 PM UTC-8, Michael >>>>>>>>>>>>>> Lumish wrote: >>>>>>>>>>>>>>> >>>>>>>>>>>>>>> I do not like the idea of directly exposing the internal >>>>>>>>>>>>>>> grpc.Call API. It is internal for a reason, and it is not >>>>>>>>>>>>>>> currently >>>>>>>>>>>>>>> guaranteed to be stable. I would strongly prefer that the >>>>>>>>>>>>>>> "outbound >>>>>>>>>>>>>>> interceptor" and "inbound interceptor" APIs be the fundamental >>>>>>>>>>>>>>> building >>>>>>>>>>>>>>> blocks of this interceptor API. >>>>>>>>>>>>>>> >>>>>>>>>>>>>>> On Thursday, February 23, 2017 at 1:01:45 PM UTC-8, Michael >>>>>>>>>>>>>>> Lumish wrote: >>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>> A proposal for a client interceptor API for the Node.js >>>>>>>>>>>>>>>> library has been created here: >>>>>>>>>>>>>>>> https://github.com/grpc/proposal/pull/14. >>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>> Please keep discussion about it on this thread. >>>>>>>>>>>>>>>> >>>>>>>>>>>>>>> -- >>>>>>>>>>>> You received this message because you are subscribed to the >>>>>>>>>>>> Google Groups "grpc.io" group. >>>>>>>>>>>> >>>>>>>>>>> To unsubscribe from this group and stop receiving emails from >>>>>>>>>>>> it, send an email to [email protected]. >>>>>>>>>>>> To post to this group, send email to [email protected]. >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>>> Visit this group at https://groups.google.com/group/grpc-io. >>>>>>>>>>>> To view this discussion on the web visit >>>>>>>>>>>> https://groups.google.com/d/msgid/grpc-io/878bc96d-f6a7-4504-b106-6654b70de7d4%40googlegroups.com >>>>>>>>>>>> >>>>>>>>>>>> <https://groups.google.com/d/msgid/grpc-io/878bc96d-f6a7-4504-b106-6654b70de7d4%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>>>>>>>>> . >>>>>>>>>>>> For more options, visit https://groups.google.com/d/optout. >>>>>>>>>>>> >>>>>>>>>>> -- >>>>>>>>>> You received this message because you are subscribed to the >>>>>>>>>> Google Groups "grpc.io" group. >>>>>>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>>>>>> send an email to [email protected]. >>>>>>>>>> To post to this group, send email to [email protected]. >>>>>>>>>> Visit this group at https://groups.google.com/group/grpc-io. >>>>>>>>>> To view this discussion on the web visit >>>>>>>>>> https://groups.google.com/d/msgid/grpc-io/0b852ae7-d5da-4302-9596-3d71884bc631%40googlegroups.com >>>>>>>>>> >>>>>>>>>> <https://groups.google.com/d/msgid/grpc-io/0b852ae7-d5da-4302-9596-3d71884bc631%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>>>>>>> . >>>>>>>>>> For more options, visit https://groups.google.com/d/optout. >>>>>>>>>> >>>>>>>>> -- >>> You received this message because you are subscribed to the Google >>> Groups "grpc.io" group. >>> To unsubscribe from this group and stop receiving emails from it, send >>> an email to [email protected]. >>> To post to this group, send email to [email protected]. >>> Visit this group at https://groups.google.com/group/grpc-io. >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/grpc-io/504cc473-66c5-4f1b-92f1-34f3414ccf15%40googlegroups.com >>> >>> <https://groups.google.com/d/msgid/grpc-io/504cc473-66c5-4f1b-92f1-34f3414ccf15%40googlegroups.com?utm_medium=email&utm_source=footer> >>> . >>> For more options, visit https://groups.google.com/d/optout. >>> >> -- You received this message because you are subscribed to the Google Groups "grpc.io" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at https://groups.google.com/group/grpc-io. To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/a4c1793a-bf3a-4ff5-bdbf-297860a11d25%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.
