github.com/cockroachdb/pebble@v1.1.2/objstorage/objstorageprovider/provider_test.go (about)

     1  // Copyright 2023 The LevelDB-Go and Pebble Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  package objstorageprovider
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"math/rand"
    11  	"strings"
    12  	"sync"
    13  	"testing"
    14  
    15  	"github.com/cockroachdb/datadriven"
    16  	"github.com/cockroachdb/pebble/internal/base"
    17  	"github.com/cockroachdb/pebble/objstorage"
    18  	"github.com/cockroachdb/pebble/objstorage/remote"
    19  	"github.com/cockroachdb/pebble/vfs"
    20  	"github.com/stretchr/testify/require"
    21  )
    22  
    23  func TestProvider(t *testing.T) {
    24  	datadriven.Walk(t, "testdata/provider", func(t *testing.T, path string) {
    25  		var log base.InMemLogger
    26  		fs := vfs.WithLogging(vfs.NewMem(), func(fmt string, args ...interface{}) {
    27  			log.Infof("<local fs> "+fmt, args...)
    28  		})
    29  		sharedStore := remote.WithLogging(remote.NewInMem(), func(fmt string, args ...interface{}) {
    30  			log.Infof("<remote> "+fmt, args...)
    31  		})
    32  		sharedFactory := remote.MakeSimpleFactory(map[remote.Locator]remote.Storage{
    33  			"": sharedStore,
    34  		})
    35  		tmpFileCounter := 0
    36  
    37  		providers := make(map[string]objstorage.Provider)
    38  		// We maintain both backings and backing handles to allow tests to use the
    39  		// backings after the handles have been closed.
    40  		backings := make(map[string]objstorage.RemoteObjectBacking)
    41  		backingHandles := make(map[string]objstorage.RemoteObjectBackingHandle)
    42  		var curProvider objstorage.Provider
    43  		readaheadConfig := DefaultReadaheadConfig
    44  		datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string {
    45  			readaheadConfig = DefaultReadaheadConfig
    46  			scanArgs := func(desc string, args ...interface{}) {
    47  				t.Helper()
    48  				if len(d.CmdArgs) != len(args) {
    49  					d.Fatalf(t, "usage: %s %s", d.Cmd, desc)
    50  				}
    51  				for i := range args {
    52  					_, err := fmt.Sscan(d.CmdArgs[i].String(), args[i])
    53  					if err != nil {
    54  						d.Fatalf(t, "%s: error parsing argument '%s'", d.Cmd, d.CmdArgs[i])
    55  					}
    56  				}
    57  			}
    58  			ctx := context.Background()
    59  
    60  			log.Reset()
    61  			switch d.Cmd {
    62  			case "open":
    63  				var fsDir string
    64  				var creatorID objstorage.CreatorID
    65  				scanArgs("<fs-dir> <remote-creator-id>", &fsDir, &creatorID)
    66  
    67  				st := DefaultSettings(fs, fsDir)
    68  				if creatorID != 0 {
    69  					st.Remote.StorageFactory = sharedFactory
    70  					st.Remote.CreateOnShared = remote.CreateOnSharedAll
    71  					st.Remote.CreateOnSharedLocator = ""
    72  				}
    73  				st.Local.ReadaheadConfigFn = func() ReadaheadConfig {
    74  					return readaheadConfig
    75  				}
    76  				require.NoError(t, fs.MkdirAll(fsDir, 0755))
    77  				p, err := Open(st)
    78  				require.NoError(t, err)
    79  				if creatorID != 0 {
    80  					require.NoError(t, p.SetCreatorID(creatorID))
    81  				}
    82  				// Checking refs on open affects the test output. We don't want tests to
    83  				// only pass when the `invariants` tag is used, so unconditionally
    84  				// enable ref checking on open.
    85  				p.(*provider).remote.shared.checkRefsOnOpen = true
    86  				providers[fsDir] = p
    87  				curProvider = p
    88  
    89  				return log.String()
    90  
    91  			case "switch":
    92  				var fsDir string
    93  				scanArgs("<fs-dir>", &fsDir)
    94  				curProvider = providers[fsDir]
    95  				if curProvider == nil {
    96  					t.Fatalf("unknown provider %s", fsDir)
    97  				}
    98  
    99  				return ""
   100  
   101  			case "close":
   102  				require.NoError(t, curProvider.Sync())
   103  				require.NoError(t, curProvider.Close())
   104  				delete(providers, curProvider.(*provider).st.FSDirName)
   105  				curProvider = nil
   106  
   107  				return log.String()
   108  
   109  			case "create":
   110  				opts := objstorage.CreateOptions{
   111  					SharedCleanupMethod: objstorage.SharedRefTracking,
   112  				}
   113  				if len(d.CmdArgs) == 5 && d.CmdArgs[4].Key == "no-ref-tracking" {
   114  					d.CmdArgs = d.CmdArgs[:4]
   115  					opts.SharedCleanupMethod = objstorage.SharedNoCleanup
   116  				}
   117  				var fileNum base.FileNum
   118  				var typ string
   119  				var salt, size int
   120  				scanArgs("<file-num> <local|shared> <salt> <size> [no-ref-tracking]", &fileNum, &typ, &salt, &size)
   121  				switch typ {
   122  				case "local":
   123  				case "shared":
   124  					opts.PreferSharedStorage = true
   125  				default:
   126  					d.Fatalf(t, "'%s' should be 'local' or 'shared'", typ)
   127  				}
   128  				w, _, err := curProvider.Create(ctx, base.FileTypeTable, fileNum.DiskFileNum(), opts)
   129  				if err != nil {
   130  					return err.Error()
   131  				}
   132  				data := make([]byte, size)
   133  				// TODO(radu): write in chunks?
   134  				genData(byte(salt), 0, data)
   135  				require.NoError(t, w.Write(data))
   136  				require.NoError(t, w.Finish())
   137  
   138  				return log.String()
   139  
   140  			case "link-or-copy":
   141  				opts := objstorage.CreateOptions{
   142  					SharedCleanupMethod: objstorage.SharedRefTracking,
   143  				}
   144  				if len(d.CmdArgs) == 5 && d.CmdArgs[4].Key == "no-ref-tracking" {
   145  					d.CmdArgs = d.CmdArgs[:4]
   146  					opts.SharedCleanupMethod = objstorage.SharedNoCleanup
   147  				}
   148  				var fileNum base.FileNum
   149  				var typ string
   150  				var salt, size int
   151  				scanArgs("<file-num> <local|shared> <salt> <size> [no-ref-tracking]", &fileNum, &typ, &salt, &size)
   152  				switch typ {
   153  				case "local":
   154  				case "shared":
   155  					opts.PreferSharedStorage = true
   156  				default:
   157  					d.Fatalf(t, "'%s' should be 'local' or 'shared'", typ)
   158  				}
   159  
   160  				tmpFileCounter++
   161  				tmpFilename := fmt.Sprintf("temp-file-%d", tmpFileCounter)
   162  				f, err := fs.Create(tmpFilename)
   163  				require.NoError(t, err)
   164  				data := make([]byte, size)
   165  				genData(byte(salt), 0, data)
   166  				n, err := f.Write(data)
   167  				require.Equal(t, len(data), n)
   168  				require.NoError(t, err)
   169  				require.NoError(t, f.Close())
   170  
   171  				_, err = curProvider.LinkOrCopyFromLocal(
   172  					ctx, fs, tmpFilename, base.FileTypeTable, fileNum.DiskFileNum(), opts,
   173  				)
   174  				require.NoError(t, err)
   175  				return log.String()
   176  
   177  			case "read":
   178  				forCompaction := d.HasArg("for-compaction")
   179  				if arg, ok := d.Arg("readahead"); ok {
   180  					var mode ReadaheadMode
   181  					switch arg.Vals[0] {
   182  					case "off":
   183  						mode = NoReadahead
   184  					case "sys-readahead":
   185  						mode = SysReadahead
   186  					case "fadvise-sequential":
   187  						mode = FadviseSequential
   188  					default:
   189  						d.Fatalf(t, "unknown readahead mode %s", arg.Vals[0])
   190  					}
   191  					if forCompaction {
   192  						readaheadConfig.Informed = mode
   193  					} else {
   194  						readaheadConfig.Speculative = mode
   195  					}
   196  				}
   197  
   198  				d.CmdArgs = d.CmdArgs[:1]
   199  				var fileNum base.FileNum
   200  				scanArgs("<file-num> [for-compaction] [readahead|speculative-overhead=off|sys-readahead|fadvise-sequential]", &fileNum)
   201  				r, err := curProvider.OpenForReading(ctx, base.FileTypeTable, fileNum.DiskFileNum(), objstorage.OpenOptions{})
   202  				if err != nil {
   203  					return err.Error()
   204  				}
   205  				var rh objstorage.ReadHandle
   206  				// Test both ways of getting a handle.
   207  				if rand.Intn(2) == 0 {
   208  					rh = r.NewReadHandle(ctx)
   209  				} else {
   210  					var prealloc PreallocatedReadHandle
   211  					rh = UsePreallocatedReadHandle(ctx, r, &prealloc)
   212  				}
   213  				if forCompaction {
   214  					rh.SetupForCompaction()
   215  				}
   216  				log.Infof("size: %d", r.Size())
   217  				for _, l := range strings.Split(d.Input, "\n") {
   218  					var offset, size int
   219  					fmt.Sscanf(l, "%d %d", &offset, &size)
   220  					data := make([]byte, size)
   221  					err := rh.ReadAt(ctx, data, int64(offset))
   222  					if err != nil {
   223  						log.Infof("%d %d: %v", offset, size, err)
   224  					} else {
   225  						salt := checkData(t, offset, data)
   226  						log.Infof("%d %d: ok (salt %d)", offset, size, salt)
   227  					}
   228  				}
   229  				require.NoError(t, rh.Close())
   230  				require.NoError(t, r.Close())
   231  				return log.String()
   232  
   233  			case "remove":
   234  				var fileNum base.FileNum
   235  				scanArgs("<file-num>", &fileNum)
   236  				if err := curProvider.Remove(base.FileTypeTable, fileNum.DiskFileNum()); err != nil {
   237  					return err.Error()
   238  				}
   239  				return log.String()
   240  
   241  			case "list":
   242  				for _, meta := range curProvider.List() {
   243  					log.Infof("%s -> %s", meta.DiskFileNum, curProvider.Path(meta))
   244  				}
   245  				return log.String()
   246  
   247  			case "save-backing":
   248  				var key string
   249  				var fileNum base.FileNum
   250  				scanArgs("<key> <file-num>", &key, &fileNum)
   251  				meta, err := curProvider.Lookup(base.FileTypeTable, fileNum.DiskFileNum())
   252  				require.NoError(t, err)
   253  				handle, err := curProvider.RemoteObjectBacking(&meta)
   254  				if err != nil {
   255  					return err.Error()
   256  				}
   257  				backing, err := handle.Get()
   258  				require.NoError(t, err)
   259  				backings[key] = backing
   260  				backingHandles[key] = handle
   261  				return log.String()
   262  
   263  			case "close-backing":
   264  				var key string
   265  				scanArgs("<key>", &key)
   266  				backingHandles[key].Close()
   267  				return ""
   268  
   269  			case "attach":
   270  				lines := strings.Split(d.Input, "\n")
   271  				if len(lines) == 0 {
   272  					d.Fatalf(t, "at least one row expected; format: <key> <file-num>")
   273  				}
   274  				var objs []objstorage.RemoteObjectToAttach
   275  				for _, l := range lines {
   276  					var key string
   277  					var fileNum base.FileNum
   278  					_, err := fmt.Sscan(l, &key, &fileNum)
   279  					require.NoError(t, err)
   280  					b, ok := backings[key]
   281  					if !ok {
   282  						d.Fatalf(t, "unknown backing key %q", key)
   283  					}
   284  					objs = append(objs, objstorage.RemoteObjectToAttach{
   285  						FileType: base.FileTypeTable,
   286  						FileNum:  fileNum.DiskFileNum(),
   287  						Backing:  b,
   288  					})
   289  				}
   290  				metas, err := curProvider.AttachRemoteObjects(objs)
   291  				if err != nil {
   292  					return log.String() + "error: " + err.Error()
   293  				}
   294  				for _, meta := range metas {
   295  					log.Infof("%s -> %s", meta.DiskFileNum, curProvider.Path(meta))
   296  				}
   297  				return log.String()
   298  
   299  			default:
   300  				d.Fatalf(t, "unknown command %s", d.Cmd)
   301  				return ""
   302  			}
   303  		})
   304  	})
   305  }
   306  
   307  func TestSharedMultipleLocators(t *testing.T) {
   308  	ctx := context.Background()
   309  	stores := map[remote.Locator]remote.Storage{
   310  		"foo": remote.NewInMem(),
   311  		"bar": remote.NewInMem(),
   312  	}
   313  	sharedFactory := remote.MakeSimpleFactory(stores)
   314  
   315  	st1 := DefaultSettings(vfs.NewMem(), "")
   316  	st1.Remote.StorageFactory = sharedFactory
   317  	st1.Remote.CreateOnShared = remote.CreateOnSharedAll
   318  	st1.Remote.CreateOnSharedLocator = "foo"
   319  	p1, err := Open(st1)
   320  	require.NoError(t, err)
   321  	require.NoError(t, p1.SetCreatorID(1))
   322  
   323  	st2 := DefaultSettings(vfs.NewMem(), "")
   324  	st2.Remote.StorageFactory = sharedFactory
   325  	st2.Remote.CreateOnShared = remote.CreateOnSharedAll
   326  	st2.Remote.CreateOnSharedLocator = "bar"
   327  	p2, err := Open(st2)
   328  	require.NoError(t, err)
   329  	require.NoError(t, p2.SetCreatorID(2))
   330  
   331  	file1 := base.FileNum(1).DiskFileNum()
   332  	file2 := base.FileNum(2).DiskFileNum()
   333  
   334  	for i, provider := range []objstorage.Provider{p1, p2} {
   335  		w, _, err := provider.Create(ctx, base.FileTypeTable, file1, objstorage.CreateOptions{
   336  			PreferSharedStorage: true,
   337  		})
   338  		require.NoError(t, err)
   339  		data := make([]byte, 100)
   340  		genData(byte(i), 0, data)
   341  		require.NoError(t, w.Write(data))
   342  		require.NoError(t, w.Finish())
   343  	}
   344  
   345  	// checkObjects reads the given object and verifies the data matches the salt.
   346  	checkObject := func(p objstorage.Provider, fileNum base.DiskFileNum, salt byte) {
   347  		t.Helper()
   348  		r, err := p.OpenForReading(ctx, base.FileTypeTable, fileNum, objstorage.OpenOptions{})
   349  		require.NoError(t, err)
   350  		data := make([]byte, r.Size())
   351  		require.NoError(t, r.ReadAt(ctx, data, 0))
   352  		r.Close()
   353  		require.Equal(t, salt, checkData(t, 0, data))
   354  	}
   355  
   356  	// Now attach p1's object (in the "foo" store) to p2.
   357  	meta1, err := p1.Lookup(base.FileTypeTable, file1)
   358  	require.NoError(t, err)
   359  	h1, err := p1.RemoteObjectBacking(&meta1)
   360  	require.NoError(t, err)
   361  	b1, err := h1.Get()
   362  	require.NoError(t, err)
   363  
   364  	_, err = p2.AttachRemoteObjects([]objstorage.RemoteObjectToAttach{{
   365  		FileNum:  file2,
   366  		FileType: base.FileTypeTable,
   367  		Backing:  b1,
   368  	}})
   369  	require.NoError(t, err)
   370  	// Close the handle from which we obtained b1.
   371  	h1.Close()
   372  	checkObject(p2, file2, 0)
   373  
   374  	// Now attach p2's object (in the "bar" store) to p1.
   375  	meta2, err := p2.Lookup(base.FileTypeTable, file1)
   376  	require.NoError(t, err)
   377  	h2, err := p2.RemoteObjectBacking(&meta2)
   378  	require.NoError(t, err)
   379  	b2, err := h2.Get()
   380  	require.NoError(t, err)
   381  	_, err = p1.AttachRemoteObjects([]objstorage.RemoteObjectToAttach{{
   382  		FileNum:  file2,
   383  		FileType: base.FileTypeTable,
   384  		Backing:  b2,
   385  	}})
   386  	require.NoError(t, err)
   387  	// Close the handle from which we obtained b2.
   388  	h2.Close()
   389  	checkObject(p1, file2, 1)
   390  
   391  	// Check that the object still works after close/reopen.
   392  	require.NoError(t, p1.Close())
   393  	p1, err = Open(st1)
   394  	require.NoError(t, err)
   395  	checkObject(p1, file2, 1)
   396  	require.NoError(t, p1.Close())
   397  
   398  	require.NoError(t, p2.Close())
   399  
   400  	// Try to attach an object to a provider that doesn't recognize the locator.
   401  	st3 := DefaultSettings(vfs.NewMem(), "")
   402  	st3.Remote.StorageFactory = remote.MakeSimpleFactory(nil)
   403  	p3, err := Open(st3)
   404  	require.NoError(t, err)
   405  	require.NoError(t, p3.SetCreatorID(3))
   406  	_, err = p3.AttachRemoteObjects([]objstorage.RemoteObjectToAttach{{
   407  		FileNum:  file2,
   408  		FileType: base.FileTypeTable,
   409  		Backing:  b2,
   410  	}})
   411  	require.Error(t, err)
   412  	require.NoError(t, p3.Close())
   413  }
   414  
   415  func TestAttachCustomObject(t *testing.T) {
   416  	ctx := context.Background()
   417  	storage := remote.NewInMem()
   418  	sharedFactory := remote.MakeSimpleFactory(map[remote.Locator]remote.Storage{
   419  		"foo": storage,
   420  	})
   421  
   422  	st1 := DefaultSettings(vfs.NewMem(), "")
   423  	st1.Remote.StorageFactory = sharedFactory
   424  	p1, err := Open(st1)
   425  	require.NoError(t, err)
   426  	defer p1.Close()
   427  	require.NoError(t, p1.SetCreatorID(1))
   428  
   429  	w, err := storage.CreateObject("some-obj-name")
   430  	require.NoError(t, err)
   431  	data := make([]byte, 100)
   432  	genData(123, 0, data)
   433  	_, err = w.Write(data)
   434  	require.NoError(t, err)
   435  	require.NoError(t, w.Close())
   436  
   437  	backing, err := p1.CreateExternalObjectBacking("foo", "some-obj-name")
   438  	require.NoError(t, err)
   439  
   440  	_, err = p1.AttachRemoteObjects([]objstorage.RemoteObjectToAttach{{
   441  		FileNum:  base.FileNum(1).DiskFileNum(),
   442  		FileType: base.FileTypeTable,
   443  		Backing:  backing,
   444  	}})
   445  	require.NoError(t, err)
   446  
   447  	// Verify the provider can read the object.
   448  	r, err := p1.OpenForReading(ctx, base.FileTypeTable, base.FileNum(1).DiskFileNum(), objstorage.OpenOptions{})
   449  	require.NoError(t, err)
   450  	require.Equal(t, int64(len(data)), r.Size())
   451  	buf := make([]byte, r.Size())
   452  	require.NoError(t, r.ReadAt(ctx, buf, 0))
   453  	require.Equal(t, byte(123), checkData(t, 0, buf))
   454  	require.NoError(t, r.Close())
   455  
   456  	// Verify that we can extract a correct backing from this provider and attach
   457  	// the object to another provider.
   458  	meta, err := p1.Lookup(base.FileTypeTable, base.FileNum(1).DiskFileNum())
   459  	require.NoError(t, err)
   460  	handle, err := p1.RemoteObjectBacking(&meta)
   461  	require.NoError(t, err)
   462  	defer handle.Close()
   463  	backing, err = handle.Get()
   464  	require.NoError(t, err)
   465  
   466  	st2 := DefaultSettings(vfs.NewMem(), "")
   467  	st2.Remote.StorageFactory = sharedFactory
   468  	p2, err := Open(st2)
   469  	require.NoError(t, err)
   470  	defer p2.Close()
   471  	require.NoError(t, p2.SetCreatorID(2))
   472  
   473  	_, err = p2.AttachRemoteObjects([]objstorage.RemoteObjectToAttach{{
   474  		FileNum:  base.FileNum(10).DiskFileNum(),
   475  		FileType: base.FileTypeTable,
   476  		Backing:  backing,
   477  	}})
   478  	require.NoError(t, err)
   479  
   480  	// Verify the provider can read the object.
   481  	r, err = p2.OpenForReading(ctx, base.FileTypeTable, base.FileNum(10).DiskFileNum(), objstorage.OpenOptions{})
   482  	require.NoError(t, err)
   483  	require.Equal(t, int64(len(data)), r.Size())
   484  	buf = make([]byte, r.Size())
   485  	require.NoError(t, r.ReadAt(ctx, buf, 0))
   486  	require.Equal(t, byte(123), checkData(t, 0, buf))
   487  	require.NoError(t, r.Close())
   488  }
   489  
   490  func TestNotExistError(t *testing.T) {
   491  	fs := vfs.NewMem()
   492  	st := DefaultSettings(fs, "")
   493  	sharedStorage := remote.NewInMem()
   494  	st.Remote.StorageFactory = remote.MakeSimpleFactory(map[remote.Locator]remote.Storage{
   495  		"": sharedStorage,
   496  	})
   497  	st.Remote.CreateOnShared = remote.CreateOnSharedAll
   498  	st.Remote.CreateOnSharedLocator = ""
   499  	provider, err := Open(st)
   500  	require.NoError(t, err)
   501  	require.NoError(t, provider.SetCreatorID(1))
   502  
   503  	for i, shared := range []bool{false, true} {
   504  		fileNum := base.FileNum(1 + i).DiskFileNum()
   505  		name := "local"
   506  		if shared {
   507  			name = "remote"
   508  		}
   509  		t.Run(name, func(t *testing.T) {
   510  			// Removing or opening an object that the provider doesn't know anything
   511  			// about should return a not-exist error.
   512  			err := provider.Remove(base.FileTypeTable, fileNum)
   513  			require.True(t, provider.IsNotExistError(err))
   514  			_, err = provider.OpenForReading(context.Background(), base.FileTypeTable, fileNum, objstorage.OpenOptions{})
   515  			require.True(t, provider.IsNotExistError(err))
   516  
   517  			w, _, err := provider.Create(context.Background(), base.FileTypeTable, fileNum, objstorage.CreateOptions{
   518  				PreferSharedStorage: shared,
   519  			})
   520  			require.NoError(t, err)
   521  			require.NoError(t, w.Write([]byte("foo")))
   522  			require.NoError(t, w.Finish())
   523  
   524  			// Remove the underlying file or object.
   525  			if !shared {
   526  				require.NoError(t, fs.Remove(base.MakeFilename(base.FileTypeTable, fileNum)))
   527  			} else {
   528  				meta, err := provider.Lookup(base.FileTypeTable, fileNum)
   529  				require.NoError(t, err)
   530  				require.NoError(t, sharedStorage.Delete(remoteObjectName(meta)))
   531  			}
   532  
   533  			_, err = provider.OpenForReading(context.Background(), base.FileTypeTable, fileNum, objstorage.OpenOptions{})
   534  			require.True(t, provider.IsNotExistError(err))
   535  
   536  			// It's acceptable for Remove to return a not-exist error, or no error at all.
   537  			if err := provider.Remove(base.FileTypeTable, fileNum); err != nil {
   538  				require.True(t, provider.IsNotExistError(err))
   539  			}
   540  		})
   541  	}
   542  }
   543  
   544  // genData generates object data that can be checked later with checkData.
   545  func genData(salt byte, offset int, p []byte) {
   546  	for i := range p {
   547  		p[i] = salt ^ xor(offset+i)
   548  	}
   549  }
   550  
   551  func checkData(t *testing.T, offset int, p []byte) (salt byte) {
   552  	t.Helper()
   553  	salt = p[0] ^ xor(offset)
   554  	for i := range p {
   555  		if p[i]^xor(offset+i) != salt {
   556  			t.Fatalf("invalid data")
   557  		}
   558  	}
   559  	return salt
   560  }
   561  
   562  // xor returns the XOR of all bytes representing the integer.
   563  func xor(n int) byte {
   564  	v := uint64(n)
   565  	v ^= v >> 32
   566  	v ^= v >> 16
   567  	v ^= v >> 8
   568  	return byte(v)
   569  }
   570  
   571  // TestParallelSync checks that multiple goroutines can create and delete
   572  // objects and sync in parallel.
   573  func TestParallelSync(t *testing.T) {
   574  	for _, shared := range []bool{false, true} {
   575  		name := "local"
   576  		if shared {
   577  			name = "shared"
   578  		}
   579  		t.Run(name, func(t *testing.T) {
   580  			st := DefaultSettings(vfs.NewMem(), "")
   581  			st.Remote.StorageFactory = remote.MakeSimpleFactory(map[remote.Locator]remote.Storage{
   582  				"": remote.NewInMem(),
   583  			})
   584  
   585  			st.Remote.CreateOnShared = remote.CreateOnSharedAll
   586  			st.Remote.CreateOnSharedLocator = ""
   587  			p, err := Open(st)
   588  			require.NoError(t, err)
   589  			require.NoError(t, p.SetCreatorID(1))
   590  
   591  			const numGoroutines = 4
   592  			const numOps = 100
   593  			var wg sync.WaitGroup
   594  			for n := 0; n < numGoroutines; n++ {
   595  				wg.Add(1)
   596  				go func(startNum int, shared bool) {
   597  					defer wg.Done()
   598  					rng := rand.New(rand.NewSource(int64(startNum)))
   599  					for i := 0; i < numOps; i++ {
   600  						num := base.FileNum(startNum + i).DiskFileNum()
   601  						w, _, err := p.Create(context.Background(), base.FileTypeTable, num, objstorage.CreateOptions{
   602  							PreferSharedStorage: shared,
   603  						})
   604  						if err != nil {
   605  							panic(err)
   606  						}
   607  						if err := w.Finish(); err != nil {
   608  							panic(err)
   609  						}
   610  						if rng.Intn(2) == 0 {
   611  							if err := p.Sync(); err != nil {
   612  								panic(err)
   613  							}
   614  						}
   615  						if err := p.Remove(base.FileTypeTable, num); err != nil {
   616  							panic(err)
   617  						}
   618  						if rng.Intn(2) == 0 {
   619  							if err := p.Sync(); err != nil {
   620  								panic(err)
   621  							}
   622  						}
   623  					}
   624  				}(numOps*n, shared)
   625  			}
   626  			wg.Wait()
   627  		})
   628  	}
   629  }