Skip to content

Conversation

@AndreaBozzo
Copy link
Contributor

@AndreaBozzo AndreaBozzo commented Nov 14, 2025

Summary

Fixes a resource leak where the log file opened in buildLogger() was never closed, causing file descriptor leaks in long-running deployments. This PR is optional and can wait mario's review.

Problem

The Builder.buildLogger() method opens a log file (b.logsFile) but the Close() method only closes RabbitMQ connections, leaving the file descriptor open for the lifetime of the application.

Solution

Added proper log file cleanup in Builder.Close() method with nil-safe check before closing to prevent panics.

Changes

  • builder/builder.go: Added log file closure in Close() method
  • builder/builder_test.go: Created comprehensive unit tests covering 3 scenarios

Testing

All existing tests pass. New tests verify:

  • Normal case: log file is properly closed
  • No log file: Close() succeeds without error
  • Nil log file: Handles nil gracefully

Coverage increased from 0% to 15.1% for builder package.

Impact

  • Risk: Very low (defensive cleanup code only)
  • Value: High (prevents FD leaks in production)
  • Breaking Changes: None

Summary by CodeRabbit

  • Bug Fixes

    • Improved resource cleanup during shutdown to ensure log files are properly closed with appropriate error handling before other resource cleanup operations.
  • Tests

    • Added comprehensive unit tests verifying proper log file closure behavior during shutdown, covering active log files and edge cases.

Added proper cleanup of the log file opened in buildLogger() to prevent
file descriptor leaks in long-running deployments. The Close() method now
properly closes both the log file and RabbitMQ connections.

Changes:
- Added nil-safe log file closure in Builder.Close() method
- Added comprehensive unit tests to verify log file is properly closed
- Tests cover normal case, nil log file, and builder without log file
@coderabbitai
Copy link

coderabbitai bot commented Nov 14, 2025

Walkthrough

The Builder's Close() method is enhanced to properly close log files before cleaning up RabbitMQ connections, with error handling included. Three unit tests verify the file closure behavior across scenarios with active log files, no log files, and nil log files.

Changes

Cohort / File(s) Summary
Log file closure enhancement
builder/builder.go
Modified Close() method to close logsFile (if opened) with error handling prior to closing RabbitMQ connections
Close behavior tests
builder/builder_test.go
Added TestBuilderClose_LogFile to verify file closure and post-close write errors; TestBuilderClose_NoLogFile and TestBuilderClose_NilLogFile to verify no-ops when no file is present

Sequence Diagram

sequenceDiagram
    participant Caller
    participant Builder
    participant LogFile
    participant RabbitMQ

    Caller->>Builder: Close()
    
    alt logsFile is open
        Builder->>LogFile: Close()
        LogFile-->>Builder: (success or error)
    end
    
    alt RabbitMQ connections exist
        Builder->>RabbitMQ: Close/cleanup
        RabbitMQ-->>Builder: (done)
    end
    
    Builder-->>Caller: return (error if any)
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

  • Single method modification with straightforward file closure logic
  • Three consistent, repetitive unit test cases exercising the same control flow
  • No public API changes or complex dependencies to verify
  • Error handling pattern is clear and testable

Poem

🐰 A rabbit hops through resource land,
File and queue now closed by hand,
Cleanup flows like morning dew,
Proper order sees us through!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: closing a log file in Builder.Close() to prevent resource leaks, which directly matches the pull request's primary objective.
Description check ✅ Passed The description covers the problem, solution, changes made, testing performed, and impact assessment comprehensively, though it doesn't explicitly check all template checkboxes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
builder/builder.go (1)

80-98: Consider collecting all cleanup errors rather than returning on first failure.

The current implementation returns immediately if closing the log file fails, which means the RabbitMQ connections won't be closed. For cleanup methods, it's generally better to attempt all cleanup operations and aggregate any errors encountered.

Here's a more robust approach:

 func (b *Builder) Close() error {
+	var errs []error
+
 	// Close log file if it was opened
 	if b.logsFile != nil {
 		if err := b.logsFile.Close(); err != nil {
-			return err
+			errs = append(errs, fmt.Errorf("failed to close log file: %w", err))
 		}
 	}
 
 	// Close RabbitMQ connections
 	if b.rabbitMQConnection != nil {
 		if err := b.rabbitMQChannel.Close(); err != nil {
-			return err
+			errs = append(errs, fmt.Errorf("failed to close RabbitMQ channel: %w", err))
 		}
 		if err := b.rabbitMQConnection.Close(); err != nil {
-			return err
+			errs = append(errs, fmt.Errorf("failed to close RabbitMQ connection: %w", err))
 		}
 	}
-	return nil
+
+	if len(errs) > 0 {
+		return errors.Join(errs...)
+	}
+	return nil
 }

Note: errors.Join() requires Go 1.20+. If using an older version, you could combine the errors manually or use a library.

builder/builder_test.go (2)

56-65: Consider removing redundant test.

TestBuilderClose_NilLogFile is functionally identical to TestBuilderClose_NoLogFile since NewBuilder() already initializes logsFile to nil. Both tests exercise the same code path and nil-safety check.

You could remove this test to reduce duplication, or if you want to keep it for explicit documentation purposes, consider adding a comment explaining why both tests exist.


1-65: Consider adding test coverage for concurrent resource cleanup.

The current tests cover log file closure in isolation, but there's no test that verifies both the log file and RabbitMQ connections are closed together. Since Close() handles both types of resources, it would be valuable to test that scenario.

Consider adding a test like:

func TestBuilderClose_LogFileAndRabbitMQ(t *testing.T) {
	// Create a temporary directory for the test
	tmpDir := t.TempDir()
	logFilePath := tmpDir + "/test.log"

	// Create a builder instance
	builder := NewBuilder()

	// Build logger
	loggingConfig := parser.Logging{
		LogsPath: logFilePath,
	}
	err := builder.buildLogger(loggingConfig)
	assert.NoError(t, err)

	// Mock or set up RabbitMQ connections (if feasible in test environment)
	// This would require either mocking or a test RabbitMQ instance

	// Close should clean up both resources
	err = builder.Close()
	assert.NoError(t, err)

	// Verify both resources are closed
	_, err = builder.logsFile.WriteString("test")
	assert.Error(t, err)
}

Note: This may require mocking RabbitMQ connections or setting up a test instance, which could be complex. At minimum, consider testing that Close() doesn't fail when both logsFile and rabbitMQConnection are set.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e168bb4 and 6c39f9e.

📒 Files selected for processing (2)
  • builder/builder.go (1 hunks)
  • builder/builder_test.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
builder/builder_test.go (2)
builder/builder.go (2)
  • NewBuilder (174-176)
  • Builder (26-33)
parser/configurations_parser.go (1)
  • Logging (26-31)
🔇 Additional comments (2)
builder/builder_test.go (2)

47-54: LGTM!

This test correctly verifies that Close() handles the case where no log file was opened. The nil safety check in the implementation prevents any panics.


40-44: No changes needed. The test correctly verifies closed file behavior.

The verification confirms that writing to a closed file does produce an error containing "file already closed". Since the test uses assert.Contains() for substring matching rather than an exact match, it will correctly pass even though the full error format includes the file path (e.g., "write /tmp/test910371643: file already closed"). The test approach is sound and handles this behavior consistently.

@AndreaBozzo
Copy link
Contributor Author

Right now we close:
Log file
RabbitMQ channel
RabbitMQ connection

I think this is the correct order, inverse of the opening.
Thanks @CodeRabbit for the CHILL review.

@coderabbitai
Copy link

coderabbitai bot commented Nov 14, 2025

Tip

For best results, initiate chat on the files or code changes.

@AndreaBozzo, you're absolutely right! Closing resources in the reverse order of opening (LIFO - Last In, First Out) is indeed the correct approach. This ensures that dependencies are properly handled—the log file is closed first, then the RabbitMQ channel, and finally the underlying connection. This pattern prevents issues where a dependent resource might try to use an already-closed resource.

Great work on implementing this properly, and thank you for the kind words! I'm glad the review was helpful. 🐰

    (\(\
    (^.^)
    O(")_(")
   *happy hops*

@mariocandela
Copy link
Owner

Thank you @AndreaBozzo, I will review and merge in the next days :)

Copy link
Owner

@mariocandela mariocandela left a comment

Choose a reason for hiding this comment

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

LGTM

@AndreaBozzo
Copy link
Contributor Author

Ty @mariocandela for the quick review!

@mariocandela mariocandela merged commit 923886f into mariocandela:main Nov 16, 2025
4 checks passed
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.

2 participants