Typically, a network communication system, like gRPC for example, will 
accept contexts for network calls and turn context done-ness into an error. 
The typical signature of an RPC-backed function is

    func(context.Context, req *Request) (*Response, error)

If the context times out or is cancelled, a good RPC library will return 
from this call immediately with a non-nil error. So context done-ness 
checking is just part of ordinary error checking.

I'm not sure how you're mapping the RPCs to channels. If you're doing

    res, err := RPC(ctx, req)
    if err != nil { return ... }
    ch <- res

then you indeed have a problem: you'll need another check whenever you 
receive from the channel. But if you're doing

    res, err := RPC(ctx, req)
    ch <- rpcResult{res, err}

then receivers don't have to check ctx.Done, but of course they do have to 
check the error:

    result <- ch
    if result.err != nil { ...}
    // use result.res
 
Channels are a bit awkward either way, so for the Google Cloud client 
libraries we use an iterator pattern (
https://github.com/googleapis/google-cloud-go/wiki/Iterator-Guidelines).

On Tuesday, June 9, 2020 at 8:04:21 AM UTC-4, Yegor Roganov wrote:
>
> Thanks Ian, it's nice to know that we're using Go correctly.
>
> I agree that more code doesn't really matter, and I think I'd be 
> completely fine with this situation had there been a vet check that ensured 
> that no selects are forgotten.
> Let's see if generics change something in this area.
>
> On Monday, June 8, 2020 at 10:46:05 PM UTC+3, Ian Lance Taylor wrote:
>>
>> On Mon, Jun 8, 2020 at 9:59 AM Yegor Roganov <yego...@gmail.com> wrote: 
>> > 
>> > My team is developing a cloud-based data processing application, and a 
>> large bulk of the application's job is doing network calls to other 
>> services. 
>> > Go's goroutines and channel-based communication are an excellent fit, 
>> but in order for the application to be able to properly shut down, 
>> > simple construct of sending to a channel `myChan <- value` needs to 
>> actually be something like: 
>> > 
>> > ``` 
>> > 
>> > select { 
>> > case myChan <- value: 
>> > case <-ctx.Done(): 
>> >    return ctx.Err() 
>> > } 
>> > 
>> > ``` 
>> > 
>> > As you see, a simple one line construct needs to be replaced with a 
>> select on context.Done EVERYWHERE. 
>> > My two gripes with this situation are: 
>> > 
>> > 1) It's more code and "clutter" which makes code less clear 
>> > 2) It's easy to forget to do a select on context.Done, and then an 
>> application is unable to shut down. 
>> > 
>> > Am I doing something wrong? Does someone relate with me? 
>>
>> You are not doing anything wrong, and, you're right: it's more code. 
>>
>> In general, Go prefers code to be explicit rather than implicit, so I 
>> don't really agree that this makes the code less clear.  I think that 
>> this code is clear to anybody who knows Go, and with more experience 
>> it becomes fairly idiomatic. 
>>
>> But you're absolutely right that it's easy to forget to do a select. 
>>
>> Personally I think that this is an area where generics can help.  For 
>> example, from the design draft published last year, see 
>>
>> https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-contracts.md#channels
>>  
>> .  I think the right step here is going to be see how generics can 
>> help with these common patterns before trying other approaches.  I 
>> acknowledge that that is a slow process. 
>>
>> Ian 
>>
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/de39eb18-2188-4236-ac33-6ec9109ce68fo%40googlegroups.com.

Reply via email to