Closed rvdb closed 6 years ago
eXist-db has a TemporaryFileManager, it is used in many places. We recently expanded its use to replace some other areas where a legacy temporary file manager was used.
The problem you are seeing is indicated because the temporary file that is used by eXist-db has subsequently been opened by some other process, and so eXist-db cannot delete it when it is finished with it. This is typically a Virus Scanner or Windows Search Service.
This is actually not really a problem, and you can safely ignore it for now. I think the logging is too severe here, rather than ERROR, it should be WARN level at the most. I will send a change to fix that.
What about ... creating a thread that cleans files that are older than say once an hour in the temp dit? The disk can actually flood ....
Thanks for looking into this @adamretter. I've given it a try with https://github.com/eXist-db/exist/pull/1864, which does indeed reduce the amount of information in the logs, but does not fix the problem.
This is actually not really a problem, and you can safely ignore it for now. I think the logging is too severe here, rather than ERROR, it should be WARN level at the most. I will send a change to fix that.
Perhaps I hadn't stated clearly enough in my original report that the problem isn't just the logging, but the fact that retrieval of the binary files with http:send-request()
actually fails with the same error message.
After your commit, behaviour remains the same: in eXide, a first call to http:send-request(<http:request href="http://localhost:8080/exist/apps/dashboard/resources/images/app.gif" method="get"/>)
succeeds, but subsequent calls fail with the "exerr:ERROR Unable to add binary value to result" error.
In exist.log
, logging now is reduced to:
2018-05-10 13:57:08,412 [qtp1980097967-53] WARN (TemporaryFileManager.java [returnTemporaryFile]:108) - Unable to delete temporary file: C:\Users\RON~1.KAN\AppData\Local\Temp\_mmtfm_693a8863-3a27-4796-87c2-313e078f4ef52135163101174793632\mmtf_15259534218951031901657529973713.tmp due to: C:\Users\RON~1.KAN\AppData\Local\Temp\_mmtfm_693a8863-3a27-4796-87c2-313e078f4ef52135163101174793632\mmtf_15259534218951031901657529973713.tmp: The process cannot access the file because it is being used by another process.
I don't know if it is relevant, but if http:send-request()
is called for the first time from the Java Admin client, a Java dialog pops up containing a slightly different error message:
org.xmldb.api.base.XMLDBException: Failed to invoke method retrieveFirstChunk in class org.exist.xmlrpc.RpcConnection: java.nio.file.FileSystemException: C:\Users\RON~1.KAN\AppData\Local\Temp\_mmtfm_693a8863-3a27-4796-87c2-313e078f4ef52135163101174793632\mmtf_15259531516226044908085437603660.tmp: The requested operation cannot be performed on a file with a user-mapped section open.
at org.exist.xmldb.AbstractRemoteResource.getRemoteContentIntoLocalFile(AbstractRemoteResource.java:346)
at org.exist.xmldb.AbstractRemoteResource.getExtendedContentInternal(AbstractRemoteResource.java:420)
at org.exist.xmldb.RemoteXMLResource.getExtendedContent(RemoteXMLResource.java:224)
at org.exist.xmldb.AbstractRemoteResource.getContent(AbstractRemoteResource.java:86)
at org.exist.xmldb.RemoteXMLResource.getContent(RemoteXMLResource.java:137)
at org.exist.client.QueryDialog$QueryThread.run(QueryDialog.java:593)
Caused by: org.apache.xmlrpc.XmlRpcException: Failed to invoke method retrieveFirstChunk in class org.exist.xmlrpc.RpcConnection: java.nio.file.FileSystemException: C:\Users\RON~1.KAN\AppData\Local\Temp\_mmtfm_693a8863-3a27-4796-87c2-313e078f4ef52135163101174793632\mmtf_15259531516226044908085437603660.tmp: The requested operation cannot be performed on a file with a user-mapped section open.
at org.apache.xmlrpc.client.XmlRpcStreamTransport.readResponse(XmlRpcStreamTransport.java:197)
at org.apache.xmlrpc.client.XmlRpcStreamTransport.sendRequest(XmlRpcStreamTransport.java:156)
at org.apache.xmlrpc.client.XmlRpcHttpTransport.sendRequest(XmlRpcHttpTransport.java:143)
at org.apache.xmlrpc.client.XmlRpcSunHttpTransport.sendRequest(XmlRpcSunHttpTransport.java:69)
at org.apache.xmlrpc.client.XmlRpcClientWorker.execute(XmlRpcClientWorker.java:56)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:167)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:158)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:147)
at org.exist.xmldb.AbstractRemoteResource.getRemoteContentIntoLocalFile(AbstractRemoteResource.java:268)
... 5 more
Caused by: org.apache.xmlrpc.XmlRpcException: Failed to invoke method retrieveFirstChunk in class org.exist.xmlrpc.RpcConnection: java.nio.file.FileSystemException: C:\Users\RON~1.KAN\AppData\Local\Temp\_mmtfm_693a8863-3a27-4796-87c2-313e078f4ef52135163101174793632\mmtf_15259531516226044908085437603660.tmp: The requested operation cannot be performed on a file with a user-mapped section open.
at org.apache.xmlrpc.client.XmlRpcStreamTransport.readResponse(XmlRpcStreamTransport.java:197)
at org.apache.xmlrpc.client.XmlRpcStreamTransport.sendRequest(XmlRpcStreamTransport.java:156)
at org.apache.xmlrpc.client.XmlRpcHttpTransport.sendRequest(XmlRpcHttpTransport.java:143)
at org.apache.xmlrpc.client.XmlRpcSunHttpTransport.sendRequest(XmlRpcSunHttpTransport.java:69)
at org.apache.xmlrpc.client.XmlRpcClientWorker.execute(XmlRpcClientWorker.java:56)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:167)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:158)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:147)
at org.exist.xmldb.AbstractRemoteResource.getRemoteContentIntoLocalFile(AbstractRemoteResource.java:268)
at org.exist.xmldb.AbstractRemoteResource.getExtendedContentInternal(AbstractRemoteResource.java:420)
at org.exist.xmldb.RemoteXMLResource.getExtendedContent(RemoteXMLResource.java:224)
at org.exist.xmldb.AbstractRemoteResource.getContent(AbstractRemoteResource.java:86)
at org.exist.xmldb.RemoteXMLResource.getContent(RemoteXMLResource.java:137)
at org.exist.client.QueryDialog$QueryThread.run(QueryDialog.java:593)
When http:send-request()
is called for a second time in the Java Admin Client, the same error is shown as in eXide :
An exception occurred during query execution: exerr:ERROR exerr:ERROR Unable to add binary value to result:C:\Users\RON~1.KAN\AppData\Local\Temp\_mmtfm_693a8863-3a27-4796-87c2-313e078f4ef52135163101174793632\mmtf_15259534218951031901657529973713.tmp: The process cannot access the file because it is being used by another process.
[at line 5, column 1] [at line 5, column 1]
@adamretter , @dizzzz : unfortunately, https://github.com/eXist-db/exist/pull/1864 does not fix the underlying issue reported above, so repeated calls on http:send-request()
still fail miserably on Windows. Could someone please re-open this issue, since I can't?
@rvdb Okay I can reproduce this.
After some initial investigation, I think the error message is misleading. The problem does not actually seem to be another process.
So the problem occurs in org.exist.xquery.value.BinaryValueFromFile
the root cause of the problem is that two file handles are opened and only one is closed. However, this isn't quite the case, as we only explicitly open one file handle, the problem is Microsoft Windows is doing something strange with Memory Mapped Files.
I can in fact easily reproduce the problem (and others) outside eXist-db in some small standalone Java code (below). The issues occurs on the call to channel.map
which seems to open a second handle to the file. I have yet to find any mechanism for unmapping the mapping (without reflection by calling java.nio.DirectByteBufferR.cleaner().clean()
).
package com.company;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.UUID;
public class Main {
public static void main(final String[] args) throws IOException {
// create a temp file with some junk in it
final Path tmpFolder = Files.createTempDirectory("test-mapped" + UUID.randomUUID().toString());
final Path file = Files.createTempFile(tmpFolder, "test-mapped" + System.currentTimeMillis(), ".tmp");
writeJunk(file, 3431);
// open the temp file and map a region of it for reading
final RandomAccessFile raf = new RandomAccessFile(file.toFile(), "r");
final FileChannel channel = raf.getChannel();
final MappedByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
// close the temp file
channel.close();
raf.close();
// Error 1: attempt to write over the temp file
writeJunk(file, 123); //java.nio.file.FileSystemException ... The requested operation cannot be performed on a file with a user-mapped section open.
// Error 2: attempt to delete the temp file
//Files.delete(file); //java.nio.file.FileSystemException ... The process cannot access the file because it is being used by another process.
// Error 3: attempt to copy over the temp file
//final Path other = Files.createTempFile("other", ".tmp");
//Files.copy(other, file, StandardCopyOption.REPLACE_EXISTING); //java.nio.file.FileSystemException ... The process cannot access the file because it is being used by another process.
}
private static void writeJunk(final Path file, final int len) throws IOException {
try (final OutputStream os = Files.newOutputStream(file)) {
int val = 0;
for (int i = 0; i < len; i++) {
os.write(val++);
if (val == 10) {
val = 0;
}
}
}
}
}
It seems that there are known issues with Memory Mapped files on Microsoft Windows. I am absolutely incredulous that this can still be the case in 2018, but it seems that it is.
https://bugs.java.com/view_bug.do?bug_id=4715154 https://bugs.openjdk.java.net/browse/JDK-8028683 https://bugs.java.com/view_bug.do?bug_id=4724038
It looks like we can only use Memory Mapped IO on Microsoft Windows when the file will only be used once during the lifetime of an eXist-db instance and never reused in other ways (for example the B-Tree is fine, but mixing Memory Mapped IO ops on a file and standard IO ops is not).
This is quite the pain to say the least and will require much work!
One quicker solution, might be to differentiate between files that have been used with mapping operations and those that have not. Files that have been "mapped" cannot be reused with standard IO operations or FS operations, they can only be reused with mapping operations.
I will give this some thought...
wow thats one java 4 (!) issue still around. Reading through the comments it looks like a workaround is scheduled for java10. Did you see if that is live already?
@rvdb Sorry for the delay, I just sent a PR.
@adamretter Many thanks! I can confirm this resolves the issue (with all three InputStreamCache settings available in conf.xml, should that matter).
@rvdb Excellent thanks :-)
What is the problem
In eXist-4.1.0, repeated retrieval of binary files with the EXPath
http:send-request()
function fails with an error that seems to indicate a problem with management of temporary binary files on Windows:This happens with all possible
<binary-manager>
settings inconf.xml
, and seems to have been introduced since eXist-4.1.0; in eXist-4.0.0 (and tested down to eXist-3.6.0), this error does not occur.What did you expect
I would expect repeated retrieval of binary files with the exPath
http:send-request()
function to succeed without errors.Describe how to reproduce or add a test
On a standard eXist-4.1.0 installation (with all standard packages installed), run following XQuery twice:
While it succeeds the first time, subsequent calls throw the error mentioned above. The
exist.log
file contains this stacktrace:Further observations:
conf.xml
.Context information