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] 
> <javascript:>> 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] <javascript:>.
>> To post to this group, send email to [email protected] 
>> <javascript:>.
>> 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/34768e0e-3aee-4d0b-9ea1-7a7a73a62c93%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to