Lower reader buffering 02/111002/8
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 23 Mar 2024 22:41:35 +0000 (23:41 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Mon, 25 Mar 2024 09:40:34 +0000 (10:40 +0100)
We are choosing buffer for two entries. This is a legacy choice, when we
could not compact our buffer and is actively hostile to large
maxEntrySize.

At the end of the day we really need space for one full entry. Since we
are being smart re. buffer management, we do not need the doubled buffer
size -- so allocate just what we need.

One exemption is when the buffer would be less than 8KiB -- in that case
just do not bother and cache up to 8KiB as needed.

JIRA: CONTROLLER-2109
Change-Id: Ic03df7e271e59588608f07c097b3831db83d7bd7
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
atomix-storage/src/main/java/io/atomix/storage/journal/DiskFileReader.java
atomix-storage/src/main/java/io/atomix/storage/journal/JournalSegment.java

index 15c3d9b5b06ba5c797cc75fd588f20fea97aa341..7d36df238026eb6bceaa3a73d3e5e362ab82921e 100644 (file)
@@ -28,21 +28,33 @@ import org.checkerframework.checker.nullness.qual.NonNull;
  * A {@link StorageLevel#DISK} implementation of {@link FileReader}. Maintains an internal buffer.
  */
 final class DiskFileReader extends FileReader {
+    /**
+     * Just do not bother with IO smaller than this many bytes.
+     */
+    private static final int MIN_IO_SIZE = 8192;
+
     private final FileChannel channel;
     private final ByteBuffer buffer;
 
     // tracks where memory's first available byte maps to in terms of FileChannel.position()
     private int bufferPosition;
 
-    DiskFileReader(final Path path, final FileChannel channel, final int maxEntrySize) {
+    DiskFileReader(final Path path, final FileChannel channel, final int maxSegmentSize, final int maxEntrySize) {
         super(path);
         this.channel = requireNonNull(channel);
-        buffer = ByteBuffer.allocate(chooseBufferSize(maxEntrySize)).flip();
+        buffer = ByteBuffer.allocate(chooseBufferSize(maxSegmentSize, maxEntrySize)).flip();
         bufferPosition = 0;
     }
 
-    private static int chooseBufferSize(final int maxEntrySize) {
-        return (maxEntrySize + SegmentEntry.HEADER_BYTES) * 2;
+    private static int chooseBufferSize(final int maxSegmentSize, final int maxEntrySize) {
+        if (maxSegmentSize <= MIN_IO_SIZE) {
+            // just buffer the entire segment
+            return maxSegmentSize;
+        }
+
+        // one full entry plus its header, or MIN_IO_SIZE, which benefits the read of many small entries
+        final int minBufferSize = maxEntrySize + SegmentEntry.HEADER_BYTES;
+        return minBufferSize <= MIN_IO_SIZE ? MIN_IO_SIZE : minBufferSize;
     }
 
     @Override
index 81699a094ac9c0927abe32a5612ee0c5e5a242ab..118d39b3d0870e467bc922ae36e368deb21cc2f9 100644 (file)
@@ -187,7 +187,7 @@ final class JournalSegment<E> implements AutoCloseable {
     final var buffer = writer.buffer();
     final var path = file.file().toPath();
     final var fileReader = buffer != null ? new MappedFileReader(path, buffer)
-        : new DiskFileReader(path, channel, maxEntrySize);
+        : new DiskFileReader(path, channel, descriptor.maxSegmentSize(), maxEntrySize);
     final var reader = new JournalSegmentReader<>(this, fileReader, maxEntrySize, namespace);
     reader.setPosition(JournalSegmentDescriptor.BYTES);
     readers.add(reader);