Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
db: modify Options.DebugCheck to be a function
Previously, pebble.Options had a boolean DebugCheck field that when
set to true caused Pebble to run (*DB).CheckLevels when a new version
was installed. This commit refactors that Options field to be a function
of the signature `func(*DB) error`, allowing custom functions to be
called instead. In addition a `DebugCheckLevels` function with the
matching signature is exposed so the existing use is still ergonomic
for callers.
This was motivated by #438 . When injecting errors into filesystem
operations, CheckLevels may fail with an injected error. With this
change, the metamorphic tests can provide a custom DebugCheck function
that wraps `DebugCheckLevels` with a retry loop for injected errors.
Loading branch information
@@ -1002,11 +1002,7 @@ func (d *DB) flush1() error {
if err == nil {
flushed = d .mu .mem .queue [:n ]
d .mu .mem .queue = d .mu .mem .queue [n :]
var checker func () error
if d .opts .DebugCheck {
checker = func () error { return d .CheckLevels (nil ) }
}
d .updateReadStateLocked (checker )
d .updateReadStateLocked (d .opts .DebugCheck )
}
d .deleteObsoleteFiles (jobID )
@@ -1178,11 +1174,7 @@ func (d *DB) compact1(c *compaction, errChannel chan error) (err error) {
// there are no references obsolete tables will be added to the obsolete
// table list.
if err == nil {
var checker func () error
if d .opts .DebugCheck {
checker = func () error { return d .CheckLevels (nil ) }
}
d .updateReadStateLocked (checker )
d .updateReadStateLocked (d .opts .DebugCheck )
}
d .deleteObsoleteFiles (jobID )
@@ -795,7 +795,7 @@ func TestCompaction(t *testing.T) {
d , err := Open ("" , & Options {
FS : mem ,
MemTableSize : memTableSize ,
DebugCheck : true ,
DebugCheck : DebugCheckLevels ,
enablePacing : true ,
})
if err != nil {
@@ -929,7 +929,7 @@ func TestManualCompaction(t *testing.T) {
var err error
d , err = Open ("" , & Options {
FS : mem ,
DebugCheck : true ,
DebugCheck : DebugCheckLevels ,
})
require .NoError (t , err )
}
@@ -1700,7 +1700,7 @@ func TestFlushInvariant(t *testing.T) {
}
},
},
DebugCheck : true ,
DebugCheck : DebugCheckLevels ,
})
require .NoError (t , err )
@@ -216,7 +216,7 @@ func ingestLink(
for i := range paths {
target := base .MakeFilename (fs , dirname , fileTypeTable , meta [i ].FileNum )
var err error
if _ , ok := opts .FS .(* vfs.MemFS ); ok && opts .DebugCheck {
if _ , ok := opts .FS .(* vfs.MemFS ); ok && opts .DebugCheck != nil {
// The combination of MemFS+Ingest+DebugCheck produces awkwardness around
// the subsequent deletion of files. The problem is that MemFS implements
// the Windows semantics of disallowing removal of an open file. This is
@@ -647,11 +647,7 @@ func (d *DB) ingestApply(jobID int, meta []*fileMetadata) (*versionEdit, error)
}); err != nil {
return nil , err
}
var checker func () error
if d .opts .DebugCheck {
checker = func () error { return d .CheckLevels (nil ) }
}
d .updateReadStateLocked (checker )
d .updateReadStateLocked (d .opts .DebugCheck )
d .deleteObsoleteFiles (jobID )
// The ingestion may have pushed a level over the threshold for compaction,
// so check to see if one is necessary and schedule it.
@@ -500,7 +500,7 @@ func TestIngest(t *testing.T) {
FS : mem ,
L0CompactionThreshold : 100 ,
L0StopWritesThreshold : 100 ,
DebugCheck : true ,
DebugCheck : DebugCheckLevels ,
})
require .NoError (t , err )
}
@@ -49,7 +49,7 @@ func (t *test) init(h *history, dir string, testOpts *testOptions) error {
t .opts = testOpts .opts .EnsureDefaults ()
t .opts .Logger = h .Logger ()
t .opts .EventListener = pebble .MakeLoggingEventListener (t .opts .Logger )
t .opts .DebugCheck = true
t .opts .DebugCheck = pebble . DebugCheckLevels
defer t .opts .Cache .Unref ()
@@ -285,11 +285,7 @@ func Open(dirname string, opts *Options) (db *DB, _ error) {
return nil , err
}
}
var checker func () error
if d .opts .DebugCheck {
checker = func () error { return d .CheckLevels (nil ) }
}
d .updateReadStateLocked (checker )
d .updateReadStateLocked (d .opts .DebugCheck )
if ! d .opts .ReadOnly {
// Write the current options to disk.
@@ -252,10 +252,10 @@ type Options struct {
// The default value uses the same ordering as bytes.Compare.
Comparer * Comparer
// Setting this to true causes DB.CheckLevels() to be called whenever a new version is being
// installed. DB.CheckLevels() iterates over all the data in the DB, so set this to true only
// in tests .
DebugCheck bool
// DebugCheck is invoked, if non-nil, whenever a new version is being
// installed. Typically, this is set to pebble.DebugCheckLevels in tests
// or tools only, to check invariants over all the data in the database .
DebugCheck func ( * DB ) error
// Disable the write-ahead log (WAL). Disabling the write-ahead log prohibits
// crash recovery, but can improve performance if crash recovery is not
@@ -399,6 +399,13 @@ type Options struct {
enablePacing bool
}
// 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.
func DebugCheckLevels (db * DB ) error {
return db .CheckLevels (nil )
}
// EnsureDefaults ensures that the default values for all options are set if a
// valid value was not already specified. Returns the new options.
func (o * Options ) EnsureDefaults () * Options {
@@ -106,7 +106,7 @@ func TestRangeDelCompactionTruncation(t *testing.T) {
{TargetFileSize : 100 },
{TargetFileSize : 1 },
},
DebugCheck : true ,
DebugCheck : DebugCheckLevels ,
})
require .NoError (t , err )
defer d .Close ()
@@ -237,7 +237,7 @@ func TestRangeDelCompactionTruncation2(t *testing.T) {
{TargetFileSize : 100 },
{TargetFileSize : 1 },
},
DebugCheck : true ,
DebugCheck : DebugCheckLevels ,
})
require .NoError (t , err )
defer d .Close ()
@@ -299,7 +299,7 @@ func TestRangeDelCompactionTruncation3(t *testing.T) {
{TargetFileSize : 100 },
{TargetFileSize : 1 },
},
DebugCheck : true ,
DebugCheck : DebugCheckLevels ,
})
require .NoError (t , err )
defer d .Close ()
@@ -401,7 +401,7 @@ func BenchmarkRangeDelIterate(b *testing.B) {
d , err := Open ("" , & Options {
Cache : cache ,
FS : mem ,
DebugCheck : true ,
DebugCheck : DebugCheckLevels ,
})
if err != nil {
b .Fatal (err )
@@ -79,7 +79,7 @@ func (d *DB) loadReadState() *readState {
// updateReadStateLocked creates a new readState from the current version and
// list of memtables. Requires DB.mu is held. If checker is not nil, it is called after installing
// the new readState
func (d * DB ) updateReadStateLocked (checker func () error ) {
func (d * DB ) updateReadStateLocked (checker func (* DB ) error ) {
s := & readState {
db : d ,
refcnt : 1 ,
@@ -96,7 +96,7 @@ func (d *DB) updateReadStateLocked(checker func() error) {
d .readState .val = s
d .readState .Unlock ()
if checker != nil {
if err := checker (); err != nil {
if err := checker (d ); err != nil {
d .opts .Logger .Fatalf ("checker failed with error: %s" , err )
}
}
Toggle all file notes
Toggle all file annotations