mirror of https://github.com/apache/cassandra
CASSANDRA-21511: Fix page cache eviction for ranges larger than 2 GiB
NativeLibrary.trySkipCache splits ranges larger than Integer.MAX_VALUE before passing them to posix_fadvise, but incorrectly moved the offset backward after each chunk. Advance the offset so all chunks cover consecutive forward file ranges, and add deterministic unit coverage for large, boundary, nonzero-offset, and whole-file ranges.
This commit is contained in:
parent
45f04ad9fa
commit
b089b14ab5
|
|
@ -226,16 +226,27 @@ public final class NativeLibrary
|
|||
}
|
||||
|
||||
public static void trySkipCache(int fd, long offset, long len, String path)
|
||||
{
|
||||
trySkipCache(offset, len, (subOffset, subLength) -> trySkipCache(fd, subOffset, subLength, path));
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface SkipCache
|
||||
{
|
||||
void skip(long offset, int len);
|
||||
}
|
||||
|
||||
static void trySkipCache(long offset, long len, SkipCache skipCache)
|
||||
{
|
||||
if (len == 0)
|
||||
trySkipCache(fd, 0, 0, path);
|
||||
skipCache.skip(0, 0);
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
int sublen = (int) Math.min(Integer.MAX_VALUE, len);
|
||||
trySkipCache(fd, offset, sublen, path);
|
||||
skipCache.skip(offset, sublen);
|
||||
len -= sublen;
|
||||
offset -= sublen;
|
||||
offset += sublen;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
*/
|
||||
package org.apache.cassandra.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
|
@ -41,4 +43,48 @@ public class NativeLibraryTest
|
|||
long pid = NativeLibrary.getProcessID();
|
||||
Assert.assertTrue(pid > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipCacheSplitsLargeRangeAtIncreasingOffsets()
|
||||
{
|
||||
long offset = 1024;
|
||||
long length = 2L * Integer.MAX_VALUE + 1;
|
||||
List<long[]> ranges = new ArrayList<>();
|
||||
|
||||
NativeLibrary.trySkipCache(offset, length, (subOffset, subLength) -> ranges.add(new long[]{ subOffset, subLength }));
|
||||
|
||||
Assert.assertEquals(3, ranges.size());
|
||||
assertRange(ranges.get(0), offset, Integer.MAX_VALUE);
|
||||
assertRange(ranges.get(1), offset + Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||
assertRange(ranges.get(2), offset + 2L * Integer.MAX_VALUE, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipCacheDoesNotSplitMaximumIntegerRange()
|
||||
{
|
||||
long offset = 4096;
|
||||
List<long[]> ranges = new ArrayList<>();
|
||||
|
||||
NativeLibrary.trySkipCache(offset, Integer.MAX_VALUE, (subOffset, subLength) -> ranges.add(new long[]{ subOffset, subLength }));
|
||||
|
||||
Assert.assertEquals(1, ranges.size());
|
||||
assertRange(ranges.get(0), offset, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipCacheZeroLengthTargetsEntireFile()
|
||||
{
|
||||
List<long[]> ranges = new ArrayList<>();
|
||||
|
||||
NativeLibrary.trySkipCache(4096, 0, (subOffset, subLength) -> ranges.add(new long[]{ subOffset, subLength }));
|
||||
|
||||
Assert.assertEquals(1, ranges.size());
|
||||
assertRange(ranges.get(0), 0, 0);
|
||||
}
|
||||
|
||||
private static void assertRange(long[] range, long offset, int length)
|
||||
{
|
||||
Assert.assertEquals(offset, range[0]);
|
||||
Assert.assertEquals(length, range[1]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue