-
Notifications
You must be signed in to change notification settings - Fork 24.9k
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
Add Windows native method to retrieve the number of allocated bytes on disk for file #79698
Add Windows native method to retrieve the number of allocated bytes on disk for file #79698
Conversation
Pinging @elastic/es-distributed (Team:Distributed) |
972f252
to
d13a95b
Compare
@elasticmachine run elasticsearch-ci/part-2-windows (unrelated failure) |
Took 4 hours but the test |
Test failed as expected:
Now reverting the create/create_new option and test should succeed again. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great :) I left a couple of small comments.
fill(cacheFile.getChannel(), Math.toIntExact(cacheFile.getLength() - 1L), Math.toIntExact(cacheFile.getLength())); | ||
|
||
sizeOnDisk = Natives.allocatedSizeInBytes(file); | ||
assertThat(sizeOnDisk, not(equalTo(1048576L))); | ||
assertThat("Cache file should be sparse and not fully allocated on disk", sizeOnDisk, not(equalTo(1048576L))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be asserting its size is <1MiB rather than ≠1MiB? I worry that some funny rounding (e.g. counting the size of the directory entry) could pass this assertion even for non-sparse files.
After we make this assertion could we fill the file with genuine data and assert that its size on disk is now ≥1MiB?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be asserting its size is <1MiB rather than ≠1MiB?
Yes it is more appropriate (I think I did it and then reverted it somehow 🤔 )
After we make this assertion could we fill the file with genuine data and assert that its size on disk is now ≥1MiB?
Yes too.
I pushed f109191
* @param lpFileSizeHigh pointer to high-order DWORD for compressed file size (or null if not needed) | ||
* @return the low-order DWORD for compressed file siz | ||
*/ | ||
native int GetCompressedFileSizeW(WString lpFileName, IntByReference lpFileSizeHigh); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we define this in test/framework
rather than server
since it's not used in prod code today? Or are we planning to use it in prod later on (e.g. expose sparse sizes in stats)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I'd like this to be returned as part of searchable snapshot's Cache Stats API, so that we have information about the shared cache and cold cache that sit altogether. What do you think?
I can try to make it work in the test framework; it should be OK as long as the static instance is created before the security manager is installed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also: I'd like to have assertion in CacheFile
itself that would retrieve the allocated size on disk and compares it with the completed ranges of a cache file, and verify if size vs ranges is not completly out of bounds.
That would also require the method to be in server
. I tried to make it work in the plugin itself but for JNA kernel32 library it's very difficult given all the permissions it requires (createClassLoader, setSecurityManager etc). Linux is OK though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 having these stats in future seems like a good plan, so I'm ok to leave the method where it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM (one nit)
@@ -411,7 +412,16 @@ public void testCacheFileCreatedAsSparseFile() throws Exception { | |||
fill(cacheFile.getChannel(), Math.toIntExact(cacheFile.getLength() - 1L), Math.toIntExact(cacheFile.getLength())); | |||
|
|||
sizeOnDisk = Natives.allocatedSizeInBytes(file); | |||
assertThat("Cache file should be sparse and not fully allocated on disk", sizeOnDisk, not(equalTo(1048576L))); | |||
assertThat("Cache file should be sparse and not fully allocated on disk", sizeOnDisk, lessThan(1048576L)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a slight preference for extracting the 1048576L
as a constant (it appears in 3 places now and anyway I don't immediately recognise that this is a nice power of two).
* @param lpFileSizeHigh pointer to high-order DWORD for compressed file size (or null if not needed) | ||
* @return the low-order DWORD for compressed file siz | ||
*/ | ||
native int GetCompressedFileSizeW(WString lpFileName, IntByReference lpFileSizeHigh); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 having these stats in future seems like a good plan, so I'm ok to leave the method where it is.
I'm holding merging this: there is a test failure and the method does not return the expected result. I'm investigating. |
All good, the test failure was caused by |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some suggestions, also I think there's a (super-rare) case in which we incorrectly return empty()
.
server/src/main/java/org/elasticsearch/common/filesystem/FileSystemNatives.java
Outdated
Show resolved
Hide resolved
return OptionalLong.empty(); | ||
} | ||
|
||
final long allocatedSize = (((long) lpFileSizeHigh.getValue()) << 32) | (lpFileSizeLow & 0xffffffffL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 to a comment here, although it's pretty much the standard idiom for doing this computation.
final long allocatedSize = (((long) lpFileSizeHigh.getValue()) << 32) | (lpFileSizeLow & 0xffffffffL); | |
// convert lpFileSizeLow to unsigned long and combine with signed/shifted lpFileSizeHigh | |
final long allocatedSize = (((long) lpFileSizeHigh.getValue()) << Integer.SIZE) | (lpFileSizeLow & 0xffffffffL); |
final IntByReference lpFileSizeHigh = new IntByReference(); | ||
|
||
final int lpFileSizeLow = GetCompressedFileSizeW(fileName, lpFileSizeHigh); | ||
if (lpFileSizeLow == 0xffffffff) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's still an int
here so I think we can just say -1
, but also the API docs call this constant INVALID_FILE_SIZE
. Could we use the same name here?
if (lpFileSizeLow == 0xffffffff) { | |
if (lpFileSizeLow == INVALID_FILE_SIZE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I pushed 5928b39
final int lpFileSizeLow = GetCompressedFileSizeW(fileName, lpFileSizeHigh); | ||
if (lpFileSizeLow == 0xffffffff) { | ||
final int err = Native.getLastError(); | ||
logger.warn("error [{}] when executing native method GetCompressedFileSizeW for file [{}]", err, path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lpFileSizeLow
can legitimately be INVALID_FILE_SIZE
(== -1
) if the file size is one less than a multiple of 2^32; in this case getLastError()
returns NO_ERROR
and we should carry on with the computation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow thanks for catching this David! I remember I left this aside while I was fighting to make this work with the security manager but I forgot to come back to it.
I pushed 3357e8f
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Thanks everybody! |
…n disk for file (elastic#79698) In elastic#79371 we fixed a bug where cache files were not created as sparse files on Windows platforms because the wrong options were used when creating the files for the first time. This bug got unnoticed as we were lacking a way to retrieve the exact number of bytes allocated for a given file on disk. This commit adds a FileSystemNatives.allocatedSizeInBytes(Path) method for that exact purpose (only implemented for Windows for now) and a test in CacheFileTests that would fail on Windows if the cache file is not sparse. Relates elastic#79371
…n disk for file (elastic#79698) In elastic#79371 we fixed a bug where cache files were not created as sparse files on Windows platforms because the wrong options were used when creating the files for the first time. This bug got unnoticed as we were lacking a way to retrieve the exact number of bytes allocated for a given file on disk. This commit adds a FileSystemNatives.allocatedSizeInBytes(Path) method for that exact purpose (only implemented for Windows for now) and a test in CacheFileTests that would fail on Windows if the cache file is not sparse. Relates elastic#79371
…d bytes on disk for a file Follow up elastic#79698
…n disk for file (#79698) (#80426) In #79371 we fixed a bug where cache files were not created as sparse files on Windows platforms because the wrong options were used when creating the files for the first time. This bug got unnoticed as we were lacking a way to retrieve the exact number of bytes allocated for a given file on disk. This commit adds a FileSystemNatives.allocatedSizeInBytes(Path) method for that exact purpose (only implemented for Windows for now) and a test in CacheFileTests that would fail on Windows if the cache file is not sparse. Relates #79371 Co-authored-by: Elastic Machine <[email protected]>
…n disk for file (#79698) (#80427) In #79371 we fixed a bug where cache files were not created as sparse files on Windows platforms because the wrong options were used when creating the files for the first time. This bug got unnoticed as we were lacking a way to retrieve the exact number of bytes allocated for a given file on disk. This commit adds a FileSystemNatives.allocatedSizeInBytes(Path) method for that exact purpose (only implemented for Windows for now) and a test in CacheFileTests that would fail on Windows if the cache file is not sparse. Relates #79371 Co-authored-by: Elastic Machine <[email protected]>
In #79371 we fixed a bug where cache files were not created as sparse files on Windows platforms because the wrong options were used when creating the files for the first time. This bug got unnoticed as we were lacking a way to retrieve the exact number of bytes allocated for a given file on disk.
This pull request adds a new
Natives.allocatedSizeInBytes(Path)
method for that exact purpose (only implemented for Windows for now) and a test inCacheFileTests
that would fail on Windows if the cache file is not sparse.As a next step I'd like to add support for
stat()
on Linux x86 64bits platforms, and then try to craft an appropriate assertion that would verify that files are allocated on disk according to the ranges that are written in cache (this may need some non trivial computation with block sizes etc)