Repository: cockroachdb/pebble
Pull request #3656 (2024-06-11 14:27:09)

crl-release-24.1: objstorage: fix preallocated handle initialization and add readahead configuration
#### vfs: add Open options to loggingFS

Log the options passed to `Open`.

#### objstorage: fix preallocated handle initialization

We were not initializing the readahead state when using a preallocated
handle. We add an assertion that would have failed and improve an
existing test to use the preallocated handle sometimes.

We also use a preallocated handle for the single level iterator (using
it only for the two level was an omission).

#### objstorage: add readahead configuration

This commit adds a way to configure the read-ahead method, for both
cases of "informed readahead" (currently just compactions, but in the
future possibly backup scans as well) and "speculative readahead".



Patch:
diff --git a/objstorage/objstorageprovider/provider.go b/objstorage/objstorageprovider/provider.go
index 499fb4ba..ea22bbbf 100644
--- a/objstorage/objstorageprovider/provider.go
+++ b/objstorage/objstorageprovider/provider.go
@@ -87,6 +87,17 @@ type Settings struct {
 	// out a large chunk of dirty filesystem buffers.
 	BytesPerSync int
 
+	// Local contains fields that are only relevant for files stored on the local
+	// filesystem.
+	Local struct {
+		// TODO(radu): move FSCleaner, NoSyncOnClose, BytesPerSync here.
+
+		// ReadaheadConfigFn is a function used to retrieve the current readahead
+		// mode. This function is run whenever a local object is open for reading.
+		// If it is nil, DefaultReadaheadConfig is used.
+		ReadaheadConfigFn func() ReadaheadConfig
+	}
+
 	// Fields here are set only if the provider is to support remote objects
 	// (experimental).
 	Remote struct {
@@ -124,6 +135,43 @@ type Settings struct {
 	}
 }
 
+// ReadaheadConfig controls the use of read-ahead.
+type ReadaheadConfig struct {
+	// Informed is the type of read-ahead for operations that are known to read a
+	// large consecutive chunk of a file.
+	Informed ReadaheadMode
+
+	// Speculative is the type of read-ahead used automatically, when consecutive
+	// reads are detected.
+	Speculative ReadaheadMode
+}
+
+// DefaultReadaheadConfig is the readahead config used when ReadaheadConfigFn is
+// not specified.
+var DefaultReadaheadConfig = ReadaheadConfig{
+	Informed:    FadviseSequential,
+	Speculative: FadviseSequential,
+}
+
+// ReadaheadMode indicates the type of read-ahead to use, either for informed
+// read-ahead (e.g. compactions) or speculative read-ahead.
+type ReadaheadMode uint8
+
+const (
+	// NoReadahead disables readahead altogether.
+	NoReadahead ReadaheadMode = iota
+
+	// SysReadahead enables the use of SYS_READAHEAD call to prefetch data.
+	// The prefetch window grows dynamically as consecutive writes are detected.
+	SysReadahead
+
+	// FadviseSequential enables the use of FADV_SEQUENTIAL. For informed
+	// read-ahead, FADV_SEQUENTIAL is used from the beginning. For speculative
+	// read-ahead SYS_READAHEAD is first used until the window reaches the maximum
+	// size, then we switch to FADV_SEQUENTIAL.
+	FadviseSequential
+)
+
 // DefaultSettings initializes default settings (with no remote storage),
 // suitable for tests and tools.
 func DefaultSettings(fs vfs.FS, dirName string) Settings {
diff --git a/objstorage/objstorageprovider/readahead.go b/objstorage/objstorageprovider/readahead.go
index e07017c9..de9ff714 100644
--- a/objstorage/objstorageprovider/readahead.go
+++ b/objstorage/objstorageprovider/readahead.go
@@ -4,6 +4,8 @@
 
 package objstorageprovider
 
+import "github.com/cockroachdb/pebble/internal/invariants"
+
 const (
 	// Constants for dynamic readahead of data blocks. Note that the size values
 	// make sense as some multiple of the default block size; and they should
@@ -81,6 +83,9 @@ func (rs *readaheadState) recordCacheHit(offset, blockLength int64) {
 // Returns a size value (greater than 0) that should be prefetched if readahead
 // would be beneficial.
 func (rs *readaheadState) maybeReadahead(offset, blockLength int64) int64 {
+	if invariants.Enabled && rs.maxReadaheadSize == 0 {
+		panic("readaheadState not initialized")
+	}
 	currentReadEnd := offset + blockLength
 	if rs.numReads >= minFileReadsForReadahead {
 		// The minimum threshold of sequential reads to justify reading ahead
diff --git a/objstorage/objstorageprovider/vfs.go b/objstorage/objstorageprovider/vfs.go
index 5b202517..9e23815a 100644
--- a/objstorage/objstorageprovider/vfs.go
+++ b/objstorage/objstorageprovider/vfs.go
@@ -31,7 +31,11 @@ func (p *provider) vfsOpenForReading(
 		}
 		return nil, err
 	}
-	return newFileReadable(file, p.st.FS, filename)
+	readaheadConfig := DefaultReadaheadConfig
+	if f := p.st.Local.ReadaheadConfigFn; f != nil {
+		readaheadConfig = f()
+	}
+	return newFileReadable(file, p.st.FS, readaheadConfig, filename)
 }
 
 func (p *provider) vfsCreate(
diff --git a/objstorage/objstorageprovider/vfs_readable.go b/objstorage/objstorageprovider/vfs_readable.go
index 93624646..584f6a90 100644
--- a/objstorage/objstorageprovider/vfs_readable.go
+++ b/objstorage/objstorageprovider/vfs_readable.go
@@ -27,22 +27,26 @@ type fileReadable struct {
 
 	// The following fields are used to possibly open the file again using the
 	// sequential reads option (see vfsReadHandle).
-	filename string
-	fs       vfs.FS
+	filename        string
+	fs              vfs.FS
+	readaheadConfig ReadaheadConfig
 }
 
 var _ objstorage.Readable = (*fileReadable)(nil)
 
-func newFileReadable(file vfs.File, fs vfs.FS, filename string) (*fileReadable, error) {
+func newFileReadable(
+	file vfs.File, fs vfs.FS, readaheadConfig ReadaheadConfig, filename string,
+) (*fileReadable, error) {
 	info, err := file.Stat()
 	if err != nil {
 		return nil, err
 	}
 	r := &fileReadable{
-		file:     file,
-		size:     info.Size(),
-		filename: filename,
-		fs:       fs,
+		file:            file,
+		size:            info.Size(),
+		filename:        filename,
+		fs:              fs,
+		readaheadConfig: readaheadConfig,
 	}
 	invariants.SetFinalizer(r, func(obj interface{}) {
 		if obj.(*fileReadable).file != nil {
@@ -76,14 +80,14 @@ func (r *fileReadable) Size() int64 {
 // NewReadHandle is part of the objstorage.Readable interface.
 func (r *fileReadable) NewReadHandle(_ context.Context) objstorage.ReadHandle {
 	rh := readHandlePool.Get().(*vfsReadHandle)
-	rh.r = r
-	rh.rs = makeReadaheadState(fileMaxReadaheadSize)
+	rh.init(r)
 	return rh
 }
 
 type vfsReadHandle struct {
-	r  *fileReadable
-	rs readaheadState
+	r             *fileReadable
+	rs            readaheadState
+	readaheadMode ReadaheadMode
 
 	// sequentialFile holds a file descriptor to the same underlying File,
 	// except with fadvise(FADV_SEQUENTIAL) called on it to take advantage of
@@ -108,6 +112,14 @@ var readHandlePool = sync.Pool{
 	},
 }
 
+func (rh *vfsReadHandle) init(r *fileReadable) {
+	*rh = vfsReadHandle{
+		r:             r,
+		rs:            makeReadaheadState(fileMaxReadaheadSize),
+		readaheadMode: r.readaheadConfig.Speculative,
+	}
+}
+
 // Close is part of the objstorage.ReadHandle interface.
 func (rh *vfsReadHandle) Close() error {
 	var err error
@@ -121,14 +133,17 @@ func (rh *vfsReadHandle) Close() error {
 
 // ReadAt is part of the objstorage.ReadHandle interface.
 func (rh *vfsReadHandle) ReadAt(_ context.Context, p []byte, offset int64) error {
-	var n int
-	var err error
 	if rh.sequentialFile != nil {
 		// Use OS-level read-ahead.
-		n, err = rh.sequentialFile.ReadAt(p, offset)
-	} else {
+		n, err := rh.sequentialFile.ReadAt(p, offset)
+		if invariants.Enabled && err == nil && n != len(p) {
+			panic("short read")
+		}
+		return err
+	}
+	if rh.readaheadMode != NoReadahead {
 		if readaheadSize := rh.rs.maybeReadahead(offset, int64(len(p))); readaheadSize > 0 {
-			if readaheadSize >= fileMaxReadaheadSize {
+			if rh.readaheadMode == FadviseSequential && readaheadSize >= fileMaxReadaheadSize {
 				// We've reached the maximum readahead size. Beyond this point, rely on
 				// OS-level readahead.
 				rh.switchToOSReadahead()
@@ -136,8 +151,8 @@ func (rh *vfsReadHandle) ReadAt(_ context.Context, p []byte, offset int64) error
 				_ = rh.r.file.Prefetch(offset, readaheadSize)
 			}
 		}
-		n, err = rh.r.file.ReadAt(p, offset)
 	}
+	n, err := rh.r.file.ReadAt(p, offset)
 	if invariants.Enabled && err == nil && n != len(p) {
 		panic("short read")
 	}
@@ -146,10 +161,16 @@ func (rh *vfsReadHandle) ReadAt(_ context.Context, p []byte, offset int64) error
 
 // SetupForCompaction is part of the objstorage.ReadHandle interface.
 func (rh *vfsReadHandle) SetupForCompaction() {
-	rh.switchToOSReadahead()
+	rh.readaheadMode = rh.r.readaheadConfig.Informed
+	if rh.readaheadMode == FadviseSequential {
+		rh.switchToOSReadahead()
+	}
 }
 
 func (rh *vfsReadHandle) switchToOSReadahead() {
+	if invariants.Enabled && rh.readaheadMode != FadviseSequential {
+		panic("readheadMode not respected")
+	}
 	if rh.sequentialFile != nil {
 		return
 	}
@@ -164,8 +185,8 @@ func (rh *vfsReadHandle) switchToOSReadahead() {
 
 // RecordCacheHit is part of the objstorage.ReadHandle interface.
 func (rh *vfsReadHandle) RecordCacheHit(_ context.Context, offset, size int64) {
-	if rh.sequentialFile != nil {
-		// Using OS-level readahead, so do nothing.
+	if rh.sequentialFile != nil || rh.readaheadMode == NoReadahead {
+		// Using OS-level or no readahead, so do nothing.
 		return
 	}
 	rh.rs.recordCacheHit(offset, size)
@@ -208,8 +229,7 @@ func UsePreallocatedReadHandle(
 	ctx context.Context, readable objstorage.Readable, rh *PreallocatedReadHandle,
 ) objstorage.ReadHandle {
 	if r, ok := readable.(*fileReadable); ok {
-		// See fileReadable.NewReadHandle.
-		rh.vfsReadHandle = vfsReadHandle{r: r}
+		rh.init(r)
 		return rh
 	}
 	return readable.NewReadHandle(ctx)
diff --git a/open.go b/open.go
index c043732c..b2dd9c6f 100644
--- a/open.go
+++ b/open.go
@@ -297,6 +297,7 @@ func Open(dirname string, opts *Options) (db *DB, err error) {
 		NoSyncOnClose:       opts.NoSyncOnClose,
 		BytesPerSync:        opts.BytesPerSync,
 	}
+	providerSettings.Local.ReadaheadConfigFn = opts.Local.ReadaheadConfigFn
 	providerSettings.Remote.StorageFactory = opts.Experimental.RemoteStorage
 	providerSettings.Remote.CreateOnShared = opts.Experimental.CreateOnShared
 	providerSettings.Remote.CreateOnSharedLocator = opts.Experimental.CreateOnSharedLocator
diff --git a/options.go b/options.go
index 876e804a..eda2a6bb 100644
--- a/options.go
+++ b/options.go
@@ -21,6 +21,7 @@ import (
 	"github.com/cockroachdb/pebble/internal/humanize"
 	"github.com/cockroachdb/pebble/internal/keyspan"
 	"github.com/cockroachdb/pebble/internal/manifest"
+	"github.com/cockroachdb/pebble/objstorage/objstorageprovider"
 	"github.com/cockroachdb/pebble/objstorage/remote"
 	"github.com/cockroachdb/pebble/rangekey"
 	"github.com/cockroachdb/pebble/sstable"
@@ -494,6 +495,15 @@ type Options struct {
 	// The default cleaner uses the DeleteCleaner.
 	Cleaner Cleaner
 
+	// Local contains option that pertain to files stored on the local filesystem.
+	Local struct {
+		// ReadaheadConfigFn is a function used to retrieve the current readahead
+		// mode. This function is consulted when a table enters the table cache.
+		ReadaheadConfigFn func() ReadaheadConfig
+
+		// TODO(radu): move BytesPerSync, LoadBlockSema, Cleaner here.
+	}
+
 	// Comparer defines a total ordering over the space of []byte keys: a 'less
 	// than' relationship. The same comparison algorithm must be used for reads
 	// and writes over the lifetime of the DB.
@@ -1111,6 +1121,9 @@ type WALFailoverOptions struct {
 	wal.FailoverOptions
 }
 
+// ReadaheadConfig controls the use of read-ahead.
+type ReadaheadConfig = objstorageprovider.ReadaheadConfig
+
 // DebugCheckLevels calls CheckLevels on the provided database.
 // It may be set in the DebugCheck field of Options to check
 // level invariants whenever a new version is installed.
diff --git a/sstable/reader_iter_two_lvl.go b/sstable/reader_iter_two_lvl.go
index cf6b4e3d..7271b07d 100644
--- a/sstable/reader_iter_two_lvl.go
+++ b/sstable/reader_iter_two_lvl.go
@@ -11,6 +11,7 @@ import (
 
 	"github.com/cockroachdb/pebble/internal/base"
 	"github.com/cockroachdb/pebble/internal/invariants"
+	"github.com/cockroachdb/pebble/objstorage/objstorageprovider"
 	"github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing"
 )
 
@@ -184,7 +185,7 @@ func (i *twoLevelIterator) init(
 		_ = i.topLevelIndex.Close()
 		return err
 	}
-	i.dataRH = r.readable.NewReadHandle(ctx)
+	i.dataRH = objstorageprovider.UsePreallocatedReadHandle(ctx, r.readable, &i.dataRHPrealloc)
 	if r.tableFormat >= TableFormatPebblev3 {
 		if r.Properties.NumValueBlocks > 0 {
 			i.vbReader = &valueBlockReader{
diff --git a/vfs/logging_fs.go b/vfs/logging_fs.go
index 820c081c..391b2560 100644
--- a/vfs/logging_fs.go
+++ b/vfs/logging_fs.go
@@ -5,6 +5,7 @@
 package vfs
 
 import (
+	"fmt"
 	"io"
 	"os"
 )
@@ -38,7 +39,15 @@ func (fs *loggingFS) Create(name string) (File, error) {
 }
 
 func (fs *loggingFS) Open(name string, opts ...OpenOption) (File, error) {
-	fs.logFn("open: %s", name)
+	var optsStr string
+	if len(opts) > 0 {
+		optsStr = " (options:"
+		for i := range opts {
+			optsStr += fmt.Sprintf(" %T", opts[i])
+		}
+		optsStr += ")"
+	}
+	fs.logFn("open: %s%s", name, optsStr)
 	f, err := fs.FS.Open(name, opts...)
 	if err != nil {
 		return nil, err


Test patch:
diff --git a/objstorage/objstorageprovider/provider_test.go b/objstorage/objstorageprovider/provider_test.go
index 141905cb..1f0c0a69 100644
--- a/objstorage/objstorageprovider/provider_test.go
+++ b/objstorage/objstorageprovider/provider_test.go
@@ -40,7 +40,9 @@ func TestProvider(t *testing.T) {
 		backings := make(map[string]objstorage.RemoteObjectBacking)
 		backingHandles := make(map[string]objstorage.RemoteObjectBackingHandle)
 		var curProvider objstorage.Provider
+		readaheadConfig := DefaultReadaheadConfig
 		datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string {
+			readaheadConfig = DefaultReadaheadConfig
 			scanArgs := func(desc string, args ...interface{}) {
 				t.Helper()
 				if len(d.CmdArgs) != len(args) {
@@ -68,6 +70,9 @@ func TestProvider(t *testing.T) {
 					st.Remote.CreateOnShared = remote.CreateOnSharedAll
 					st.Remote.CreateOnSharedLocator = ""
 				}
+				st.Local.ReadaheadConfigFn = func() ReadaheadConfig {
+					return readaheadConfig
+				}
 				require.NoError(t, fs.MkdirAll(fsDir, 0755))
 				p, err := Open(st)
 				require.NoError(t, err)
@@ -170,18 +175,41 @@ func TestProvider(t *testing.T) {
 				return log.String()
 
 			case "read":
-				forCompaction := false
-				if len(d.CmdArgs) == 2 && d.CmdArgs[1].Key == "for-compaction" {
-					d.CmdArgs = d.CmdArgs[:1]
-					forCompaction = true
+				forCompaction := d.HasArg("for-compaction")
+				if arg, ok := d.Arg("readahead"); ok {
+					var mode ReadaheadMode
+					switch arg.Vals[0] {
+					case "off":
+						mode = NoReadahead
+					case "sys-readahead":
+						mode = SysReadahead
+					case "fadvise-sequential":
+						mode = FadviseSequential
+					default:
+						d.Fatalf(t, "unknown readahead mode %s", arg.Vals[0])
+					}
+					if forCompaction {
+						readaheadConfig.Informed = mode
+					} else {
+						readaheadConfig.Speculative = mode
+					}
 				}
+
+				d.CmdArgs = d.CmdArgs[:1]
 				var fileNum base.DiskFileNum
-				scanArgs("<file-num> [for-compaction]", &fileNum)
+				scanArgs("<file-num> [for-compaction] [readahead|speculative-overhead=off|sys-readahead|fadvise-sequential]", &fileNum)
 				r, err := curProvider.OpenForReading(ctx, base.FileTypeTable, fileNum, objstorage.OpenOptions{})
 				if err != nil {
 					return err.Error()
 				}
-				rh := r.NewReadHandle(ctx)
+				var rh objstorage.ReadHandle
+				// Test both ways of getting a handle.
+				if rand.Intn(2) == 0 {
+					rh = r.NewReadHandle(ctx)
+				} else {
+					var prealloc PreallocatedReadHandle
+					rh = UsePreallocatedReadHandle(ctx, r, &prealloc)
+				}
 				if forCompaction {
 					rh.SetupForCompaction()
 				}
diff --git a/objstorage/objstorageprovider/testdata/provider/local b/objstorage/objstorageprovider/testdata/provider/local
index a2f4ce37..c2bba671 100644
--- a/objstorage/objstorageprovider/testdata/provider/local
+++ b/objstorage/objstorageprovider/testdata/provider/local
@@ -17,7 +17,7 @@ read 1
 0 1024
 512 1024
 ----
-<local fs> open: p0/000001.sst
+<local fs> open: p0/000001.sst (options: *vfs.randomReadsOption)
 size: 1024
 <local fs> read-at(0, 512): p0/000001.sst
 0 512: ok (salt 1)
@@ -41,7 +41,7 @@ read 2
 0 1024
 512 1024
 ----
-<local fs> open: p0/000002.sst
+<local fs> open: p0/000002.sst (options: *vfs.randomReadsOption)
 size: 1024
 <local fs> read-at(0, 512): p0/000002.sst
 0 512: ok (salt 2)
@@ -73,7 +73,7 @@ link-or-copy 3 local 3 100
 read 3
 0 100
 ----
-<local fs> open: p0/000003.sst
+<local fs> open: p0/000003.sst (options: *vfs.randomReadsOption)
 size: 100
 <local fs> read-at(0, 100): p0/000003.sst
 0 100: ok (salt 3)
@@ -88,7 +88,7 @@ link-or-copy 4 shared 4 1234
 read 4
 0 1234
 ----
-<local fs> open: p0/000004.sst
+<local fs> open: p0/000004.sst (options: *vfs.randomReadsOption)
 size: 1234
 <local fs> read-at(0, 1234): p0/000004.sst
 0 1234: ok (salt 4)
diff --git a/objstorage/objstorageprovider/testdata/provider/local_readahead b/objstorage/objstorageprovider/testdata/provider/local_readahead
index 87266cc4..8ad754ed 100644
--- a/objstorage/objstorageprovider/testdata/provider/local_readahead
+++ b/objstorage/objstorageprovider/testdata/provider/local_readahead
@@ -17,8 +17,8 @@ create 1 local 1 2000000
 <local fs> sync-data: p1/000001.sst
 <local fs> close: p1/000001.sst
 
-# We should see prefetch calls, and eventually a reopen
-# (with sequential reads option).
+# We should see prefetch calls, and eventually a reopen with sequential reads
+# option.
 read 1
 0 1000
 1000 15000
@@ -28,7 +28,7 @@ read 1
 106000 30000
 140000 80000
 ----
-<local fs> open: p1/000001.sst
+<local fs> open: p1/000001.sst (options: *vfs.randomReadsOption)
 size: 2000000
 <local fs> read-at(0, 1000): p1/000001.sst
 0 1000: ok (salt 1)
@@ -44,8 +44,141 @@ size: 2000000
 56000 50000: ok (salt 1)
 <local fs> read-at(106000, 30000): p1/000001.sst
 106000 30000: ok (salt 1)
-<local fs> open: p1/000001.sst
+<local fs> open: p1/000001.sst (options: *vfs.sequentialReadsOption)
 <local fs> read-at(140000, 80000): p1/000001.sst
 140000 80000: ok (salt 1)
 <local fs> close: p1/000001.sst
 <local fs> close: p1/000001.sst
+
+# We should directly see a reopen with sequential reads option.
+read 1 for-compaction
+0 1000
+1000 15000
+----
+<local fs> open: p1/000001.sst (options: *vfs.randomReadsOption)
+<local fs> open: p1/000001.sst (options: *vfs.sequentialReadsOption)
+size: 2000000
+<local fs> read-at(0, 1000): p1/000001.sst
+0 1000: ok (salt 1)
+<local fs> read-at(1000, 15000): p1/000001.sst
+1000 15000: ok (salt 1)
+<local fs> close: p1/000001.sst
+<local fs> close: p1/000001.sst
+
+# Test non-default readahead modes.
+
+read 1 readahead=off
+0 1000
+1000 15000
+16000 30000
+46000 10000
+56000 50000
+106000 30000
+140000 80000
+----
+<local fs> open: p1/000001.sst (options: *vfs.randomReadsOption)
+size: 2000000
+<local fs> read-at(0, 1000): p1/000001.sst
+0 1000: ok (salt 1)
+<local fs> read-at(1000, 15000): p1/000001.sst
+1000 15000: ok (salt 1)
+<local fs> read-at(16000, 30000): p1/000001.sst
+16000 30000: ok (salt 1)
+<local fs> read-at(46000, 10000): p1/000001.sst
+46000 10000: ok (salt 1)
+<local fs> read-at(56000, 50000): p1/000001.sst
+56000 50000: ok (salt 1)
+<local fs> read-at(106000, 30000): p1/000001.sst
+106000 30000: ok (salt 1)
+<local fs> read-at(140000, 80000): p1/000001.sst
+140000 80000: ok (salt 1)
+<local fs> close: p1/000001.sst
+
+read 1 for-compaction readahead=off
+0 1000
+1000 15000
+16000 30000
+46000 10000
+56000 50000
+106000 30000
+140000 80000
+----
+<local fs> open: p1/000001.sst (options: *vfs.randomReadsOption)
+size: 2000000
+<local fs> read-at(0, 1000): p1/000001.sst
+0 1000: ok (salt 1)
+<local fs> read-at(1000, 15000): p1/000001.sst
+1000 15000: ok (salt 1)
+<local fs> read-at(16000, 30000): p1/000001.sst
+16000 30000: ok (salt 1)
+<local fs> read-at(46000, 10000): p1/000001.sst
+46000 10000: ok (salt 1)
+<local fs> read-at(56000, 50000): p1/000001.sst
+56000 50000: ok (salt 1)
+<local fs> read-at(106000, 30000): p1/000001.sst
+106000 30000: ok (salt 1)
+<local fs> read-at(140000, 80000): p1/000001.sst
+140000 80000: ok (salt 1)
+<local fs> close: p1/000001.sst
+
+read 1 readahead=sys-readahead
+0 1000
+1000 15000
+16000 30000
+46000 10000
+56000 50000
+106000 30000
+140000 80000
+----
+<local fs> open: p1/000001.sst (options: *vfs.randomReadsOption)
+size: 2000000
+<local fs> read-at(0, 1000): p1/000001.sst
+0 1000: ok (salt 1)
+<local fs> read-at(1000, 15000): p1/000001.sst
+1000 15000: ok (salt 1)
+<local fs> prefetch(16000, 65536): p1/000001.sst
+<local fs> read-at(16000, 30000): p1/000001.sst
+16000 30000: ok (salt 1)
+<local fs> read-at(46000, 10000): p1/000001.sst
+46000 10000: ok (salt 1)
+<local fs> prefetch(56000, 131072): p1/000001.sst
+<local fs> read-at(56000, 50000): p1/000001.sst
+56000 50000: ok (salt 1)
+<local fs> read-at(106000, 30000): p1/000001.sst
+106000 30000: ok (salt 1)
+<local fs> prefetch(140000, 262144): p1/000001.sst
+<local fs> read-at(140000, 80000): p1/000001.sst
+140000 80000: ok (salt 1)
+<local fs> close: p1/000001.sst
+
+# TODO(radu): for informed/sys-readahead, we should start with the maximum
+# prefetch window.
+read 1 for-compaction readahead=sys-readahead
+0 1000
+1000 15000
+16000 30000
+46000 10000
+56000 50000
+106000 30000
+140000 80000
+----
+<local fs> open: p1/000001.sst (options: *vfs.randomReadsOption)
+size: 2000000
+<local fs> read-at(0, 1000): p1/000001.sst
+0 1000: ok (salt 1)
+<local fs> read-at(1000, 15000): p1/000001.sst
+1000 15000: ok (salt 1)
+<local fs> prefetch(16000, 65536): p1/000001.sst
+<local fs> read-at(16000, 30000): p1/000001.sst
+16000 30000: ok (salt 1)
+<local fs> read-at(46000, 10000): p1/000001.sst
+46000 10000: ok (salt 1)
+<local fs> prefetch(56000, 131072): p1/000001.sst
+<local fs> read-at(56000, 50000): p1/000001.sst
+56000 50000: ok (salt 1)
+<local fs> read-at(106000, 30000): p1/000001.sst
+106000 30000: ok (salt 1)
+<local fs> prefetch(140000, 262144): p1/000001.sst
+<local fs> read-at(140000, 80000): p1/000001.sst
+140000 80000: ok (salt 1)
+<local fs> close: p1/000001.sst
diff --git a/objstorage/objstorageprovider/testdata/provider/shared_basic b/objstorage/objstorageprovider/testdata/provider/shared_basic
index 2882d498..8ebbaa17 100644
--- a/objstorage/objstorageprovider/testdata/provider/shared_basic
+++ b/objstorage/objstorageprovider/testdata/provider/shared_basic
@@ -22,7 +22,7 @@ create 1 local 1 100
 read 1
 0 100
 ----
-<local fs> open: p1/000001.sst
+<local fs> open: p1/000001.sst (options: *vfs.randomReadsOption)
 size: 100
 <local fs> read-at(0, 100): p1/000001.sst
 0 100: ok (salt 1)
@@ -91,7 +91,7 @@ link-or-copy 3 local 3 100
 read 3
 0 100
 ----
-<local fs> open: p1/000003.sst
+<local fs> open: p1/000003.sst (options: *vfs.randomReadsOption)
 size: 100
 <local fs> read-at(0, 100): p1/000003.sst
 0 100: ok (salt 3)
@@ -102,7 +102,7 @@ link-or-copy 4 shared 4 100
 <local fs> create: temp-file-2
 <local fs> close: temp-file-2
 <remote> create object "2f2f-1-000004.sst"
-<local fs> open: temp-file-2
+<local fs> open: temp-file-2 (options: *vfs.sequentialReadsOption)
 <remote> close writer for "2f2f-1-000004.sst" after 100 bytes
 <remote> create object "2f2f-1-000004.sst.ref.1.000004"
 <remote> close writer for "2f2f-1-000004.sst.ref.1.000004" after 0 bytes
diff --git a/objstorage/objstorageprovider/testdata/provider/shared_no_ref b/objstorage/objstorageprovider/testdata/provider/shared_no_ref
index 538dde8b..3a9ed5d4 100644
--- a/objstorage/objstorageprovider/testdata/provider/shared_no_ref
+++ b/objstorage/objstorageprovider/testdata/provider/shared_no_ref
@@ -51,7 +51,7 @@ link-or-copy 3 shared 3 100 no-ref-tracking
 <local fs> create: temp-file-1
 <local fs> close: temp-file-1
 <remote> create object "eaac-1-000003.sst"
-<local fs> open: temp-file-1
+<local fs> open: temp-file-1 (options: *vfs.sequentialReadsOption)
 <remote> close writer for "eaac-1-000003.sst" after 100 bytes
 <local fs> close: temp-file-1
 
diff --git a/testdata/checkpoint b/testdata/checkpoint
index 8686e74b..80280e19 100644
--- a/testdata/checkpoint
+++ b/testdata/checkpoint
@@ -105,7 +105,7 @@ sync: checkpoints/checkpoint1
 close: checkpoints/checkpoint1
 link: db/000005.sst -> checkpoints/checkpoint1/000005.sst
 link: db/000007.sst -> checkpoints/checkpoint1/000007.sst
-open: db/MANIFEST-000001
+open: db/MANIFEST-000001 (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint1/MANIFEST-000001
 sync-data: checkpoints/checkpoint1/MANIFEST-000001
 close: checkpoints/checkpoint1/MANIFEST-000001
@@ -116,7 +116,7 @@ sync-data: checkpoints/checkpoint1/marker.manifest.000001.MANIFEST-000001
 close: checkpoints/checkpoint1/marker.manifest.000001.MANIFEST-000001
 sync: checkpoints/checkpoint1
 close: checkpoints/checkpoint1
-open: db/000006.log
+open: db/000006.log (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint1/000006.log
 sync-data: checkpoints/checkpoint1/000006.log
 close: checkpoints/checkpoint1/000006.log
@@ -144,7 +144,7 @@ close: checkpoints/checkpoint2/marker.format-version.000001.017
 sync: checkpoints/checkpoint2
 close: checkpoints/checkpoint2
 link: db/000007.sst -> checkpoints/checkpoint2/000007.sst
-open: db/MANIFEST-000001
+open: db/MANIFEST-000001 (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint2/MANIFEST-000001
 sync-data: checkpoints/checkpoint2/MANIFEST-000001
 close: checkpoints/checkpoint2/MANIFEST-000001
@@ -155,7 +155,7 @@ sync-data: checkpoints/checkpoint2/marker.manifest.000001.MANIFEST-000001
 close: checkpoints/checkpoint2/marker.manifest.000001.MANIFEST-000001
 sync: checkpoints/checkpoint2
 close: checkpoints/checkpoint2
-open: db/000006.log
+open: db/000006.log (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint2/000006.log
 sync-data: checkpoints/checkpoint2/000006.log
 close: checkpoints/checkpoint2/000006.log
@@ -180,7 +180,7 @@ sync: checkpoints/checkpoint3
 close: checkpoints/checkpoint3
 link: db/000005.sst -> checkpoints/checkpoint3/000005.sst
 link: db/000007.sst -> checkpoints/checkpoint3/000007.sst
-open: db/MANIFEST-000001
+open: db/MANIFEST-000001 (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint3/MANIFEST-000001
 sync-data: checkpoints/checkpoint3/MANIFEST-000001
 close: checkpoints/checkpoint3/MANIFEST-000001
@@ -191,7 +191,7 @@ sync-data: checkpoints/checkpoint3/marker.manifest.000001.MANIFEST-000001
 close: checkpoints/checkpoint3/marker.manifest.000001.MANIFEST-000001
 sync: checkpoints/checkpoint3
 close: checkpoints/checkpoint3
-open: db/000006.log
+open: db/000006.log (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint3/000006.log
 sync-data: checkpoints/checkpoint3/000006.log
 close: checkpoints/checkpoint3/000006.log
@@ -210,28 +210,28 @@ sync-data: db/000009.sst
 close: db/000009.sst
 sync: db
 sync: db/MANIFEST-000001
-open: db/000005.sst
+open: db/000005.sst (options: *vfs.randomReadsOption)
 read-at(558, 53): db/000005.sst
 read-at(521, 37): db/000005.sst
 read-at(74, 447): db/000005.sst
-open: db/000009.sst
+open: db/000009.sst (options: *vfs.randomReadsOption)
 read-at(553, 53): db/000009.sst
 read-at(516, 37): db/000009.sst
 read-at(69, 447): db/000009.sst
-open: db/000007.sst
+open: db/000007.sst (options: *vfs.randomReadsOption)
 read-at(558, 53): db/000007.sst
 read-at(521, 37): db/000007.sst
 read-at(74, 447): db/000007.sst
 read-at(47, 27): db/000005.sst
-open: db/000005.sst
+open: db/000005.sst (options: *vfs.sequentialReadsOption)
 read-at(0, 47): db/000005.sst
 read-at(47, 27): db/000007.sst
-open: db/000007.sst
+open: db/000007.sst (options: *vfs.sequentialReadsOption)
 read-at(0, 47): db/000007.sst
 create: db/000010.sst
 close: db/000005.sst
 read-at(42, 27): db/000009.sst
-open: db/000009.sst
+open: db/000009.sst (options: *vfs.sequentialReadsOption)
 read-at(0, 42): db/000009.sst
 close: db/000007.sst
 close: db/000009.sst
@@ -290,13 +290,13 @@ close: checkpoints/checkpoint1/000006.log
 
 scan checkpoints/checkpoint1
 ----
-open: checkpoints/checkpoint1/000007.sst
+open: checkpoints/checkpoint1/000007.sst (options: *vfs.randomReadsOption)
 read-at(558, 53): checkpoints/checkpoint1/000007.sst
 read-at(521, 37): checkpoints/checkpoint1/000007.sst
 read-at(74, 447): checkpoints/checkpoint1/000007.sst
 read-at(47, 27): checkpoints/checkpoint1/000007.sst
 read-at(0, 47): checkpoints/checkpoint1/000007.sst
-open: checkpoints/checkpoint1/000005.sst
+open: checkpoints/checkpoint1/000005.sst (options: *vfs.randomReadsOption)
 read-at(558, 53): checkpoints/checkpoint1/000005.sst
 read-at(521, 37): checkpoints/checkpoint1/000005.sst
 read-at(74, 447): checkpoints/checkpoint1/000005.sst
@@ -313,7 +313,7 @@ g 10
 
 scan db
 ----
-open: db/000010.sst
+open: db/000010.sst (options: *vfs.randomReadsOption)
 read-at(585, 53): db/000010.sst
 read-at(548, 37): db/000010.sst
 read-at(101, 447): db/000010.sst
@@ -357,7 +357,7 @@ close: checkpoints/checkpoint2/000006.log
 
 scan checkpoints/checkpoint2
 ----
-open: checkpoints/checkpoint2/000007.sst
+open: checkpoints/checkpoint2/000007.sst (options: *vfs.randomReadsOption)
 read-at(558, 53): checkpoints/checkpoint2/000007.sst
 read-at(521, 37): checkpoints/checkpoint2/000007.sst
 read-at(74, 447): checkpoints/checkpoint2/000007.sst
@@ -399,13 +399,13 @@ close: checkpoints/checkpoint3/000006.log
 
 scan checkpoints/checkpoint3
 ----
-open: checkpoints/checkpoint3/000007.sst
+open: checkpoints/checkpoint3/000007.sst (options: *vfs.randomReadsOption)
 read-at(558, 53): checkpoints/checkpoint3/000007.sst
 read-at(521, 37): checkpoints/checkpoint3/000007.sst
 read-at(74, 447): checkpoints/checkpoint3/000007.sst
 read-at(47, 27): checkpoints/checkpoint3/000007.sst
 read-at(0, 47): checkpoints/checkpoint3/000007.sst
-open: checkpoints/checkpoint3/000005.sst
+open: checkpoints/checkpoint3/000005.sst (options: *vfs.randomReadsOption)
 read-at(558, 53): checkpoints/checkpoint3/000005.sst
 read-at(521, 37): checkpoints/checkpoint3/000005.sst
 read-at(74, 447): checkpoints/checkpoint3/000005.sst
@@ -476,7 +476,7 @@ g 10
 h 11
 i i
 k k
-open: db/000014.sst
+open: db/000014.sst (options: *vfs.randomReadsOption)
 read-at(541, 53): db/000014.sst
 read-at(504, 37): db/000014.sst
 z z
@@ -500,7 +500,7 @@ close: checkpoints/checkpoint4
 link: db/000010.sst -> checkpoints/checkpoint4/000010.sst
 link: db/000011.sst -> checkpoints/checkpoint4/000011.sst
 link: db/000014.sst -> checkpoints/checkpoint4/000014.sst
-open: db/MANIFEST-000001
+open: db/MANIFEST-000001 (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint4/MANIFEST-000001
 sync-data: checkpoints/checkpoint4/MANIFEST-000001
 close: checkpoints/checkpoint4/MANIFEST-000001
@@ -511,7 +511,7 @@ sync-data: checkpoints/checkpoint4/marker.manifest.000001.MANIFEST-000001
 close: checkpoints/checkpoint4/marker.manifest.000001.MANIFEST-000001
 sync: checkpoints/checkpoint4
 close: checkpoints/checkpoint4
-open: db/000008.log
+open: db/000008.log (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint4/000008.log
 sync-data: checkpoints/checkpoint4/000008.log
 close: checkpoints/checkpoint4/000008.log
@@ -537,7 +537,7 @@ close: checkpoints/checkpoint4/000008.log
 
 scan checkpoints/checkpoint4
 ----
-open: checkpoints/checkpoint4/000010.sst
+open: checkpoints/checkpoint4/000010.sst (options: *vfs.randomReadsOption)
 read-at(585, 53): checkpoints/checkpoint4/000010.sst
 read-at(548, 37): checkpoints/checkpoint4/000010.sst
 read-at(101, 447): checkpoints/checkpoint4/000010.sst
@@ -549,7 +549,7 @@ d 7
 e 8
 f 9
 g 10
-open: checkpoints/checkpoint4/000011.sst
+open: checkpoints/checkpoint4/000011.sst (options: *vfs.randomReadsOption)
 read-at(558, 53): checkpoints/checkpoint4/000011.sst
 read-at(521, 37): checkpoints/checkpoint4/000011.sst
 read-at(70, 451): checkpoints/checkpoint4/000011.sst
@@ -558,7 +558,7 @@ read-at(0, 43): checkpoints/checkpoint4/000011.sst
 h 11
 i i
 k k
-open: checkpoints/checkpoint4/000014.sst
+open: checkpoints/checkpoint4/000014.sst (options: *vfs.randomReadsOption)
 read-at(541, 53): checkpoints/checkpoint4/000014.sst
 read-at(504, 37): checkpoints/checkpoint4/000014.sst
 read-at(53, 451): checkpoints/checkpoint4/000014.sst
@@ -605,7 +605,7 @@ close: checkpoints/checkpoint5
 link: db/000010.sst -> checkpoints/checkpoint5/000010.sst
 link: db/000011.sst -> checkpoints/checkpoint5/000011.sst
 link: db/000014.sst -> checkpoints/checkpoint5/000014.sst
-open: db/MANIFEST-000001
+open: db/MANIFEST-000001 (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint5/MANIFEST-000001
 sync-data: checkpoints/checkpoint5/MANIFEST-000001
 close: checkpoints/checkpoint5/MANIFEST-000001
@@ -616,7 +616,7 @@ sync-data: checkpoints/checkpoint5/marker.manifest.000001.MANIFEST-000001
 close: checkpoints/checkpoint5/marker.manifest.000001.MANIFEST-000001
 sync: checkpoints/checkpoint5
 close: checkpoints/checkpoint5
-open: db/000008.log
+open: db/000008.log (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint5/000008.log
 sync-data: checkpoints/checkpoint5/000008.log
 close: checkpoints/checkpoint5/000008.log
@@ -702,7 +702,7 @@ sync: checkpoints/checkpoint6
 close: checkpoints/checkpoint6
 link: db/000011.sst -> checkpoints/checkpoint6/000011.sst
 link: db/000014.sst -> checkpoints/checkpoint6/000014.sst
-open: db/MANIFEST-000001
+open: db/MANIFEST-000001 (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint6/MANIFEST-000001
 sync-data: checkpoints/checkpoint6/MANIFEST-000001
 close: checkpoints/checkpoint6/MANIFEST-000001
@@ -713,7 +713,7 @@ sync-data: checkpoints/checkpoint6/marker.manifest.000001.MANIFEST-000001
 close: checkpoints/checkpoint6/marker.manifest.000001.MANIFEST-000001
 sync: checkpoints/checkpoint6
 close: checkpoints/checkpoint6
-open: db/000008.log
+open: db/000008.log (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint6/000008.log
 sync-data: checkpoints/checkpoint6/000008.log
 close: checkpoints/checkpoint6/000008.log
diff --git a/testdata/checkpoint_shared b/testdata/checkpoint_shared
index 24929737..eda39dc9 100644
--- a/testdata/checkpoint_shared
+++ b/testdata/checkpoint_shared
@@ -91,7 +91,7 @@ sync-data: checkpoints/checkpoint1/marker.format-version.000001.017
 close: checkpoints/checkpoint1/marker.format-version.000001.017
 sync: checkpoints/checkpoint1
 close: checkpoints/checkpoint1
-open: db/MANIFEST-000001
+open: db/MANIFEST-000001 (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint1/MANIFEST-000001
 sync-data: checkpoints/checkpoint1/MANIFEST-000001
 close: checkpoints/checkpoint1/MANIFEST-000001
@@ -102,7 +102,7 @@ sync-data: checkpoints/checkpoint1/marker.manifest.000001.MANIFEST-000001
 close: checkpoints/checkpoint1/marker.manifest.000001.MANIFEST-000001
 sync: checkpoints/checkpoint1
 close: checkpoints/checkpoint1
-open: db/REMOTE-OBJ-CATALOG-000001
+open: db/REMOTE-OBJ-CATALOG-000001 (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint1/REMOTE-OBJ-CATALOG-000001
 sync-data: checkpoints/checkpoint1/REMOTE-OBJ-CATALOG-000001
 close: checkpoints/checkpoint1/REMOTE-OBJ-CATALOG-000001
@@ -113,7 +113,7 @@ sync-data: checkpoints/checkpoint1/marker.remote-obj-catalog.000001.REMOTE-OBJ-C
 close: checkpoints/checkpoint1/marker.remote-obj-catalog.000001.REMOTE-OBJ-CATALOG-000001
 sync: checkpoints/checkpoint1
 close: checkpoints/checkpoint1
-open: db/000006.log
+open: db/000006.log (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint1/000006.log
 sync-data: checkpoints/checkpoint1/000006.log
 close: checkpoints/checkpoint1/000006.log
@@ -140,7 +140,7 @@ sync-data: checkpoints/checkpoint2/marker.format-version.000001.017
 close: checkpoints/checkpoint2/marker.format-version.000001.017
 sync: checkpoints/checkpoint2
 close: checkpoints/checkpoint2
-open: db/MANIFEST-000001
+open: db/MANIFEST-000001 (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint2/MANIFEST-000001
 sync-data: checkpoints/checkpoint2/MANIFEST-000001
 close: checkpoints/checkpoint2/MANIFEST-000001
@@ -151,7 +151,7 @@ sync-data: checkpoints/checkpoint2/marker.manifest.000001.MANIFEST-000001
 close: checkpoints/checkpoint2/marker.manifest.000001.MANIFEST-000001
 sync: checkpoints/checkpoint2
 close: checkpoints/checkpoint2
-open: db/REMOTE-OBJ-CATALOG-000001
+open: db/REMOTE-OBJ-CATALOG-000001 (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint2/REMOTE-OBJ-CATALOG-000001
 sync-data: checkpoints/checkpoint2/REMOTE-OBJ-CATALOG-000001
 close: checkpoints/checkpoint2/REMOTE-OBJ-CATALOG-000001
@@ -162,7 +162,7 @@ sync-data: checkpoints/checkpoint2/marker.remote-obj-catalog.000001.REMOTE-OBJ-C
 close: checkpoints/checkpoint2/marker.remote-obj-catalog.000001.REMOTE-OBJ-CATALOG-000001
 sync: checkpoints/checkpoint2
 close: checkpoints/checkpoint2
-open: db/000006.log
+open: db/000006.log (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint2/000006.log
 sync-data: checkpoints/checkpoint2/000006.log
 close: checkpoints/checkpoint2/000006.log
@@ -185,7 +185,7 @@ sync-data: checkpoints/checkpoint3/marker.format-version.000001.017
 close: checkpoints/checkpoint3/marker.format-version.000001.017
 sync: checkpoints/checkpoint3
 close: checkpoints/checkpoint3
-open: db/MANIFEST-000001
+open: db/MANIFEST-000001 (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint3/MANIFEST-000001
 sync-data: checkpoints/checkpoint3/MANIFEST-000001
 close: checkpoints/checkpoint3/MANIFEST-000001
@@ -196,7 +196,7 @@ sync-data: checkpoints/checkpoint3/marker.manifest.000001.MANIFEST-000001
 close: checkpoints/checkpoint3/marker.manifest.000001.MANIFEST-000001
 sync: checkpoints/checkpoint3
 close: checkpoints/checkpoint3
-open: db/REMOTE-OBJ-CATALOG-000001
+open: db/REMOTE-OBJ-CATALOG-000001 (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint3/REMOTE-OBJ-CATALOG-000001
 sync-data: checkpoints/checkpoint3/REMOTE-OBJ-CATALOG-000001
 close: checkpoints/checkpoint3/REMOTE-OBJ-CATALOG-000001
@@ -207,7 +207,7 @@ sync-data: checkpoints/checkpoint3/marker.remote-obj-catalog.000001.REMOTE-OBJ-C
 close: checkpoints/checkpoint3/marker.remote-obj-catalog.000001.REMOTE-OBJ-CATALOG-000001
 sync: checkpoints/checkpoint3
 close: checkpoints/checkpoint3
-open: db/000006.log
+open: db/000006.log (options: *vfs.sequentialReadsOption)
 create: checkpoints/checkpoint3/000006.log
 sync-data: checkpoints/checkpoint3/000006.log
 close: checkpoints/checkpoint3/000006.log
diff --git a/testdata/cleaner b/testdata/cleaner
index d233e921..0bafa369 100644
--- a/testdata/cleaner
+++ b/testdata/cleaner
@@ -75,21 +75,21 @@ sync: db
 sync: db/MANIFEST-000001
 mkdir-all: db_wal/archive 0755
 rename: db_wal/000004.log -> db_wal/archive/000004.log
-open: db/000005.sst
+open: db/000005.sst (options: *vfs.randomReadsOption)
 read-at(535, 53): db/000005.sst
 read-at(498, 37): db/000005.sst
 read-at(79, 419): db/000005.sst
-open: db/000007.sst
+open: db/000007.sst (options: *vfs.randomReadsOption)
 read-at(509, 53): db/000007.sst
 read-at(472, 37): db/000007.sst
 read-at(53, 419): db/000007.sst
 read-at(52, 27): db/000005.sst
-open: db/000005.sst
+open: db/000005.sst (options: *vfs.sequentialReadsOption)
 read-at(0, 52): db/000005.sst
 create: db/000008.sst
 close: db/000005.sst
 read-at(26, 27): db/000007.sst
-open: db/000007.sst
+open: db/000007.sst (options: *vfs.sequentialReadsOption)
 read-at(0, 26): db/000007.sst
 close: db/000007.sst
 sync-data: db/000008.sst
diff --git a/testdata/event_listener b/testdata/event_listener
index 3d0756d0..239b3508 100644
--- a/testdata/event_listener
+++ b/testdata/event_listener
@@ -103,19 +103,19 @@ sync: db
 remove: db/MANIFEST-000001
 [JOB 5] MANIFEST deleted 000001
 [JOB 6] compacting(default) L0 [000005 000008] (1.2KB) Score=0.00 + L6 [] (0B) Score=0.00; OverlappingRatio: Single 0.00, Multi 0.00
-open: db/000005.sst
+open: db/000005.sst (options: *vfs.randomReadsOption)
 read-at(537, 53): db/000005.sst
 read-at(500, 37): db/000005.sst
 read-at(53, 447): db/000005.sst
-open: db/000008.sst
+open: db/000008.sst (options: *vfs.randomReadsOption)
 read-at(537, 53): db/000008.sst
 read-at(500, 37): db/000008.sst
 read-at(53, 447): db/000008.sst
 read-at(26, 27): db/000005.sst
-open: db/000005.sst
+open: db/000005.sst (options: *vfs.sequentialReadsOption)
 read-at(0, 26): db/000005.sst
 read-at(26, 27): db/000008.sst
-open: db/000008.sst
+open: db/000008.sst (options: *vfs.sequentialReadsOption)
 read-at(0, 26): db/000008.sst
 close: db/000008.sst
 close: db/000005.sst
@@ -186,7 +186,7 @@ close: ext/0
 link: ext/0 -> db/000015.sst
 [JOB 10] ingesting: sstable created 000015
 sync: db
-open: db/000013.sst
+open: db/000013.sst (options: *vfs.randomReadsOption)
 read-at(537, 53): db/000013.sst
 read-at(500, 37): db/000013.sst
 read-at(53, 447): db/000013.sst
@@ -367,7 +367,7 @@ link: db/000022.sst -> checkpoint/000022.sst
 link: db/000017.sst -> checkpoint/000017.sst
 link: db/000010.sst -> checkpoint/000010.sst
 link: db/000018.sst -> checkpoint/000018.sst
-open: db/MANIFEST-000023
+open: db/MANIFEST-000023 (options: *vfs.sequentialReadsOption)
 create: checkpoint/MANIFEST-000023
 sync-data: checkpoint/MANIFEST-000023
 close: checkpoint/MANIFEST-000023
@@ -378,7 +378,7 @@ sync-data: checkpoint/marker.manifest.000001.MANIFEST-000023
 close: checkpoint/marker.manifest.000001.MANIFEST-000023
 sync: checkpoint
 close: checkpoint
-open: wal/000021.log
+open: wal/000021.log (options: *vfs.sequentialReadsOption)
 create: checkpoint/000021.log
 sync-data: checkpoint/000021.log
 close: checkpoint/000021.log