mirror of
				https://source.quilibrium.com/quilibrium/ceremonyclient.git
				synced 2025-11-04 00:27:41 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			602 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			602 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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 (
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"math/rand"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"github.com/cockroachdb/datadriven"
 | 
						|
	"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 TestProvider(t *testing.T) {
 | 
						|
	datadriven.Walk(t, "testdata/provider", func(t *testing.T, path string) {
 | 
						|
		var log base.InMemLogger
 | 
						|
		fs := vfs.WithLogging(vfs.NewMem(), func(fmt string, args ...interface{}) {
 | 
						|
			log.Infof("<local fs> "+fmt, args...)
 | 
						|
		})
 | 
						|
		sharedStore := remote.WithLogging(remote.NewInMem(), func(fmt string, args ...interface{}) {
 | 
						|
			log.Infof("<remote> "+fmt, args...)
 | 
						|
		})
 | 
						|
		sharedFactory := remote.MakeSimpleFactory(map[remote.Locator]remote.Storage{
 | 
						|
			"": sharedStore,
 | 
						|
		})
 | 
						|
		tmpFileCounter := 0
 | 
						|
 | 
						|
		providers := make(map[string]objstorage.Provider)
 | 
						|
		// We maintain both backings and backing handles to allow tests to use the
 | 
						|
		// backings after the handles have been closed.
 | 
						|
		backings := make(map[string]objstorage.RemoteObjectBacking)
 | 
						|
		backingHandles := make(map[string]objstorage.RemoteObjectBackingHandle)
 | 
						|
		var curProvider objstorage.Provider
 | 
						|
		datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string {
 | 
						|
			scanArgs := func(desc string, args ...interface{}) {
 | 
						|
				t.Helper()
 | 
						|
				if len(d.CmdArgs) != len(args) {
 | 
						|
					d.Fatalf(t, "usage: %s %s", d.Cmd, desc)
 | 
						|
				}
 | 
						|
				for i := range args {
 | 
						|
					_, err := fmt.Sscan(d.CmdArgs[i].String(), args[i])
 | 
						|
					if err != nil {
 | 
						|
						d.Fatalf(t, "%s: error parsing argument '%s'", d.Cmd, d.CmdArgs[i])
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
			ctx := context.Background()
 | 
						|
 | 
						|
			log.Reset()
 | 
						|
			switch d.Cmd {
 | 
						|
			case "open":
 | 
						|
				var fsDir string
 | 
						|
				var creatorID objstorage.CreatorID
 | 
						|
				scanArgs("<fs-dir> <remote-creator-id>", &fsDir, &creatorID)
 | 
						|
 | 
						|
				st := DefaultSettings(fs, fsDir)
 | 
						|
				if creatorID != 0 {
 | 
						|
					st.Remote.StorageFactory = sharedFactory
 | 
						|
					st.Remote.CreateOnShared = remote.CreateOnSharedAll
 | 
						|
					st.Remote.CreateOnSharedLocator = ""
 | 
						|
				}
 | 
						|
				require.NoError(t, fs.MkdirAll(fsDir, 0755))
 | 
						|
				p, err := Open(st)
 | 
						|
				require.NoError(t, err)
 | 
						|
				if creatorID != 0 {
 | 
						|
					require.NoError(t, p.SetCreatorID(creatorID))
 | 
						|
				}
 | 
						|
				// Checking refs on open affects the test output. We don't want tests to
 | 
						|
				// only pass when the `invariants` tag is used, so unconditionally
 | 
						|
				// enable ref checking on open.
 | 
						|
				p.(*provider).remote.shared.checkRefsOnOpen = true
 | 
						|
				providers[fsDir] = p
 | 
						|
				curProvider = p
 | 
						|
 | 
						|
				return log.String()
 | 
						|
 | 
						|
			case "switch":
 | 
						|
				var fsDir string
 | 
						|
				scanArgs("<fs-dir>", &fsDir)
 | 
						|
				curProvider = providers[fsDir]
 | 
						|
				if curProvider == nil {
 | 
						|
					t.Fatalf("unknown provider %s", fsDir)
 | 
						|
				}
 | 
						|
 | 
						|
				return ""
 | 
						|
 | 
						|
			case "close":
 | 
						|
				require.NoError(t, curProvider.Sync())
 | 
						|
				require.NoError(t, curProvider.Close())
 | 
						|
				delete(providers, curProvider.(*provider).st.FSDirName)
 | 
						|
				curProvider = nil
 | 
						|
 | 
						|
				return log.String()
 | 
						|
 | 
						|
			case "create":
 | 
						|
				opts := objstorage.CreateOptions{
 | 
						|
					SharedCleanupMethod: objstorage.SharedRefTracking,
 | 
						|
				}
 | 
						|
				if len(d.CmdArgs) == 5 && d.CmdArgs[4].Key == "no-ref-tracking" {
 | 
						|
					d.CmdArgs = d.CmdArgs[:4]
 | 
						|
					opts.SharedCleanupMethod = objstorage.SharedNoCleanup
 | 
						|
				}
 | 
						|
				var fileNum base.FileNum
 | 
						|
				var typ string
 | 
						|
				var salt, size int
 | 
						|
				scanArgs("<file-num> <local|shared> <salt> <size> [no-ref-tracking]", &fileNum, &typ, &salt, &size)
 | 
						|
				switch typ {
 | 
						|
				case "local":
 | 
						|
				case "shared":
 | 
						|
					opts.PreferSharedStorage = true
 | 
						|
				default:
 | 
						|
					d.Fatalf(t, "'%s' should be 'local' or 'shared'", typ)
 | 
						|
				}
 | 
						|
				w, _, err := curProvider.Create(ctx, base.FileTypeTable, fileNum.DiskFileNum(), opts)
 | 
						|
				if err != nil {
 | 
						|
					return err.Error()
 | 
						|
				}
 | 
						|
				data := make([]byte, size)
 | 
						|
				// TODO(radu): write in chunks?
 | 
						|
				genData(byte(salt), 0, data)
 | 
						|
				require.NoError(t, w.Write(data))
 | 
						|
				require.NoError(t, w.Finish())
 | 
						|
 | 
						|
				return log.String()
 | 
						|
 | 
						|
			case "link-or-copy":
 | 
						|
				opts := objstorage.CreateOptions{
 | 
						|
					SharedCleanupMethod: objstorage.SharedRefTracking,
 | 
						|
				}
 | 
						|
				if len(d.CmdArgs) == 5 && d.CmdArgs[4].Key == "no-ref-tracking" {
 | 
						|
					d.CmdArgs = d.CmdArgs[:4]
 | 
						|
					opts.SharedCleanupMethod = objstorage.SharedNoCleanup
 | 
						|
				}
 | 
						|
				var fileNum base.FileNum
 | 
						|
				var typ string
 | 
						|
				var salt, size int
 | 
						|
				scanArgs("<file-num> <local|shared> <salt> <size> [no-ref-tracking]", &fileNum, &typ, &salt, &size)
 | 
						|
				switch typ {
 | 
						|
				case "local":
 | 
						|
				case "shared":
 | 
						|
					opts.PreferSharedStorage = true
 | 
						|
				default:
 | 
						|
					d.Fatalf(t, "'%s' should be 'local' or 'shared'", typ)
 | 
						|
				}
 | 
						|
 | 
						|
				tmpFileCounter++
 | 
						|
				tmpFilename := fmt.Sprintf("temp-file-%d", tmpFileCounter)
 | 
						|
				f, err := fs.Create(tmpFilename)
 | 
						|
				require.NoError(t, err)
 | 
						|
				data := make([]byte, size)
 | 
						|
				genData(byte(salt), 0, data)
 | 
						|
				n, err := f.Write(data)
 | 
						|
				require.Equal(t, len(data), n)
 | 
						|
				require.NoError(t, err)
 | 
						|
				require.NoError(t, f.Close())
 | 
						|
 | 
						|
				_, err = curProvider.LinkOrCopyFromLocal(
 | 
						|
					ctx, fs, tmpFilename, base.FileTypeTable, fileNum.DiskFileNum(), opts,
 | 
						|
				)
 | 
						|
				require.NoError(t, err)
 | 
						|
				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
 | 
						|
				}
 | 
						|
				var fileNum base.FileNum
 | 
						|
				scanArgs("<file-num> [for-compaction]", &fileNum)
 | 
						|
				r, err := curProvider.OpenForReading(ctx, base.FileTypeTable, fileNum.DiskFileNum(), objstorage.OpenOptions{})
 | 
						|
				if err != nil {
 | 
						|
					return err.Error()
 | 
						|
				}
 | 
						|
				rh := r.NewReadHandle(ctx)
 | 
						|
				if forCompaction {
 | 
						|
					rh.SetupForCompaction()
 | 
						|
				}
 | 
						|
				log.Infof("size: %d", r.Size())
 | 
						|
				for _, l := range strings.Split(d.Input, "\n") {
 | 
						|
					var offset, size int
 | 
						|
					fmt.Sscanf(l, "%d %d", &offset, &size)
 | 
						|
					data := make([]byte, size)
 | 
						|
					err := rh.ReadAt(ctx, data, int64(offset))
 | 
						|
					if err != nil {
 | 
						|
						log.Infof("%d %d: %v", offset, size, err)
 | 
						|
					} else {
 | 
						|
						salt := checkData(t, offset, data)
 | 
						|
						log.Infof("%d %d: ok (salt %d)", offset, size, salt)
 | 
						|
					}
 | 
						|
				}
 | 
						|
				require.NoError(t, rh.Close())
 | 
						|
				require.NoError(t, r.Close())
 | 
						|
				return log.String()
 | 
						|
 | 
						|
			case "remove":
 | 
						|
				var fileNum base.FileNum
 | 
						|
				scanArgs("<file-num>", &fileNum)
 | 
						|
				if err := curProvider.Remove(base.FileTypeTable, fileNum.DiskFileNum()); err != nil {
 | 
						|
					return err.Error()
 | 
						|
				}
 | 
						|
				return log.String()
 | 
						|
 | 
						|
			case "list":
 | 
						|
				for _, meta := range curProvider.List() {
 | 
						|
					log.Infof("%s -> %s", meta.DiskFileNum, curProvider.Path(meta))
 | 
						|
				}
 | 
						|
				return log.String()
 | 
						|
 | 
						|
			case "save-backing":
 | 
						|
				var key string
 | 
						|
				var fileNum base.FileNum
 | 
						|
				scanArgs("<key> <file-num>", &key, &fileNum)
 | 
						|
				meta, err := curProvider.Lookup(base.FileTypeTable, fileNum.DiskFileNum())
 | 
						|
				require.NoError(t, err)
 | 
						|
				handle, err := curProvider.RemoteObjectBacking(&meta)
 | 
						|
				if err != nil {
 | 
						|
					return err.Error()
 | 
						|
				}
 | 
						|
				backing, err := handle.Get()
 | 
						|
				require.NoError(t, err)
 | 
						|
				backings[key] = backing
 | 
						|
				backingHandles[key] = handle
 | 
						|
				return log.String()
 | 
						|
 | 
						|
			case "close-backing":
 | 
						|
				var key string
 | 
						|
				scanArgs("<key>", &key)
 | 
						|
				backingHandles[key].Close()
 | 
						|
				return ""
 | 
						|
 | 
						|
			case "attach":
 | 
						|
				lines := strings.Split(d.Input, "\n")
 | 
						|
				if len(lines) == 0 {
 | 
						|
					d.Fatalf(t, "at least one row expected; format: <key> <file-num>")
 | 
						|
				}
 | 
						|
				var objs []objstorage.RemoteObjectToAttach
 | 
						|
				for _, l := range lines {
 | 
						|
					var key string
 | 
						|
					var fileNum base.FileNum
 | 
						|
					_, err := fmt.Sscan(l, &key, &fileNum)
 | 
						|
					require.NoError(t, err)
 | 
						|
					b, ok := backings[key]
 | 
						|
					if !ok {
 | 
						|
						d.Fatalf(t, "unknown backing key %q", key)
 | 
						|
					}
 | 
						|
					objs = append(objs, objstorage.RemoteObjectToAttach{
 | 
						|
						FileType: base.FileTypeTable,
 | 
						|
						FileNum:  fileNum.DiskFileNum(),
 | 
						|
						Backing:  b,
 | 
						|
					})
 | 
						|
				}
 | 
						|
				metas, err := curProvider.AttachRemoteObjects(objs)
 | 
						|
				if err != nil {
 | 
						|
					return log.String() + "error: " + err.Error()
 | 
						|
				}
 | 
						|
				for _, meta := range metas {
 | 
						|
					log.Infof("%s -> %s", meta.DiskFileNum, curProvider.Path(meta))
 | 
						|
				}
 | 
						|
				return log.String()
 | 
						|
 | 
						|
			default:
 | 
						|
				d.Fatalf(t, "unknown command %s", d.Cmd)
 | 
						|
				return ""
 | 
						|
			}
 | 
						|
		})
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestSharedMultipleLocators(t *testing.T) {
 | 
						|
	ctx := context.Background()
 | 
						|
	stores := map[remote.Locator]remote.Storage{
 | 
						|
		"foo": remote.NewInMem(),
 | 
						|
		"bar": remote.NewInMem(),
 | 
						|
	}
 | 
						|
	sharedFactory := remote.MakeSimpleFactory(stores)
 | 
						|
 | 
						|
	st1 := DefaultSettings(vfs.NewMem(), "")
 | 
						|
	st1.Remote.StorageFactory = sharedFactory
 | 
						|
	st1.Remote.CreateOnShared = remote.CreateOnSharedAll
 | 
						|
	st1.Remote.CreateOnSharedLocator = "foo"
 | 
						|
	p1, err := Open(st1)
 | 
						|
	require.NoError(t, err)
 | 
						|
	require.NoError(t, p1.SetCreatorID(1))
 | 
						|
 | 
						|
	st2 := DefaultSettings(vfs.NewMem(), "")
 | 
						|
	st2.Remote.StorageFactory = sharedFactory
 | 
						|
	st2.Remote.CreateOnShared = remote.CreateOnSharedAll
 | 
						|
	st2.Remote.CreateOnSharedLocator = "bar"
 | 
						|
	p2, err := Open(st2)
 | 
						|
	require.NoError(t, err)
 | 
						|
	require.NoError(t, p2.SetCreatorID(2))
 | 
						|
 | 
						|
	file1 := base.FileNum(1).DiskFileNum()
 | 
						|
	file2 := base.FileNum(2).DiskFileNum()
 | 
						|
 | 
						|
	for i, provider := range []objstorage.Provider{p1, p2} {
 | 
						|
		w, _, err := provider.Create(ctx, base.FileTypeTable, file1, objstorage.CreateOptions{
 | 
						|
			PreferSharedStorage: true,
 | 
						|
		})
 | 
						|
		require.NoError(t, err)
 | 
						|
		data := make([]byte, 100)
 | 
						|
		genData(byte(i), 0, data)
 | 
						|
		require.NoError(t, w.Write(data))
 | 
						|
		require.NoError(t, w.Finish())
 | 
						|
	}
 | 
						|
 | 
						|
	// checkObjects reads the given object and verifies the data matches the salt.
 | 
						|
	checkObject := func(p objstorage.Provider, fileNum base.DiskFileNum, salt byte) {
 | 
						|
		t.Helper()
 | 
						|
		r, err := p.OpenForReading(ctx, base.FileTypeTable, fileNum, objstorage.OpenOptions{})
 | 
						|
		require.NoError(t, err)
 | 
						|
		data := make([]byte, r.Size())
 | 
						|
		require.NoError(t, r.ReadAt(ctx, data, 0))
 | 
						|
		r.Close()
 | 
						|
		require.Equal(t, salt, checkData(t, 0, data))
 | 
						|
	}
 | 
						|
 | 
						|
	// Now attach p1's object (in the "foo" store) to p2.
 | 
						|
	meta1, err := p1.Lookup(base.FileTypeTable, file1)
 | 
						|
	require.NoError(t, err)
 | 
						|
	h1, err := p1.RemoteObjectBacking(&meta1)
 | 
						|
	require.NoError(t, err)
 | 
						|
	b1, err := h1.Get()
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	_, err = p2.AttachRemoteObjects([]objstorage.RemoteObjectToAttach{{
 | 
						|
		FileNum:  file2,
 | 
						|
		FileType: base.FileTypeTable,
 | 
						|
		Backing:  b1,
 | 
						|
	}})
 | 
						|
	require.NoError(t, err)
 | 
						|
	// Close the handle from which we obtained b1.
 | 
						|
	h1.Close()
 | 
						|
	checkObject(p2, file2, 0)
 | 
						|
 | 
						|
	// Now attach p2's object (in the "bar" store) to p1.
 | 
						|
	meta2, err := p2.Lookup(base.FileTypeTable, file1)
 | 
						|
	require.NoError(t, err)
 | 
						|
	h2, err := p2.RemoteObjectBacking(&meta2)
 | 
						|
	require.NoError(t, err)
 | 
						|
	b2, err := h2.Get()
 | 
						|
	require.NoError(t, err)
 | 
						|
	_, err = p1.AttachRemoteObjects([]objstorage.RemoteObjectToAttach{{
 | 
						|
		FileNum:  file2,
 | 
						|
		FileType: base.FileTypeTable,
 | 
						|
		Backing:  b2,
 | 
						|
	}})
 | 
						|
	require.NoError(t, err)
 | 
						|
	// Close the handle from which we obtained b2.
 | 
						|
	h2.Close()
 | 
						|
	checkObject(p1, file2, 1)
 | 
						|
 | 
						|
	// Check that the object still works after close/reopen.
 | 
						|
	require.NoError(t, p1.Close())
 | 
						|
	p1, err = Open(st1)
 | 
						|
	require.NoError(t, err)
 | 
						|
	checkObject(p1, file2, 1)
 | 
						|
	require.NoError(t, p1.Close())
 | 
						|
 | 
						|
	require.NoError(t, p2.Close())
 | 
						|
 | 
						|
	// Try to attach an object to a provider that doesn't recognize the locator.
 | 
						|
	st3 := DefaultSettings(vfs.NewMem(), "")
 | 
						|
	st3.Remote.StorageFactory = remote.MakeSimpleFactory(nil)
 | 
						|
	p3, err := Open(st3)
 | 
						|
	require.NoError(t, err)
 | 
						|
	require.NoError(t, p3.SetCreatorID(3))
 | 
						|
	_, err = p3.AttachRemoteObjects([]objstorage.RemoteObjectToAttach{{
 | 
						|
		FileNum:  file2,
 | 
						|
		FileType: base.FileTypeTable,
 | 
						|
		Backing:  b2,
 | 
						|
	}})
 | 
						|
	require.Error(t, err)
 | 
						|
	require.NoError(t, p3.Close())
 | 
						|
}
 | 
						|
 | 
						|
func TestAttachCustomObject(t *testing.T) {
 | 
						|
	ctx := context.Background()
 | 
						|
	storage := remote.NewInMem()
 | 
						|
	sharedFactory := remote.MakeSimpleFactory(map[remote.Locator]remote.Storage{
 | 
						|
		"foo": storage,
 | 
						|
	})
 | 
						|
 | 
						|
	st1 := DefaultSettings(vfs.NewMem(), "")
 | 
						|
	st1.Remote.StorageFactory = sharedFactory
 | 
						|
	p1, err := Open(st1)
 | 
						|
	require.NoError(t, err)
 | 
						|
	defer p1.Close()
 | 
						|
	require.NoError(t, p1.SetCreatorID(1))
 | 
						|
 | 
						|
	w, err := storage.CreateObject("some-obj-name")
 | 
						|
	require.NoError(t, err)
 | 
						|
	data := make([]byte, 100)
 | 
						|
	genData(123, 0, data)
 | 
						|
	_, err = w.Write(data)
 | 
						|
	require.NoError(t, err)
 | 
						|
	require.NoError(t, w.Close())
 | 
						|
 | 
						|
	backing, err := p1.CreateExternalObjectBacking("foo", "some-obj-name")
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	_, err = p1.AttachRemoteObjects([]objstorage.RemoteObjectToAttach{{
 | 
						|
		FileNum:  base.FileNum(1).DiskFileNum(),
 | 
						|
		FileType: base.FileTypeTable,
 | 
						|
		Backing:  backing,
 | 
						|
	}})
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	// Verify the provider can read the object.
 | 
						|
	r, err := p1.OpenForReading(ctx, base.FileTypeTable, base.FileNum(1).DiskFileNum(), objstorage.OpenOptions{})
 | 
						|
	require.NoError(t, err)
 | 
						|
	require.Equal(t, int64(len(data)), r.Size())
 | 
						|
	buf := make([]byte, r.Size())
 | 
						|
	require.NoError(t, r.ReadAt(ctx, buf, 0))
 | 
						|
	require.Equal(t, byte(123), checkData(t, 0, buf))
 | 
						|
	require.NoError(t, r.Close())
 | 
						|
 | 
						|
	// Verify that we can extract a correct backing from this provider and attach
 | 
						|
	// the object to another provider.
 | 
						|
	meta, err := p1.Lookup(base.FileTypeTable, base.FileNum(1).DiskFileNum())
 | 
						|
	require.NoError(t, err)
 | 
						|
	handle, err := p1.RemoteObjectBacking(&meta)
 | 
						|
	require.NoError(t, err)
 | 
						|
	defer handle.Close()
 | 
						|
	backing, err = handle.Get()
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	st2 := DefaultSettings(vfs.NewMem(), "")
 | 
						|
	st2.Remote.StorageFactory = sharedFactory
 | 
						|
	p2, err := Open(st2)
 | 
						|
	require.NoError(t, err)
 | 
						|
	defer p2.Close()
 | 
						|
	require.NoError(t, p2.SetCreatorID(2))
 | 
						|
 | 
						|
	_, err = p2.AttachRemoteObjects([]objstorage.RemoteObjectToAttach{{
 | 
						|
		FileNum:  base.FileNum(10).DiskFileNum(),
 | 
						|
		FileType: base.FileTypeTable,
 | 
						|
		Backing:  backing,
 | 
						|
	}})
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	// Verify the provider can read the object.
 | 
						|
	r, err = p2.OpenForReading(ctx, base.FileTypeTable, base.FileNum(10).DiskFileNum(), objstorage.OpenOptions{})
 | 
						|
	require.NoError(t, err)
 | 
						|
	require.Equal(t, int64(len(data)), r.Size())
 | 
						|
	buf = make([]byte, r.Size())
 | 
						|
	require.NoError(t, r.ReadAt(ctx, buf, 0))
 | 
						|
	require.Equal(t, byte(123), checkData(t, 0, buf))
 | 
						|
	require.NoError(t, r.Close())
 | 
						|
}
 | 
						|
 | 
						|
func TestNotExistError(t *testing.T) {
 | 
						|
	fs := vfs.NewMem()
 | 
						|
	st := DefaultSettings(fs, "")
 | 
						|
	sharedStorage := remote.NewInMem()
 | 
						|
	st.Remote.StorageFactory = remote.MakeSimpleFactory(map[remote.Locator]remote.Storage{
 | 
						|
		"": sharedStorage,
 | 
						|
	})
 | 
						|
	st.Remote.CreateOnShared = remote.CreateOnSharedAll
 | 
						|
	st.Remote.CreateOnSharedLocator = ""
 | 
						|
	provider, err := Open(st)
 | 
						|
	require.NoError(t, err)
 | 
						|
	require.NoError(t, provider.SetCreatorID(1))
 | 
						|
 | 
						|
	for i, shared := range []bool{false, true} {
 | 
						|
		fileNum := base.FileNum(1 + i).DiskFileNum()
 | 
						|
		name := "local"
 | 
						|
		if shared {
 | 
						|
			name = "remote"
 | 
						|
		}
 | 
						|
		t.Run(name, func(t *testing.T) {
 | 
						|
			// Removing or opening an object that the provider doesn't know anything
 | 
						|
			// about should return a not-exist error.
 | 
						|
			err := provider.Remove(base.FileTypeTable, fileNum)
 | 
						|
			require.True(t, provider.IsNotExistError(err))
 | 
						|
			_, err = provider.OpenForReading(context.Background(), base.FileTypeTable, fileNum, objstorage.OpenOptions{})
 | 
						|
			require.True(t, provider.IsNotExistError(err))
 | 
						|
 | 
						|
			w, _, err := provider.Create(context.Background(), base.FileTypeTable, fileNum, objstorage.CreateOptions{
 | 
						|
				PreferSharedStorage: shared,
 | 
						|
			})
 | 
						|
			require.NoError(t, err)
 | 
						|
			require.NoError(t, w.Write([]byte("foo")))
 | 
						|
			require.NoError(t, w.Finish())
 | 
						|
 | 
						|
			// Remove the underlying file or object.
 | 
						|
			if !shared {
 | 
						|
				require.NoError(t, fs.Remove(base.MakeFilename(base.FileTypeTable, fileNum)))
 | 
						|
			} else {
 | 
						|
				meta, err := provider.Lookup(base.FileTypeTable, fileNum)
 | 
						|
				require.NoError(t, err)
 | 
						|
				require.NoError(t, sharedStorage.Delete(remoteObjectName(meta)))
 | 
						|
			}
 | 
						|
 | 
						|
			_, err = provider.OpenForReading(context.Background(), base.FileTypeTable, fileNum, objstorage.OpenOptions{})
 | 
						|
			require.True(t, provider.IsNotExistError(err))
 | 
						|
 | 
						|
			// It's acceptable for Remove to return a not-exist error, or no error at all.
 | 
						|
			if err := provider.Remove(base.FileTypeTable, fileNum); err != nil {
 | 
						|
				require.True(t, provider.IsNotExistError(err))
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// genData generates object data that can be checked later with checkData.
 | 
						|
func genData(salt byte, offset int, p []byte) {
 | 
						|
	for i := range p {
 | 
						|
		p[i] = salt ^ xor(offset+i)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func checkData(t *testing.T, offset int, p []byte) (salt byte) {
 | 
						|
	t.Helper()
 | 
						|
	salt = p[0] ^ xor(offset)
 | 
						|
	for i := range p {
 | 
						|
		if p[i]^xor(offset+i) != salt {
 | 
						|
			t.Fatalf("invalid data")
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return salt
 | 
						|
}
 | 
						|
 | 
						|
// xor returns the XOR of all bytes representing the integer.
 | 
						|
func xor(n int) byte {
 | 
						|
	v := uint64(n)
 | 
						|
	v ^= v >> 32
 | 
						|
	v ^= v >> 16
 | 
						|
	v ^= v >> 8
 | 
						|
	return byte(v)
 | 
						|
}
 | 
						|
 | 
						|
// TestParallelSync checks that multiple goroutines can create and delete
 | 
						|
// objects and sync in parallel.
 | 
						|
func TestParallelSync(t *testing.T) {
 | 
						|
	for _, shared := range []bool{false, true} {
 | 
						|
		name := "local"
 | 
						|
		if shared {
 | 
						|
			name = "shared"
 | 
						|
		}
 | 
						|
		t.Run(name, func(t *testing.T) {
 | 
						|
			st := DefaultSettings(vfs.NewMem(), "")
 | 
						|
			st.Remote.StorageFactory = remote.MakeSimpleFactory(map[remote.Locator]remote.Storage{
 | 
						|
				"": remote.NewInMem(),
 | 
						|
			})
 | 
						|
 | 
						|
			st.Remote.CreateOnShared = remote.CreateOnSharedAll
 | 
						|
			st.Remote.CreateOnSharedLocator = ""
 | 
						|
			p, err := Open(st)
 | 
						|
			require.NoError(t, err)
 | 
						|
			require.NoError(t, p.SetCreatorID(1))
 | 
						|
 | 
						|
			const numGoroutines = 4
 | 
						|
			const numOps = 100
 | 
						|
			var wg sync.WaitGroup
 | 
						|
			for n := 0; n < numGoroutines; n++ {
 | 
						|
				wg.Add(1)
 | 
						|
				go func(startNum int, shared bool) {
 | 
						|
					defer wg.Done()
 | 
						|
					rng := rand.New(rand.NewSource(int64(startNum)))
 | 
						|
					for i := 0; i < numOps; i++ {
 | 
						|
						num := base.FileNum(startNum + i).DiskFileNum()
 | 
						|
						w, _, err := p.Create(context.Background(), base.FileTypeTable, num, objstorage.CreateOptions{
 | 
						|
							PreferSharedStorage: shared,
 | 
						|
						})
 | 
						|
						if err != nil {
 | 
						|
							panic(err)
 | 
						|
						}
 | 
						|
						if err := w.Finish(); err != nil {
 | 
						|
							panic(err)
 | 
						|
						}
 | 
						|
						if rng.Intn(2) == 0 {
 | 
						|
							if err := p.Sync(); err != nil {
 | 
						|
								panic(err)
 | 
						|
							}
 | 
						|
						}
 | 
						|
						if err := p.Remove(base.FileTypeTable, num); err != nil {
 | 
						|
							panic(err)
 | 
						|
						}
 | 
						|
						if rng.Intn(2) == 0 {
 | 
						|
							if err := p.Sync(); err != nil {
 | 
						|
								panic(err)
 | 
						|
							}
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}(numOps*(n+1), shared)
 | 
						|
			}
 | 
						|
			wg.Wait()
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 |