Skip to content

HDDS-14370. RandomAccessFileChannel to implement Closeable#9628

Open
rich7420 wants to merge 3 commits intoapache:masterfrom
rich7420:HDDS-14370
Open

HDDS-14370. RandomAccessFileChannel to implement Closeable#9628
rich7420 wants to merge 3 commits intoapache:masterfrom
rich7420:HDDS-14370

Conversation

@rich7420
Copy link
Contributor

What changes were proposed in this pull request?

The class already had a close() method but didn't implement Closeable, which could lead to resource leaks if developers miss calling close().

What is the link to the Apache JIRA

HDDS-14370

How was this patch tested?

https://github.com/rich7420/ozone/actions/runs/20941407661

Copy link
Contributor

@Gargi-jais11 Gargi-jais11 left a comment

Choose a reason for hiding this comment

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

Thanks @rich7420 for the patch.
Please see the inline comment.

Comment on lines 90 to 96
@Override
public synchronized void close() {
if (blockFile == null) {
return;
}
blockFile = null;
try {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not related to your changes but I found a bug over here.
Here, blockFile is set to null before it was used in the catch blocks
for error logging. This would cause log messages to show null instead of the actual file path when closing fails making debugging difficult.
Solution: Save the file reference so that error messages can properly display the file path.

public synchronized void close() {
    if (blockFile == null) {
      return;
    }
    final File fileToClose = blockFile;
    blockFile = null;
    try {
       channel.close();
       channel = null;
    } catch (IOException e) {
      LOG.warn("Failed to close channel for {}", fileToClose, e);
    }
    try {
     raf.close();
     raf = null;
    } catch (IOException e) {
      LOG.warn("Failed to close RandomAccessFile for {}", fileToClose, e);
    }
  }
}

Copy link
Contributor

@jojochuang jojochuang left a comment

Choose a reason for hiding this comment

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

Better to have a test file for this class: TestRandomAccessFileChannel

@Gargi-jais11
Copy link
Contributor

Below the few test cases suggestion which I could think as of now:

  1. Exception during close() — partial resource cleanup
  • Test: If channel.close() throws IOException, verify raf.close() is still called
  • Test: If raf.close() throws IOException, verify channel.close() was already attempted
  • Rationale: close() catches exceptions but must attempt to close both resources even if one fails to prevent file descriptor leaks.
  1. Partial initialization failure
  • Test: Simulate open() failing after setting blockFile but before raf/channel are set
  • Verify: close() handles null raf/channel gracefully (null checks exist, but should be tested)
  • Rationale: If open() throws mid-initialization, close() must handle partial state safely.
  1. Zero-sized buffer
  • Test: read() with ByteBuffer.allocate(0) — buffer has no remaining capacity
  • Expected: Should return immediately without reading; verify return value
  • Rationale: Edge case that could cause infinite loops or incorrect behavior.

It would be great @rich7420 if you come up with some more critical tests.

@rich7420
Copy link
Contributor Author

got it! thanks for the review @jojochuang , @Gargi-jais11 !

Copy link
Contributor

@Gargi-jais11 Gargi-jais11 left a comment

Choose a reason for hiding this comment

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

Please find the inline comments

Comment on lines +95 to 98
final File fileToClose = blockFile;
if (fileToClose == null) {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Check for null before copying blockFile.

Suggested change
final File fileToClose = blockFile;
if (fileToClose == null) {
return;
}
if (blockFile == null) {
return; // Already closed
}
// Store file reference for logging before clearing
final File fileToClose = blockFile;
blockFile = null;
}

Comment on lines 49 to 57
public synchronized void open(File file) throws FileNotFoundException {
Preconditions.assertNull(blockFile, "blockFile");
blockFile = Objects.requireNonNull(file, "blockFile == null");
raf = new RandomAccessFile(blockFile, "r");
channel = raf.getChannel();
final File f = Objects.requireNonNull(file, "blockFile == null");
final RandomAccessFile newRaf = new RandomAccessFile(f, "r");
final FileChannel newChannel = newRaf.getChannel();
blockFile = f;
raf = newRaf;
channel = newChannel;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should consider wrapping open() in try-catch to clean up on failure .
I am saying this as I suggest that :
If FileNotFoundException is thrown: blockFile is set, but raf and channel remain null.
Here the problem is that isOpen() returns true (because blockFile != null), but the object isn't usable. And will the subsequent calls to read() or position() will fail with NPE when accessing channel

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

Comments