Change GCS reads to skip forwards when the read offset is beyond the reader position.
This avoids throwing away GCS connections and already read data.
It is a 15x improvement for concurrent sequential reads to the same file!
@@ -27,6 +27,9 @@ import (
// end of the object comes first).
const minReadSize = 1 << 20
+// We will skip forwards in a GCS response at most this many bytes.
+const skipForwardThreshold = 1 << 20
+
// An object that knows how to read ranges within a particular generation of a
// particular GCS object. May make optimizations when it e.g. detects large
// sequential reads.
@@ -111,6 +114,19 @@ func (rr *randomReader) ReadAt(
return
}
+ // When the offset is AFTER the reader position, try to seek forward, within reason.
+ // This happens when the kernel page cache serves some data. It's very common for
+ // concurrent reads, often by only a few 128kB fuse read requests. The aim is to
+ // re-use GCS connection and avoid throwing away already read data.
+ // For parallel sequential reads to a single file, not throwing away the connections
+ // is a 15x improvement in throughput.
+ if rr.reader != nil && rr.start < offset && offset - rr.start < skipForwardThreshold {
+ bytes_to_skip := int64(offset - rr.start)
+ p := make([]byte, bytes_to_skip)
+ n, _ := rr.reader.Read(p)
+ rr.start += int64(n)
+ }
+
// If we have an existing reader but it's positioned at the wrong place,
// clean it up and throw it away.
if rr.reader != nil && rr.start != offset {
