Skip to content

OAK-12098: blob-plugins - remove use of TransientFileFactory (part 2)#2744

Draft
reschke wants to merge 1 commit intotrunkfrom
OAK-12098
Draft

OAK-12098: blob-plugins - remove use of TransientFileFactory (part 2)#2744
reschke wants to merge 1 commit intotrunkfrom
OAK-12098

Conversation

@reschke
Copy link
Contributor

@reschke reschke commented Feb 16, 2026

No description provided.

@reschke
Copy link
Contributor Author

reschke commented Feb 16, 2026

OK, this fails on Linux because the file entries are removed earlier than expected by the test.

However, TransientFileFactory does not guarantee when the File objects are closed. It could happen late, it could happen at VM shutdown, or it could happen earlier than expected. This was verified with a version of the Factory that adds a method to run the cleanup immediately. I'll conclude that the tests checking the contents of the temp directory are broken.

@reschke reschke marked this pull request as draft February 16, 2026 16:13
try (InputStream in = backend.getRecord(getIdentifier()).getStream()) {
copyInputStreamToFile(in, tmpFile);
return new LazyFileInputStream(tmpFile);
return new FileInputStream(tmpFile);
Copy link
Contributor

Choose a reason for hiding this comment

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

You might have better luck returning an InputStream that deletes the tmpFile on InputStream#close(). However, that requires all users of the API to actually close the stream.

Of course this could be combined with the TransientFileFactory as a safeguard. I.e. the TransientFileFactory would only clean up after non-conforming users.

Out of curiosity, why do you want to get rid of the TransientFileFactory? Are there problems with it?

Copy link
Contributor

@rishabhdaim rishabhdaim Feb 19, 2026

Choose a reason for hiding this comment

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

Won't this cause issues, since we are deleting the file before returning the FileInputStream?

cc @amit-jain

Copy link
Contributor

@rishabhdaim rishabhdaim Feb 19, 2026

Choose a reason for hiding this comment

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

Also, this won't work well in Windows:

  • On Windows, you cannot delete a file that is open by any process.
  • On Unix-like systems, you can delete an open file, and the file's data remains accessible to processes that have it open until all handles are closed.

So basically, on Windows, we would be having garbage files under tmp folders since we are opening a Stream before calling delete.

See : https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-deletefilea

Copy link
Contributor

@rishabhdaim rishabhdaim Feb 19, 2026

Choose a reason for hiding this comment

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

Created a new to check this on MacOS:

private Object[] createTempFileAndReturnStream() throws Exception {
        File tempFile = File.createTempFile("test", ".txt");
        System.out.println(tempFile.getAbsolutePath());
        try (FileOutputStream fos = new FileOutputStream(tempFile)) {
            fos.write("hello world".getBytes());
        }
        FileInputStream fis = new FileInputStream(tempFile);
        // Delete the file immediately after opening the stream
        tempFile.delete();
        return new Object[]{fis, tempFile};
    }

    @Test
    public void testWaitForBucketNotFound() throws Exception {
        FileInputStream fis = null;
        String name = null;
        try {
            Object[] tempFileAndReturnStream = createTempFileAndReturnStream();
            fis = (FileInputStream) tempFileAndReturnStream[0];
            name = tempFileAndReturnStream[1].toString();
            byte[] buf = new byte[11];
            int read = fis.read(buf);
            Assert.assertEquals(11, read);
            Assert.assertEquals("hello world", new String(buf));
            System.out.println(Path.of(name));
            Assert.assertFalse(Files.exists(Path.of(name)));
        } finally {
            if (fis != null) {
                fis.close();
            }
            Assert.assertFalse(Files.exists(Path.of(name)));
        }
    }

On Mac, it is working fine, but I couldn't test on Windows since I don't have one.

@Joscorbe could you please try the above test on Windows if possible?

Copy link
Member

Choose a reason for hiding this comment

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

@rishabhdaim

[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.357 s <<< FAILURE! -- in org.apache.jackrabbit.oak.plugins.blob.TransientFileFactoryTest
[ERROR] org.apache.jackrabbit.oak.plugins.blob.TransientFileFactoryTest.testWaitForBucketNotFound -- Time elapsed: 0.320 s <<< FAILURE!
java.lang.AssertionError
        at org.junit.Assert.fail(Assert.java:87)
        at org.junit.Assert.assertTrue(Assert.java:42)
        at org.junit.Assert.assertFalse(Assert.java:65)
        at org.junit.Assert.assertFalse(Assert.java:75)
        at org.apache.jackrabbit.oak.plugins.blob.TransientFileFactoryTest.testWaitForBucketNotFound(TransientFileFactoryTest.java:67)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
        at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
        at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
        at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
        at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
        at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
        at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:316)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:240)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:214)
        at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:155)
        at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:385)
        at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162)
        at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:507)
        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:495)

[INFO]
[INFO] Results:
[INFO]
[ERROR] Failures:
[ERROR]   TransientFileFactoryTest.testWaitForBucketNotFound:67
[INFO]
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0

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.

4 participants