diff --git a/pebble.go b/pebble.go index 18c4cec..e425845 100644 --- a/pebble.go +++ b/pebble.go @@ -24,6 +24,17 @@ var _ DB = (*PebbleDB)(nil) func NewPebbleDB(name string, dir string) (*PebbleDB, error) { opts := &pebble.Options{} + // EnsureDefaults would settle on FormatMostCompatible and leave it there + // for the database's whole life, which pebble v2 refuses to open -- and + // refuses before it would migrate anything, so no v2 build can do this for + // itself. Upgrading here readies a node for that bump instead of failing + // to start after it. + // + // FlushableIngest rather than FormatNewest is exactly what v2 needs: the + // next version up holds the open until every table predating the Pebblev1 + // format has been rewritten, which here is all of them. Stopping short + // leaves those to ordinary compaction. + opts.FormatMajorVersion = pebble.FormatFlushableIngest opts.EnsureDefaults() return NewPebbleDBWithOpts(name, dir, opts) } diff --git a/pebble_test.go b/pebble_test.go index 0d06be1..95355cd 100644 --- a/pebble_test.go +++ b/pebble_test.go @@ -3,12 +3,52 @@ package db import ( "fmt" "os" + "path/filepath" "testing" + "github.com/cockroachdb/pebble" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +// TestPebbleRatchetsFormatMajorVersion covers the upgrade this option exists +// for. NewPebbleDB has to move an existing database forward, not just new +// ones, since every node already has one from before this. +// +// The test goes when the migration does: v2 deleted FormatMostCompatible, so +// none of it compiles there. Delete it rather than repair it -- but carry the +// ceiling assertion over. Staying below FormatNewest is not about the +// migration; it is what leaves a database readable by the release before, and +// it stays true on v2. Dropping the test takes that with it and nothing says +// so. +func TestPebbleRatchetsFormatMajorVersion(t *testing.T) { + dir := t.TempDir() + + // What a node that has never run this code has on disk. + old, err := pebble.Open(filepath.Join(dir, "test.db"), &pebble.Options{}) + require.NoError(t, err) + require.Equal(t, pebble.FormatMostCompatible, old.FormatMajorVersion(), + "pebble's own default is the version v2 refuses") + require.NoError(t, old.Set([]byte("key"), []byte("value"), pebble.Sync)) + require.NoError(t, old.Close()) + + db, err := NewPebbleDB("test", dir) + require.NoError(t, err) + defer db.Close() + + // FlushableIngest is pebble v2's FormatMinSupported, which is the point. + // Staying below FormatNewest is the other half: the next version up would + // not return from the open above until every table had been rewritten. + vers := db.DB().FormatMajorVersion() + require.Equal(t, pebble.FormatFlushableIngest, vers) + require.Less(t, vers, pebble.FormatNewest, + "going further would put a full rewrite in front of the node's first start") + + got, err := db.Get([]byte("key")) + require.NoError(t, err) + require.Equal(t, []byte("value"), got, "the upgrade must carry the data over") +} + func TestPebbleDBBackend(t *testing.T) { name := fmt.Sprintf("test_%x", randStr(12)) dir := os.TempDir()