twmb / franz-go

franz-go contains a feature complete, pure Go library for interacting with Kafka from 0.8.0 through 3.8+. Producing, consuming, transacting, administrating, etc.
BSD 3-Clause "New" or "Revised" License
1.86k stars 191 forks source link

Unexpected behavior with async produce context #859

Open echo8 opened 2 weeks ago

echo8 commented 2 weeks ago

I'm using this client's producer in an HTTP service but am running into some unexpected behavior regarding context usage.

Here is a simple example:

func httpHandler(w http.ResponseWriter, r *http.Request) {
    record := &kgo.Record{
        // ...
        Context: context.Background(),
    }
    client.Produce(r.Context(), record, producePromise)
    w.WriteHeader(http.StatusNoContent)
}

func producePromise(r *kgo.Record, err error) {
    if err != nil {
        log.Println("Produce failed.", err)
    } else {
        log.Println("Produce succeeded.")
    }
}

When I call this HTTP endpoint Produce failed. context canceled is logged. So it seems that the canceling of the request's context is causing the buffered record to also be canceled. This is unexpected to me because the record's context is being set to a totally different (background) context. In this case shouldn't the request's context only be used until the record is buffered?

twmb commented 3 days ago

I'd rather update the wording on the documentation. The Context field on the record came much later than the context parameter in Produce, and is primarily intended as a kv store, but exists so that people can also use it for other purposes (cancellation).

The region where Produce itself blocks is small, and only if you've reached the internal max buffered records -- which largely shouldn't happen unless your brokers are down. Canceling the context is meant to cancel the record, not the Produce call.

echo8 commented 2 days ago

Understood, thanks for the clarification! I'll make sure to not propagate the request's context in this case then.