filer: keep a ranged read in random mode through its contiguous tail

A far ReadAt on a fresh ReaderPattern left the sequential counter at -1,
so the next buffer of the same ranged request landed on the frontier and
flipped the verdict straight back to sequential — readChunkSliceAt then
paid a whole-chunk fetch for the remainder of the range. Drop the
counter to -ModeChangeLimit when random mode is entered so the verdict
needs sustained sequential evidence to undo, matching the hysteresis an
established sequential stream already gets.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Chris LuandDevin committed 2026-10-02 09:32:40 -07:00
1 parent 3fd5e4c329
commit 8227ae3f49
2 files changed
+27 -3

No files matched your search

+6 -3
View File
@@ -54,10 +54,13 @@ func (rp *ReaderPattern) MonitorReadAt(offset int64, size int) {
if counter < ModeChangeLimit {
atomic.AddInt64(&rp.isSequentialCounter, 1)
}
} else if counter <= 0 {
// Entering random mode is a strong verdict: drop to the bottom of
// the window so the contiguous tail of one ranged request cannot
// flip it back on the next buffer read and pay a whole-chunk fetch.
atomic.StoreInt64(&rp.isSequentialCounter, -ModeChangeLimit)
} else {
if counter > -ModeChangeLimit {
atomic.AddInt64(&rp.isSequentialCounter, -1)
}
atomic.AddInt64(&rp.isSequentialCounter, -1)
}
}
+21
View File
@@ -102,3 +102,24 @@ func TestReaderPatternRecoversFromRandom(t *testing.T) {
t.Fatal("sustained near reads must recover sequential mode")
}
}
// A ranged request's first read lands far from the frontier, but its
// remaining buffer reads are contiguous. The random verdict must stick for
// them — otherwise the tail of every range >256KiB pays a whole-chunk fetch.
func TestReaderPatternRangedReadStaysRandom(t *testing.T) {
rp := NewReaderPattern()
rp.MonitorReadAt(500*mb, 256*1024) // far first read -> -ModeChangeLimit
for i := 1; i <= 2; i++ {
rp.MonitorReadAt(500*mb+int64(i)*256*1024, 256*1024)
if !rp.IsRandomMode() {
t.Fatalf("contiguous read %d of a ranged request flipped back to sequential", i+1)
}
}
for i := 3; i < 10; i++ {
rp.MonitorReadAt(500*mb+int64(i)*256*1024, 256*1024)
}
if rp.IsRandomMode() {
t.Fatal("sustained sequential reads should restore sequential mode")
}
}