Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Issue 387] fix goroutine leak for closing consumers. #808

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

crossoverJie
Copy link
Member

@crossoverJie crossoverJie commented Jul 14, 2022

Fixes #387

Motivation

Fix goroutine leak for closing consumers.

Modifications

Close the c.messageCh at the same time as closing the consumer.

Before fix:

image
image

Copy link
Contributor

@pgier pgier left a comment

Choose a reason for hiding this comment

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

Is there any chance that c.messageCh can be nil? Just wondering if we need a nil check around closing it since trying to close a nil channel can cause a panic.
Otherwise LGTM

@crossoverJie
Copy link
Member Author

Is there any chance that c.messageCh can be nil? Just wondering if we need a nil check around closing it since trying to close a nil channel can cause a panic. Otherwise LGTM

Thanks for the reminder.

messageCh = make(chan ConsumerMessage, 10)

Initialization is done when the consumer is created.

@merlimat merlimat added this to the v0.9.0 milestone Jul 14, 2022
@pgier
Copy link
Contributor

pgier commented Jul 15, 2022

Looks like there is a failure in one of the tests that we're trying to close an already closed channel. I guess in some cases the channel gets closed and others it doesn't?

@crossoverJie
Copy link
Member Author

@pgier
PTAL, already adjusted.

The regexConsumer and multiTopicConsumer share the same messageCh within the same consumer, so they only need to be closed once.

This is the reason why chan was closed repeatedly before.

@@ -563,6 +566,11 @@ func (c *consumer) Close() {
}
wg.Wait()
close(c.closeCh)
closed := closeChanSet[c.messageCh]
if !closed {
Copy link
Contributor

Choose a reason for hiding this comment

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

closeChanSet is a global variable. The element in the set is never deleted. Would it be more reasonable to create a isClosed (bool) attribute in the consumer struct. It can be checked at line 569 and 570 as

if !c.isClosed {
    close(c.messageCh)
}

I just do not understand why there is a need to use a global map to track the messageCh channel which will not be GCed. Isn't a leak?

Copy link
Member Author

Choose a reason for hiding this comment

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

That's what I thought before, but in the case of multiTopicConsumer it causes the chan to be closed repeatedly.


image

This is because although the consumers are multiple instances, they use the same messageCh and can only be closed once.

So I'd like to record the closed flag for messageCh via a global variable.

With regard to the closeChanSet leak, we can set it to nil in the client.Close() method.

func (c *client) Close() {

image

Is there a better way?

Copy link
Contributor

@Gleiphir2769 Gleiphir2769 left a comment

Choose a reason for hiding this comment

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

Why not use Sync.Once()? Let closeMsgChOnce be a attribute in the consumer.

c.closeMsgChOnce.Do(func() {
	close(c.messageCh)
})

multiTopicConsumer uses newInternalConsumer to creat. You can add a parameter closeMsgChOnce in newInternalConsumer, then the consumers also share the Sync.once().

func newInternalConsumer(client *client, options ConsumerOptions, topic string,
messageCh chan ConsumerMessage, dlq *dlqRouter, rlq *retryRouter, disableForceTopicCreation bool) (*consumer, error) {

@crossoverJie
Copy link
Member Author

@Gleiphir2769 Good suggestion, thx.

Copy link
Contributor

@Gleiphir2769 Gleiphir2769 left a comment

Choose a reason for hiding this comment

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

Could you remove the redundant code comments? Otherwise, LGTM.

close(c.messageCh)
closeChanSet[c.messageCh] = true
}
})
Copy link
Contributor

Choose a reason for hiding this comment

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

Please remove these redundant code comments.

Copy link
Member Author

Choose a reason for hiding this comment

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

PTAL

Copy link
Contributor

@Gleiphir2769 Gleiphir2769 left a comment

Choose a reason for hiding this comment

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

LGTM

@wolfstudy wolfstudy modified the milestones: v0.9.0, v0.10.0 Sep 29, 2022
@RobertIndie RobertIndie modified the milestones: v0.10.0, v0.11.0 Mar 27, 2023
@RobertIndie RobertIndie modified the milestones: v0.11.0, v0.12.0 Jul 4, 2023
@RobertIndie RobertIndie modified the milestones: v0.12.0, v0.13.0 Jan 10, 2024
@RobertIndie RobertIndie modified the milestones: v0.13.0, v0.14.0 Jul 15, 2024
@RobertIndie RobertIndie modified the milestones: v0.14.0, v0.15.0 Oct 8, 2024
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.

Close consumer message channel when consumer closed.
7 participants