Skip to content

fix: handle errors gracefully instead of panicking in async producer - #3599

Open
willy182 wants to merge 3 commits into
IBM:mainfrom
willy182:fix_loss_messages
Open

fix: handle errors gracefully instead of panicking in async producer#3599
willy182 wants to merge 3 commits into
IBM:mainfrom
willy182:fix_loss_messages

Conversation

@willy182

@willy182 willy182 commented Jun 1, 2026

Copy link
Copy Markdown

This PR replaces a panic in produce_set.go with a proper error return when message set encoding fails during produce request building

@puellanivis puellanivis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can have a little bit of copying… as a treat.

But if we copy some code, and then mutate it, we should also look into how that mutation means we might be able to simplify things.

Comment thread async_producer.go Outdated
Comment on lines +993 to +1001
sendResponse := func(response *ProduceResponse, err error) {
pending <- &brokerProducerResponse{
set: mutedSet,
err: err,
res: response,
}
wg.Done()
}
sendResponse(nil, err)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes no sense to pass response = nil as a parameter here, nor set it as a field in the &brokerProducerRespnse as nil is going to be the automatic zero value.

Additionally, there’s no reason to pass err as a parameter either, it’s already available by closure, and we’re not mutating it anyways.

Comment thread async_producer.go Outdated
Comment on lines +993 to +1001
sendResponse := func(response *ProduceResponse, err error) {
pending <- &brokerProducerResponse{
set: mutedSet,
err: err,
res: response,
}
wg.Done()
}
sendResponse(nil, err)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One can simply call an anonymous function without needing to pass it through a temporary named variable.

Suggested change
sendResponse := func(response *ProduceResponse, err error) {
pending <- &brokerProducerResponse{
set: mutedSet,
err: err,
res: response,
}
wg.Done()
}
sendResponse(nil, err)
func(response *ProduceResponse, err error) {
pending <- &brokerProducerResponse{
set: mutedSet,
err: err,
res: response,
}
wg.Done()
}(nil, err)

Comment thread async_producer.go Outdated
// Count the in flight requests to know when we can close the pending channel safely
wg.Add(1)
// capture the muted set. unmuting is deferred to handleResponse.
mutedSet := set

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We’re using go1.25 https://github.com/IBM/sarama/blob/main/go.mod#L3

Capturing a loop variable in an iteration variable if it might escape isn’t necessary.

Comment thread produce_set_test.go Outdated
}

req := ps.buildRequest()
req, _ := ps.buildRequest()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not throw away errors in tests. This is the fastest way to have an issue that could have been caught in tests get missed until it’s released to a deployment.

@willy182

willy182 commented Jun 2, 2026

Copy link
Copy Markdown
Author

We can have a little bit of copying… as a treat.

But if we copy some code, and then mutate it, we should also look into how that mutation means we might be able to simplify things.

I already changed, please review again

@dnwe

dnwe commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

I’m curious when and how you’ve hit this panic?

@puellanivis puellanivis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than this last lint, the code seems reasonable. The larger scope I leave to the team.

Comment thread async_producer.go Outdated
Comment on lines +990 to +995
wg.Add(1)
pending <- &brokerProducerResponse{
set: set,
err: err,
}
wg.Done()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spot now that the wg.Add and wg.Done are unnecessary. The WaitGroup is defined on line 984, so this wait group control is definitely per-function-call, and since the send on pending blocks execution of this goroutine, and so the for loop cannot exit while this is executing, there’s no need to use wait group coördination.

You can spot below that the wg.Done is executed at an indeterminate point in the future by the async producer, but we’re in a different situation in this block.

@puellanivis puellanivis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving just the code. The logic and fit with design lies with the main contributors.

@dnwe

dnwe commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

I’m still curious when and how you’ve hit this panic, because we should never fail to encode requests, or whether this is just a speculative PR because you spotted panic in the code?

@willy182

willy182 commented Jun 4, 2026

Copy link
Copy Markdown
Author

@dnwe Honestly, I haven't hit this panic in production. I spotted it while reviewing the codebase and realized that a panic here could crash the entire application unnecessarily. Since there's already a working error propagation path., it felt like a straightforward improvement to replace the panic with a proper error return - defensinve programming rather than a bug fix. But if I guess, maybe this Panic case could happen because the encoding/compression process failed.

@dnwe

dnwe commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

OK cool thanks - I was just concerned if we'd accidentally regressed something as I hadn't seen this panic happening

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants