Skip to content

fix: fix memory leak in reaper connect function #3201

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

ionicc
Copy link

@ionicc ionicc commented Jun 5, 2025

What does this PR do?

Adds a fix to pass termination signal to reaper in the context of an error. The previous implementation had a memory leak issue due to the usage of a redundant check on an error which was returned before reaching the statement of passing the termination signal.

Why is it important?

This will make sure a memory leak does not happen if reaper.connect returns an error and properly sends a termination signal.

Related issues

…ly to avoid to memory leak

Signed-off-by: Sagar Vakkala <[email protected]>
@ionicc ionicc requested a review from a team as a code owner June 5, 2025 13:56
Copy link

netlify bot commented Jun 5, 2025

Deploy Preview for testcontainers-go ready!

Name Link
🔨 Latest commit aec3d59
🔍 Latest deploy log https://app.netlify.com/projects/testcontainers-go/deploys/6841a20d5673a00008f6da47
😎 Deploy Preview https://deploy-preview-3201--testcontainers-go.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@ionicc ionicc changed the title Fix memory leak in reaper connect function fix: Fix memory leak in reaper connect function Jun 5, 2025
@ionicc ionicc changed the title fix: Fix memory leak in reaper connect function fix: fix memory leak in reaper connect function Jun 5, 2025
Copy link
Contributor

@stevenh stevenh left a comment

Choose a reason for hiding this comment

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

I'm not sure I understand the case you're trying to fix, see the question below.

@@ -1335,15 +1335,14 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain

termSignal, err := r.Connect()
if err != nil {
if termSignal != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

question: I don't see a flow where r.Connect can return an error and termSignal, it appears to be one or the other. Can you clarify where this would come from?

Copy link
Author

@ionicc ionicc Jun 5, 2025

Choose a reason for hiding this comment

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

@stevenh Hmm, From my observation of term signal handling in these methods, The defer func written to pass the term signal is redundant as the method returns with an error before the defer func is set. See below:
From: https://github.com/testcontainers/testcontainers-go/blob/main/docker.go#L1336-L1347

termSignal, err := r.Connect()
		if err != nil {
			return nil, fmt.Errorf("reaper connect: %w", err) <-- Returns
		}

		// Cleanup on error.
		defer func() { <-- Never gets set in the case of error present
			if err != nil {
				termSignal <- true
			}
		}()

Due to this, There's a possibility of a memory leak in the case both, a term signal and an error is passed. This is also the conclusive point of the issue linked.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's not the case as err and termHandker can't both be set and err is the captured err so any error which is returned from then on will result in the term signal being handled before return.

What evidence of memory leak do you have?

Copy link
Contributor

Choose a reason for hiding this comment

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

@ionicc any thoughts on the above?

Copy link
Author

Choose a reason for hiding this comment

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

@stevenh Sorry for my late reply on this, I didn't get a chance to dig deeper into the mutually exclusive nature of r.Connect() for termSignal and err. I understand your point, the shadowing of the function signature err by original errors that are being instantiated through the func will not apply to the captured error case, Is that the intention? For ex in https://github.com/testcontainers/testcontainers-go/blob/main/docker.go#L1371, The function signature will be nil and would not be caught by the defer func.

Copy link
Contributor

@stevenh stevenh Jun 12, 2025

Choose a reason for hiding this comment

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

That's actually not shadowed, new vars are instantiated left to right only if needed, so where err is already defined a new one is not created.

In a defer statement where a named variable is in play, the value is always that of return value not what was last assigned to that variable. You can see this behaviour here.

So what the defer is doing is ensuring that the termSignal is triggered if the function encounters an error before returning.

@ionicc ionicc requested a review from stevenh June 5, 2025 22:52
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.

[Bug]: leak in reaper connect function with terminationSignal
2 participants