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