Skip to content

Commit 491b500

Browse files
committed
metamorphic: fix WAL recovery dirs logic
When we set up an initial state and the `WALDir` changed, we add the previous directory as a WAL recovery dir. But the logic uses the existence of a `wal` subdirectory; this is incorrect in the case where the initial store used a `WALDir` in the past, but not in the most recent iteration. We fix this by finding the latest OPTIONS file and parsing it instead. In addition, if the previous store did not use a WAL dir, we remove any `wal` subdirectory that was left over. We make the analogous fixes for the WAL secondary.
1 parent e14f683 commit 491b500

File tree

1 file changed

+80
-49
lines changed

1 file changed

+80
-49
lines changed

metamorphic/options.go

Lines changed: 80 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package metamorphic
77
import (
88
"bytes"
99
"fmt"
10+
"io"
1011
"math"
1112
"math/rand/v2"
1213
"os"
@@ -926,10 +927,12 @@ func expRandDuration(rng *rand.Rand, meanDur, maxDur time.Duration) time.Duratio
926927
}
927928

928929
func setupInitialState(dataDir string, testOpts *TestOptions) error {
930+
fs := testOpts.Opts.FS
931+
929932
// Copy (vfs.Default,<initialStatePath>/data) to (testOpts.opts.FS,<dataDir>).
930933
ok, err := vfs.Clone(
931934
vfs.Default,
932-
testOpts.Opts.FS,
935+
fs,
933936
vfs.Default.PathJoin(testOpts.initialStatePath, "data"),
934937
dataDir,
935938
vfs.CloneSync,
@@ -945,58 +948,86 @@ func setupInitialState(dataDir string, testOpts *TestOptions) error {
945948
return os.ErrNotExist
946949
}
947950

948-
// Tests with wal_dir set store their WALs in a `wal` directory. The source
949-
// database (initialStatePath) could've had wal_dir set, or the current test
950-
// options (testOpts) could have wal_dir set, or both.
951-
//
952-
// If the test opts are not configured to use a WAL dir, we add the WAL dir
953-
// as a 'WAL recovery dir' so that we'll read any WALs in the directory in
954-
// Open.
955-
fs := testOpts.Opts.FS
956-
walRecoveryPath := fs.PathJoin(dataDir, "wal")
957-
if _, err := fs.Stat(walRecoveryPath); err == nil {
958-
// Previous test used a WAL dir.
959-
if testOpts.Opts.WALDir == "" {
960-
// This test is not using a WAL dir. Add the previous WAL dir as a
961-
// recovery dir.
962-
testOpts.Opts.WALRecoveryDirs = append(testOpts.Opts.WALRecoveryDirs, wal.Dir{
963-
FS: fs,
964-
Dirname: pebble.MakeStoreRelativePath(fs, "wal"),
965-
})
966-
} else {
967-
// Both the previous test and the current test are using a WAL dir. We
968-
// assume that they are the same.
969-
if testOpts.Opts.WALDir != pebble.MakeStoreRelativePath(fs, "wal") {
970-
return errors.Errorf("unsupported wal dir value %q", testOpts.Opts.WALDir)
971-
}
972-
}
973-
} else {
974-
// Previous test did not use a WAL dir.
975-
if testOpts.Opts.WALDir != "" {
976-
// The current test is using a WAL dir; we add the data directory itself
977-
// as a 'WAL recovery dir' so that we'll read any WALs if the previous
978-
// test was writing them to the data directory.
979-
testOpts.Opts.WALRecoveryDirs = append(testOpts.Opts.WALRecoveryDirs, wal.Dir{
980-
FS: fs,
981-
Dirname: pebble.MakeStoreRelativePath(fs, ""),
982-
})
983-
}
951+
// Find the previous OPTIONS file.
952+
ls, err := fs.List(dataDir)
953+
if err != nil {
954+
return err
984955
}
985956

986-
// If the previous test used WAL failover and this test does not use failover,
987-
// add the failover directory as a 'WAL recovery dir' in case the previous
988-
// test was configured to use failover.
989-
failoverDir := testOpts.Opts.FS.PathJoin(dataDir, "wal_secondary")
990-
if _, err := testOpts.Opts.FS.Stat(failoverDir); err == nil {
991-
if testOpts.Opts.WALFailover == nil {
992-
testOpts.Opts.WALRecoveryDirs = append(testOpts.Opts.WALRecoveryDirs, wal.Dir{
993-
FS: testOpts.Opts.FS,
994-
Dirname: pebble.MakeStoreRelativePath(testOpts.Opts.FS, "wal_secondary"),
995-
})
996-
} else if testOpts.Opts.WALFailover.Secondary.Dirname != pebble.MakeStoreRelativePath(testOpts.Opts.FS, "wal_secondary") {
997-
return errors.Errorf("unsupported wal failover dir value %q", testOpts.Opts.WALFailover.Secondary.Dirname)
957+
var lastOptionsNum base.DiskFileNum
958+
var lastOptionsFilename string
959+
960+
for _, filename := range ls {
961+
ft, fn, ok := base.ParseFilename(fs, filename)
962+
if ok && ft == base.FileTypeOptions && fn > lastOptionsNum {
963+
lastOptionsNum = fn
964+
lastOptionsFilename = filename
998965
}
999966
}
967+
968+
if lastOptionsFilename == "" {
969+
return errors.Errorf("could not find any OPTIONS file in %s/data", testOpts.initialStatePath)
970+
}
971+
972+
f, err := fs.Open(fs.PathJoin(dataDir, lastOptionsFilename))
973+
if err != nil {
974+
return err
975+
}
976+
data, err := io.ReadAll(f)
977+
f.Close()
978+
if err != nil {
979+
return err
980+
}
981+
982+
var opts pebble.Options
983+
if err := opts.Parse(string(data), nil /* hooks */); err != nil {
984+
return errors.Wrapf(err, "failed to parse %s/%s", dataDir, lastOptionsFilename)
985+
}
986+
987+
// WALDir must be either empty or `{store_path}/wal`.
988+
expectedWALDir := pebble.MakeStoreRelativePath(fs, "wal")
989+
if opts.WALDir != "" && opts.WALDir != expectedWALDir {
990+
return errors.Errorf("unexpected wal_dir value in initial store: %q", opts.WALDir)
991+
}
992+
if testOpts.Opts.WALDir != "" && testOpts.Opts.WALDir != expectedWALDir {
993+
return errors.Errorf("unexpected wal_dir value in test optons: %q", testOpts.Opts.WALDir)
994+
}
995+
996+
if opts.WALDir == "" && testOpts.Opts.WALDir != "" {
997+
// The previous test did not use a WAL dir but this test does. Add the
998+
// store path as a WAL recovery dir.
999+
testOpts.Opts.WALRecoveryDirs = append(testOpts.Opts.WALRecoveryDirs, wal.Dir{
1000+
FS: fs,
1001+
Dirname: pebble.MakeStoreRelativePath(fs, ""),
1002+
})
1003+
} else if opts.WALDir != "" && testOpts.Opts.WALDir == "" {
1004+
// The previous test used a WAL dir but this test does not. Add the
1005+
// previous WAL dir as a WAL recovery dir.
1006+
testOpts.Opts.WALRecoveryDirs = append(testOpts.Opts.WALRecoveryDirs, wal.Dir{
1007+
FS: fs,
1008+
Dirname: opts.WALDir,
1009+
})
1010+
}
1011+
1012+
// WAL secondary must be `{store_path}/wal_secondary` if WAL failover is enabled.
1013+
expectedSecondaryDir := pebble.MakeStoreRelativePath(fs, "wal_secondary")
1014+
if wf := opts.WALFailover; wf != nil && wf.Secondary.Dirname != expectedSecondaryDir {
1015+
return errors.Errorf("unexpected wal_secondary value in initial store: %q", wf.Secondary.Dirname)
1016+
}
1017+
if wf := testOpts.Opts.WALFailover; wf != nil && wf.Secondary.Dirname != expectedSecondaryDir {
1018+
return errors.Errorf("unexpected wal_secondary value in test options: %q", wf.Secondary.Dirname)
1019+
}
1020+
1021+
if opts.WALFailover != nil && testOpts.Opts.WALFailover == nil {
1022+
// The previous test used WAL failover but this test does not. Add the
1023+
// previous secondary directory as a WAL recovery dir.
1024+
testOpts.Opts.WALRecoveryDirs = append(testOpts.Opts.WALRecoveryDirs, wal.Dir{
1025+
FS: testOpts.Opts.FS,
1026+
Dirname: opts.WALFailover.Secondary.Dirname,
1027+
})
1028+
}
1029+
// If the previous test did not use WAL failover but this test does, we don't
1030+
// need to add any recovery dir.
10001031
return nil
10011032
}
10021033

0 commit comments

Comments
 (0)