// Copyright 2023 The LevelDB-Go and Pebble Authors. All rights reserved. Use // of this source code is governed by a BSD-style license that can be found in // the LICENSE file. package objstorageprovider import ( "encoding/binary" "testing" "github.com/cockroachdb/pebble/internal/base" "github.com/cockroachdb/pebble/objstorage" "github.com/cockroachdb/pebble/objstorage/remote" "github.com/cockroachdb/pebble/vfs" "github.com/stretchr/testify/require" ) func TestSharedObjectBacking(t *testing.T) { for _, cleanup := range []objstorage.SharedCleanupMethod{objstorage.SharedRefTracking, objstorage.SharedNoCleanup} { name := "ref-tracking" if cleanup == objstorage.SharedNoCleanup { name = "no-cleanup" } t.Run(name, func(t *testing.T) { st := DefaultSettings(vfs.NewMem(), "") sharedStorage := remote.NewInMem() st.Remote.StorageFactory = remote.MakeSimpleFactory(map[remote.Locator]remote.Storage{ "foo": sharedStorage, }) p, err := Open(st) require.NoError(t, err) defer p.Close() const creatorID = objstorage.CreatorID(99) require.NoError(t, p.SetCreatorID(creatorID)) meta := objstorage.ObjectMetadata{ DiskFileNum: base.FileNum(1).DiskFileNum(), FileType: base.FileTypeTable, } meta.Remote.CreatorID = 100 meta.Remote.CreatorFileNum = base.FileNum(200).DiskFileNum() meta.Remote.CleanupMethod = cleanup meta.Remote.Locator = "foo" meta.Remote.CustomObjectName = "obj-name" meta.Remote.Storage = sharedStorage h, err := p.RemoteObjectBacking(&meta) require.NoError(t, err) buf, err := h.Get() require.NoError(t, err) h.Close() _, err = h.Get() require.Error(t, err) d1, err := decodeRemoteObjectBacking(base.FileTypeTable, base.FileNum(100).DiskFileNum(), buf) require.NoError(t, err) require.Equal(t, uint64(100), uint64(d1.meta.DiskFileNum.FileNum())) require.Equal(t, base.FileTypeTable, d1.meta.FileType) d1.meta.Remote.Storage = sharedStorage require.Equal(t, meta.Remote, d1.meta.Remote) if cleanup == objstorage.SharedRefTracking { require.Equal(t, creatorID, d1.refToCheck.creatorID) require.Equal(t, base.FileNum(1).DiskFileNum(), d1.refToCheck.fileNum) } else { require.Equal(t, objstorage.CreatorID(0), d1.refToCheck.creatorID) require.Equal(t, base.FileNum(0).DiskFileNum(), d1.refToCheck.fileNum) } t.Run("unknown-tags", func(t *testing.T) { // Append a tag that is safe to ignore. buf2 := buf buf2 = binary.AppendUvarint(buf2, 13) buf2 = binary.AppendUvarint(buf2, 2) buf2 = append(buf2, 1, 1) d2, err := decodeRemoteObjectBacking(base.FileTypeTable, base.FileNum(100).DiskFileNum(), buf2) require.NoError(t, err) require.Equal(t, uint64(100), uint64(d2.meta.DiskFileNum.FileNum())) require.Equal(t, base.FileTypeTable, d2.meta.FileType) d2.meta.Remote.Storage = sharedStorage require.Equal(t, meta.Remote, d2.meta.Remote) if cleanup == objstorage.SharedRefTracking { require.Equal(t, creatorID, d2.refToCheck.creatorID) require.Equal(t, base.FileNum(1).DiskFileNum(), d2.refToCheck.fileNum) } else { require.Equal(t, objstorage.CreatorID(0), d2.refToCheck.creatorID) require.Equal(t, base.FileNum(0).DiskFileNum(), d2.refToCheck.fileNum) } buf3 := buf2 buf3 = binary.AppendUvarint(buf3, tagNotSafeToIgnoreMask+5) _, err = decodeRemoteObjectBacking(meta.FileType, meta.DiskFileNum, buf3) require.Error(t, err) require.Contains(t, err.Error(), "unknown tag") }) }) } } func TestCreateSharedObjectBacking(t *testing.T) { st := DefaultSettings(vfs.NewMem(), "") sharedStorage := remote.NewInMem() st.Remote.StorageFactory = remote.MakeSimpleFactory(map[remote.Locator]remote.Storage{ "foo": sharedStorage, }) p, err := Open(st) require.NoError(t, err) defer p.Close() require.NoError(t, p.SetCreatorID(1)) backing, err := p.CreateExternalObjectBacking("foo", "custom-obj-name") require.NoError(t, err) d, err := decodeRemoteObjectBacking(base.FileTypeTable, base.FileNum(100).DiskFileNum(), backing) require.NoError(t, err) require.Equal(t, uint64(100), uint64(d.meta.DiskFileNum.FileNum())) require.Equal(t, base.FileTypeTable, d.meta.FileType) require.Equal(t, remote.Locator("foo"), d.meta.Remote.Locator) require.Equal(t, "custom-obj-name", d.meta.Remote.CustomObjectName) require.Equal(t, objstorage.SharedNoCleanup, d.meta.Remote.CleanupMethod) } func TestAttachRemoteObjects(t *testing.T) { st := DefaultSettings(vfs.NewMem(), "") sharedStorage := remote.NewInMem() st.Remote.StorageFactory = remote.MakeSimpleFactory(map[remote.Locator]remote.Storage{ "foo": sharedStorage, }) p, err := Open(st) require.NoError(t, err) defer p.Close() require.NoError(t, p.SetCreatorID(1)) backing, err := p.CreateExternalObjectBacking("foo", "custom-obj-name") require.NoError(t, err) _, err = p.AttachRemoteObjects([]objstorage.RemoteObjectToAttach{{ FileType: base.FileTypeTable, FileNum: base.FileNum(100).DiskFileNum(), Backing: backing, }}) require.NoError(t, err) // Sync, close, and reopen the provider and expect that we see // our object. require.NoError(t, p.Sync()) require.NoError(t, p.Close()) p, err = Open(st) require.NoError(t, err) defer p.Close() require.NoError(t, p.SetCreatorID(1)) objs := p.List() require.Len(t, objs, 1) o := objs[0] require.Equal(t, remote.Locator("foo"), o.Remote.Locator) require.Equal(t, "custom-obj-name", o.Remote.CustomObjectName) require.Equal(t, uint64(100), uint64(o.DiskFileNum.FileNum())) require.Equal(t, base.FileTypeTable, o.FileType) }