github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/objstorage/objstorageprovider/remote_backing.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  	"bytes"
     9  	"encoding/binary"
    10  	"io"
    11  
    12  	"github.com/cockroachdb/errors"
    13  	"github.com/cockroachdb/pebble/internal/base"
    14  	"github.com/cockroachdb/pebble/objstorage"
    15  	"github.com/cockroachdb/pebble/objstorage/objstorageprovider/remoteobjcat"
    16  	"github.com/cockroachdb/pebble/objstorage/remote"
    17  )
    18  
    19  const (
    20  	tagCreatorID      = 1
    21  	tagCreatorFileNum = 2
    22  	tagCleanupMethod  = 3
    23  	// tagRefCheckID encodes the information for a ref marker that needs to be
    24  	// checked when attaching this object to another provider. This is set to the
    25  	// creator ID and FileNum for the provider that encodes the backing, and
    26  	// allows the "target" provider to check that the "source" provider kept its
    27  	// reference on the object alive.
    28  	tagRefCheckID = 4
    29  	// tagLocator encodes the remote.Locator; if absent the locator is "". It is
    30  	// followed by the locator string length and the locator string.
    31  	tagLocator = 5
    32  	// tagLocator encodes a custom object name (if present). It is followed by the
    33  	// custom name string length and the string.
    34  	tagCustomObjectName = 6
    35  
    36  	// Any new tags that don't have the tagNotSafeToIgnoreMask bit set must be
    37  	// followed by the length of the data (so they can be skipped).
    38  
    39  	// Any new tags that have the tagNotSafeToIgnoreMask bit set cause errors if
    40  	// they are encountered by earlier code that doesn't know the tag.
    41  	tagNotSafeToIgnoreMask = 64
    42  )
    43  
    44  func (p *provider) encodeRemoteObjectBacking(
    45  	meta *objstorage.ObjectMetadata,
    46  ) (objstorage.RemoteObjectBacking, error) {
    47  	if !meta.IsRemote() {
    48  		return nil, errors.AssertionFailedf("object %s not on remote storage", meta.DiskFileNum)
    49  	}
    50  
    51  	buf := make([]byte, 0, binary.MaxVarintLen64*4)
    52  	buf = binary.AppendUvarint(buf, tagCreatorID)
    53  	buf = binary.AppendUvarint(buf, uint64(meta.Remote.CreatorID))
    54  	// TODO(radu): encode file type as well?
    55  	buf = binary.AppendUvarint(buf, tagCreatorFileNum)
    56  	buf = binary.AppendUvarint(buf, uint64(meta.Remote.CreatorFileNum.FileNum()))
    57  	buf = binary.AppendUvarint(buf, tagCleanupMethod)
    58  	buf = binary.AppendUvarint(buf, uint64(meta.Remote.CleanupMethod))
    59  	if meta.Remote.CleanupMethod == objstorage.SharedRefTracking {
    60  		buf = binary.AppendUvarint(buf, tagRefCheckID)
    61  		buf = binary.AppendUvarint(buf, uint64(p.remote.shared.creatorID))
    62  		buf = binary.AppendUvarint(buf, uint64(meta.DiskFileNum.FileNum()))
    63  	}
    64  	if meta.Remote.Locator != "" {
    65  		buf = binary.AppendUvarint(buf, tagLocator)
    66  		buf = encodeString(buf, string(meta.Remote.Locator))
    67  	}
    68  	if meta.Remote.CustomObjectName != "" {
    69  		buf = binary.AppendUvarint(buf, tagCustomObjectName)
    70  		buf = encodeString(buf, meta.Remote.CustomObjectName)
    71  	}
    72  	return buf, nil
    73  }
    74  
    75  type remoteObjectBackingHandle struct {
    76  	backing objstorage.RemoteObjectBacking
    77  	fileNum base.DiskFileNum
    78  	p       *provider
    79  }
    80  
    81  func (s *remoteObjectBackingHandle) Get() (objstorage.RemoteObjectBacking, error) {
    82  	if s.backing == nil {
    83  		return nil, errors.Errorf("RemoteObjectBackingHandle.Get() called after Close()")
    84  	}
    85  	return s.backing, nil
    86  }
    87  
    88  func (s *remoteObjectBackingHandle) Close() {
    89  	if s.backing != nil {
    90  		s.backing = nil
    91  		s.p.unprotectObject(s.fileNum)
    92  	}
    93  }
    94  
    95  var _ objstorage.RemoteObjectBackingHandle = (*remoteObjectBackingHandle)(nil)
    96  
    97  // RemoteObjectBacking is part of the objstorage.Provider interface.
    98  func (p *provider) RemoteObjectBacking(
    99  	meta *objstorage.ObjectMetadata,
   100  ) (objstorage.RemoteObjectBackingHandle, error) {
   101  	backing, err := p.encodeRemoteObjectBacking(meta)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	p.protectObject(meta.DiskFileNum)
   106  	return &remoteObjectBackingHandle{
   107  		backing: backing,
   108  		fileNum: meta.DiskFileNum,
   109  		p:       p,
   110  	}, nil
   111  }
   112  
   113  // CreateExternalObjectBacking is part of the objstorage.Provider interface.
   114  func (p *provider) CreateExternalObjectBacking(
   115  	locator remote.Locator, objName string,
   116  ) (objstorage.RemoteObjectBacking, error) {
   117  	var meta objstorage.ObjectMetadata
   118  	meta.Remote.Locator = locator
   119  	meta.Remote.CustomObjectName = objName
   120  	meta.Remote.CleanupMethod = objstorage.SharedNoCleanup
   121  	return p.encodeRemoteObjectBacking(&meta)
   122  }
   123  
   124  type decodedBacking struct {
   125  	meta objstorage.ObjectMetadata
   126  	// refToCheck is set only when meta.Remote.CleanupMethod is RefTracking
   127  	refToCheck struct {
   128  		creatorID objstorage.CreatorID
   129  		fileNum   base.DiskFileNum
   130  	}
   131  }
   132  
   133  // decodeRemoteObjectBacking decodes the remote object metadata.
   134  //
   135  // Note that the meta.Remote.Storage field is not set.
   136  func decodeRemoteObjectBacking(
   137  	fileType base.FileType, fileNum base.DiskFileNum, buf objstorage.RemoteObjectBacking,
   138  ) (decodedBacking, error) {
   139  	var creatorID, creatorFileNum, cleanupMethod, refCheckCreatorID, refCheckFileNum uint64
   140  	var locator, customObjName string
   141  	br := bytes.NewReader(buf)
   142  	for {
   143  		tag, err := binary.ReadUvarint(br)
   144  		if err == io.EOF {
   145  			break
   146  		}
   147  		if err != nil {
   148  			return decodedBacking{}, err
   149  		}
   150  		switch tag {
   151  		case tagCreatorID:
   152  			creatorID, err = binary.ReadUvarint(br)
   153  
   154  		case tagCreatorFileNum:
   155  			creatorFileNum, err = binary.ReadUvarint(br)
   156  
   157  		case tagCleanupMethod:
   158  			cleanupMethod, err = binary.ReadUvarint(br)
   159  
   160  		case tagRefCheckID:
   161  			refCheckCreatorID, err = binary.ReadUvarint(br)
   162  			if err == nil {
   163  				refCheckFileNum, err = binary.ReadUvarint(br)
   164  			}
   165  
   166  		case tagLocator:
   167  			locator, err = decodeString(br)
   168  
   169  		case tagCustomObjectName:
   170  			customObjName, err = decodeString(br)
   171  
   172  		default:
   173  			// Ignore unknown tags, unless they're not safe to ignore.
   174  			if tag&tagNotSafeToIgnoreMask != 0 {
   175  				return decodedBacking{}, errors.Newf("unknown tag %d", tag)
   176  			}
   177  			var dataLen uint64
   178  			dataLen, err = binary.ReadUvarint(br)
   179  			if err == nil {
   180  				_, err = br.Seek(int64(dataLen), io.SeekCurrent)
   181  			}
   182  		}
   183  		if err != nil {
   184  			return decodedBacking{}, err
   185  		}
   186  	}
   187  	if customObjName == "" {
   188  		if creatorID == 0 {
   189  			return decodedBacking{}, errors.Newf("remote object backing missing creator ID")
   190  		}
   191  		if creatorFileNum == 0 {
   192  			return decodedBacking{}, errors.Newf("remote object backing missing creator file num")
   193  		}
   194  	}
   195  	var res decodedBacking
   196  	res.meta.DiskFileNum = fileNum
   197  	res.meta.FileType = fileType
   198  	res.meta.Remote.CreatorID = objstorage.CreatorID(creatorID)
   199  	res.meta.Remote.CreatorFileNum = base.FileNum(creatorFileNum).DiskFileNum()
   200  	res.meta.Remote.CleanupMethod = objstorage.SharedCleanupMethod(cleanupMethod)
   201  	if res.meta.Remote.CleanupMethod == objstorage.SharedRefTracking {
   202  		if refCheckCreatorID == 0 || refCheckFileNum == 0 {
   203  			return decodedBacking{}, errors.Newf("remote object backing missing ref to check")
   204  		}
   205  		res.refToCheck.creatorID = objstorage.CreatorID(refCheckCreatorID)
   206  		res.refToCheck.fileNum = base.FileNum(refCheckFileNum).DiskFileNum()
   207  	}
   208  	res.meta.Remote.Locator = remote.Locator(locator)
   209  	res.meta.Remote.CustomObjectName = customObjName
   210  	return res, nil
   211  }
   212  
   213  func encodeString(buf []byte, s string) []byte {
   214  	buf = binary.AppendUvarint(buf, uint64(len(s)))
   215  	buf = append(buf, []byte(s)...)
   216  	return buf
   217  }
   218  
   219  func decodeString(br io.ByteReader) (string, error) {
   220  	length, err := binary.ReadUvarint(br)
   221  	if err != nil || length == 0 {
   222  		return "", err
   223  	}
   224  	buf := make([]byte, length)
   225  	for i := range buf {
   226  		buf[i], err = br.ReadByte()
   227  		if err != nil {
   228  			return "", err
   229  		}
   230  	}
   231  	return string(buf), nil
   232  }
   233  
   234  // AttachRemoteObjects is part of the objstorage.Provider interface.
   235  func (p *provider) AttachRemoteObjects(
   236  	objs []objstorage.RemoteObjectToAttach,
   237  ) ([]objstorage.ObjectMetadata, error) {
   238  	decoded := make([]decodedBacking, len(objs))
   239  	for i, o := range objs {
   240  		var err error
   241  		decoded[i], err = decodeRemoteObjectBacking(o.FileType, o.FileNum, o.Backing)
   242  		if err != nil {
   243  			return nil, err
   244  		}
   245  		decoded[i].meta.Remote.Storage, err = p.ensureStorage(decoded[i].meta.Remote.Locator)
   246  		if err != nil {
   247  			return nil, err
   248  		}
   249  	}
   250  
   251  	// Create the reference marker objects.
   252  	// TODO(radu): parallelize this.
   253  	for _, d := range decoded {
   254  		if d.meta.Remote.CleanupMethod != objstorage.SharedRefTracking {
   255  			continue
   256  		}
   257  		if err := p.sharedCreateRef(d.meta); err != nil {
   258  			// TODO(radu): clean up references previously created in this loop.
   259  			return nil, err
   260  		}
   261  		// Check the "origin"'s reference.
   262  		refName := sharedObjectRefName(d.meta, d.refToCheck.creatorID, d.refToCheck.fileNum)
   263  		if _, err := d.meta.Remote.Storage.Size(refName); err != nil {
   264  			_ = p.sharedUnref(d.meta)
   265  			// TODO(radu): clean up references previously created in this loop.
   266  			if d.meta.Remote.Storage.IsNotExistError(err) {
   267  				return nil, errors.Errorf("origin marker object %q does not exist;"+
   268  					" object probably removed from the provider which created the backing", refName)
   269  			}
   270  			return nil, errors.Wrapf(err, "checking origin's marker object %s", refName)
   271  		}
   272  	}
   273  
   274  	func() {
   275  		p.mu.Lock()
   276  		defer p.mu.Unlock()
   277  		for _, d := range decoded {
   278  			p.mu.remote.catalogBatch.AddObject(remoteobjcat.RemoteObjectMetadata{
   279  				FileNum:        d.meta.DiskFileNum,
   280  				FileType:       d.meta.FileType,
   281  				CreatorID:      d.meta.Remote.CreatorID,
   282  				CreatorFileNum: d.meta.Remote.CreatorFileNum,
   283  				CleanupMethod:  d.meta.Remote.CleanupMethod,
   284  				Locator:        d.meta.Remote.Locator,
   285  			})
   286  		}
   287  	}()
   288  	if err := p.sharedSync(); err != nil {
   289  		return nil, err
   290  	}
   291  
   292  	metas := make([]objstorage.ObjectMetadata, len(decoded))
   293  	for i, d := range decoded {
   294  		metas[i] = d.meta
   295  	}
   296  
   297  	p.mu.Lock()
   298  	defer p.mu.Unlock()
   299  	for _, meta := range metas {
   300  		p.mu.knownObjects[meta.DiskFileNum] = meta
   301  	}
   302  	return metas, nil
   303  }