Description

gin introduces context fallback logic in v1.8.0. There a lot of DB query / cache query operation in our HTTP service, which accept ctx as the first param. So those query method will be affected by ctx.

When a user cancels their HTTP request (e.g. press ctrl+c when doing curl in the terminal), ctx inside gin.Context.Request will be canceled, so those DB / cache method will raise a context canceled error.

It's working as expected since the user dropped their request. However, most Go application will handle error with the following way:

// do some DB query
result, err := DBQuery(ctx, ...)
if err != nil {
    log.Error("DB Query error", zap.Error(err)) // Here. User cancel is fine, but how to avoid those log
    return err
}

So there will be an error log output.

With the previous version like v1.7.6, fallback logic is not introduced so user cancels won't trigger those errors in the deep level of those HTTP apps.

We upgrade to v1.8.0 because there is some open tracing information that need to be set to the context (The context key is not a string but an interface, so we need to set it to gin.Context.Request.ctx, not gin.Context) and we need the fallback way.

Is there any way to keep using the fallback way but ignore users' cancel? (e.g. replace gin.Context.Request.ctx with an empty ctx)

Environment

  • go version: 1.14
  • gin version (or commit ref): v1.8.0 & v1.8.1
  • operating system: Linux & MacOS

Comment From: jialechan

result, err := DBQuery(ctx, ...)
if err != nil && err != context.Canceled {
   log.Error("DB Query error", zap.Error(err)) // Here. User cancel is fine, but how to avoid those log
    return err
}

does this solve your problem?

Comment From: jiekun

go result, err := DBQuery(ctx, ...) if err != nil && err != context.Canceled { log.Error("DB Query error", zap.Error(err)) // Here. User cancel is fine, but how to avoid those log return err }

does this solve your problem?

I think it would work. But there are a lot of err != nil in a business project and you cannot change all of them to err != nil && err != context.Canceled. I think it's not elegant to use.

Comment From: ferdikurniawan

i see this question is marked as closed but I'm curious to see what's your workaround regarding this situation, could you share your approach 🙏 ? @jiekun

Comment From: jiekun

@ferdikurniawan Unfortunately we did not make special handling for this situation. I don't like putting much special logic / wrapped code for situations like this.

We think the error helps us to know how many users canceled their requests within a short period. We just logged it, and let the error log go into our logging system, but set a whitelist for it in the alert system.

BTW if we consider this issue from another aspect, those canceled ctx help the system interrupt request processing earlier, to save (a few) unnecessary resource usage. If your application is robust enough then it should be fine anyway.

Comment From: ferdikurniawan

totally make sense, I was planning to apply the same mechanism but curious to see how you deal with this type of thing first. Thank you for sharing @jiekun !

Comment From: elmehdiabdi-src

my solution i just need that use case.

r.GET("/path", func(c *gin.Context) {

        signal := make(chan struct{})

        go func() {
            for {
                select {
                case <-signal:
                    return
                default:
                    sleep_1_hour()
                    close(signal)
                }
            }
        }()

        <-c.Request.Context().Done()

        close(signal)
    })