github.com/cockroachdb/pebble@v0.0.0-20231214172447-ab4952c5f87b/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 CustomObjectName: d.meta.Remote.CustomObjectName, 286 }) 287 } 288 }() 289 if err := p.sharedSync(); err != nil { 290 return nil, err 291 } 292 293 metas := make([]objstorage.ObjectMetadata, len(decoded)) 294 for i, d := range decoded { 295 metas[i] = d.meta 296 } 297 298 p.mu.Lock() 299 defer p.mu.Unlock() 300 for _, meta := range metas { 301 p.mu.knownObjects[meta.DiskFileNum] = meta 302 } 303 return metas, nil 304 }