github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/objstorage/objstorageprovider/remote.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 "runtime" 11 "sync" 12 "sync/atomic" 13 14 "github.com/cockroachdb/errors" 15 "github.com/cockroachdb/pebble/internal/base" 16 "github.com/cockroachdb/pebble/internal/invariants" 17 "github.com/cockroachdb/pebble/objstorage" 18 "github.com/cockroachdb/pebble/objstorage/objstorageprovider/remoteobjcat" 19 "github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedcache" 20 "github.com/cockroachdb/pebble/objstorage/remote" 21 ) 22 23 // remoteSubsystem contains the provider fields related to remote storage. 24 // All fields remain unset if remote storage is not configured. 25 type remoteSubsystem struct { 26 catalog *remoteobjcat.Catalog 27 // catalogSyncMutex is used to correctly serialize two sharedSync operations. 28 // It must be acquired before the provider mutex. 29 catalogSyncMutex sync.Mutex 30 31 cache *sharedcache.Cache 32 33 // shared contains the fields relevant to shared objects, i.e. objects that 34 // are created by Pebble and potentially shared between Pebble instances. 35 shared struct { 36 // initialized guards access to the creatorID field. 37 initialized atomic.Bool 38 creatorID objstorage.CreatorID 39 initOnce sync.Once 40 41 // checkRefsOnOpen controls whether we check the ref marker file when opening 42 // an object. Normally this is true when invariants are enabled (but the provider 43 // test tweaks this field). 44 checkRefsOnOpen bool 45 } 46 } 47 48 // remoteInit initializes the remote object subsystem (if configured) and finds 49 // any remote objects. 50 func (p *provider) remoteInit() error { 51 if p.st.Remote.StorageFactory == nil { 52 return nil 53 } 54 catalog, contents, err := remoteobjcat.Open(p.st.FS, p.st.FSDirName) 55 if err != nil { 56 return errors.Wrapf(err, "pebble: could not open remote object catalog") 57 } 58 p.remote.catalog = catalog 59 p.remote.shared.checkRefsOnOpen = invariants.Enabled 60 61 // The creator ID may or may not be initialized yet. 62 if contents.CreatorID.IsSet() { 63 p.remote.initShared(contents.CreatorID) 64 p.st.Logger.Infof("remote storage configured; creatorID = %s", contents.CreatorID) 65 } else { 66 p.st.Logger.Infof("remote storage configured; no creatorID yet") 67 } 68 69 if p.st.Remote.CacheSizeBytes > 0 { 70 const defaultBlockSize = 32 * 1024 71 blockSize := p.st.Remote.CacheBlockSize 72 if blockSize == 0 { 73 blockSize = defaultBlockSize 74 } 75 76 const defaultShardingBlockSize = 1024 * 1024 77 shardingBlockSize := p.st.Remote.ShardingBlockSize 78 if shardingBlockSize == 0 { 79 shardingBlockSize = defaultShardingBlockSize 80 } 81 82 numShards := p.st.Remote.CacheShardCount 83 if numShards == 0 { 84 numShards = 2 * runtime.GOMAXPROCS(0) 85 } 86 87 p.remote.cache, err = sharedcache.Open( 88 p.st.FS, p.st.Logger, p.st.FSDirName, blockSize, shardingBlockSize, p.st.Remote.CacheSizeBytes, numShards) 89 if err != nil { 90 return errors.Wrapf(err, "pebble: could not open remote object cache") 91 } 92 } 93 94 for _, meta := range contents.Objects { 95 o := objstorage.ObjectMetadata{ 96 DiskFileNum: meta.FileNum, 97 FileType: meta.FileType, 98 } 99 o.Remote.CreatorID = meta.CreatorID 100 o.Remote.CreatorFileNum = meta.CreatorFileNum 101 o.Remote.CleanupMethod = meta.CleanupMethod 102 o.Remote.Locator = meta.Locator 103 o.Remote.CustomObjectName = meta.CustomObjectName 104 o.Remote.Storage, err = p.ensureStorageLocked(o.Remote.Locator) 105 if err != nil { 106 return errors.Wrapf(err, "creating remote.Storage object for locator '%s'", o.Remote.Locator) 107 } 108 if invariants.Enabled { 109 o.AssertValid() 110 } 111 p.mu.knownObjects[o.DiskFileNum] = o 112 } 113 return nil 114 } 115 116 // initShared initializes the creator ID, allowing use of shared objects. 117 func (ss *remoteSubsystem) initShared(creatorID objstorage.CreatorID) { 118 ss.shared.initOnce.Do(func() { 119 ss.shared.creatorID = creatorID 120 ss.shared.initialized.Store(true) 121 }) 122 } 123 124 func (p *provider) sharedClose() error { 125 if p.st.Remote.StorageFactory == nil { 126 return nil 127 } 128 var err error 129 if p.remote.cache != nil { 130 err = p.remote.cache.Close() 131 p.remote.cache = nil 132 } 133 if p.remote.catalog != nil { 134 err = firstError(err, p.remote.catalog.Close()) 135 p.remote.catalog = nil 136 } 137 return err 138 } 139 140 // SetCreatorID is part of the objstorage.Provider interface. 141 func (p *provider) SetCreatorID(creatorID objstorage.CreatorID) error { 142 if p.st.Remote.StorageFactory == nil { 143 return errors.AssertionFailedf("attempt to set CreatorID but remote storage not enabled") 144 } 145 // Note: this call is a cheap no-op if the creator ID was already set. This 146 // call also checks if we are trying to change the ID. 147 if err := p.remote.catalog.SetCreatorID(creatorID); err != nil { 148 return err 149 } 150 if !p.remote.shared.initialized.Load() { 151 p.st.Logger.Infof("remote storage creatorID set to %s", creatorID) 152 p.remote.initShared(creatorID) 153 } 154 return nil 155 } 156 157 // IsSharedForeign is part of the objstorage.Provider interface. 158 func (p *provider) IsSharedForeign(meta objstorage.ObjectMetadata) bool { 159 if !p.remote.shared.initialized.Load() { 160 return false 161 } 162 return meta.IsShared() && (meta.Remote.CreatorID != p.remote.shared.creatorID) 163 } 164 165 func (p *provider) remoteCheckInitialized() error { 166 if p.st.Remote.StorageFactory == nil { 167 return errors.Errorf("remote object support not configured") 168 } 169 return nil 170 } 171 172 func (p *provider) sharedCheckInitialized() error { 173 if err := p.remoteCheckInitialized(); err != nil { 174 return err 175 } 176 if !p.remote.shared.initialized.Load() { 177 return errors.Errorf("remote object support not available: remote creator ID not yet set") 178 } 179 return nil 180 } 181 182 func (p *provider) sharedSync() error { 183 // Serialize parallel sync operations. Note that ApplyBatch is already 184 // serialized internally, but we want to make sure they get called with 185 // batches in the right order. 186 p.remote.catalogSyncMutex.Lock() 187 defer p.remote.catalogSyncMutex.Unlock() 188 189 batch := func() remoteobjcat.Batch { 190 p.mu.Lock() 191 defer p.mu.Unlock() 192 res := p.mu.remote.catalogBatch.Copy() 193 p.mu.remote.catalogBatch.Reset() 194 return res 195 }() 196 197 if batch.IsEmpty() { 198 return nil 199 } 200 201 if err := p.remote.catalog.ApplyBatch(batch); err != nil { 202 // Put back the batch (for the next Sync), appending any operations that 203 // happened in the meantime. 204 p.mu.Lock() 205 defer p.mu.Unlock() 206 batch.Append(p.mu.remote.catalogBatch) 207 p.mu.remote.catalogBatch = batch 208 return err 209 } 210 211 return nil 212 } 213 214 func (p *provider) remotePath(meta objstorage.ObjectMetadata) string { 215 if meta.Remote.Locator != "" { 216 return fmt.Sprintf("remote-%s://%s", meta.Remote.Locator, remoteObjectName(meta)) 217 } 218 return "remote://" + remoteObjectName(meta) 219 } 220 221 // sharedCreateRef creates a reference marker object. 222 func (p *provider) sharedCreateRef(meta objstorage.ObjectMetadata) error { 223 if err := p.sharedCheckInitialized(); err != nil { 224 return err 225 } 226 if meta.Remote.CleanupMethod != objstorage.SharedRefTracking { 227 return nil 228 } 229 refName := p.sharedObjectRefName(meta) 230 writer, err := meta.Remote.Storage.CreateObject(refName) 231 if err == nil { 232 // The object is empty, just close the writer. 233 err = writer.Close() 234 } 235 if err != nil { 236 return errors.Wrapf(err, "creating marker object %q", refName) 237 } 238 return nil 239 } 240 241 func (p *provider) sharedCreate( 242 _ context.Context, 243 fileType base.FileType, 244 fileNum base.DiskFileNum, 245 locator remote.Locator, 246 opts objstorage.CreateOptions, 247 ) (objstorage.Writable, objstorage.ObjectMetadata, error) { 248 if err := p.sharedCheckInitialized(); err != nil { 249 return nil, objstorage.ObjectMetadata{}, err 250 } 251 storage, err := p.ensureStorage(locator) 252 if err != nil { 253 return nil, objstorage.ObjectMetadata{}, err 254 } 255 meta := objstorage.ObjectMetadata{ 256 DiskFileNum: fileNum, 257 FileType: fileType, 258 } 259 meta.Remote.CreatorID = p.remote.shared.creatorID 260 meta.Remote.CreatorFileNum = fileNum 261 meta.Remote.CleanupMethod = opts.SharedCleanupMethod 262 meta.Remote.Locator = locator 263 meta.Remote.Storage = storage 264 265 objName := remoteObjectName(meta) 266 writer, err := storage.CreateObject(objName) 267 if err != nil { 268 return nil, objstorage.ObjectMetadata{}, errors.Wrapf(err, "creating object %q", objName) 269 } 270 return &sharedWritable{ 271 p: p, 272 meta: meta, 273 storageWriter: writer, 274 }, meta, nil 275 } 276 277 func (p *provider) remoteOpenForReading( 278 ctx context.Context, meta objstorage.ObjectMetadata, opts objstorage.OpenOptions, 279 ) (objstorage.Readable, error) { 280 if err := p.remoteCheckInitialized(); err != nil { 281 return nil, err 282 } 283 // Verify we have a reference on this object; for performance reasons, we only 284 // do this in testing scenarios. 285 if p.remote.shared.checkRefsOnOpen && meta.Remote.CleanupMethod == objstorage.SharedRefTracking { 286 if err := p.sharedCheckInitialized(); err != nil { 287 return nil, err 288 } 289 refName := p.sharedObjectRefName(meta) 290 if _, err := meta.Remote.Storage.Size(refName); err != nil { 291 if meta.Remote.Storage.IsNotExistError(err) { 292 if opts.MustExist { 293 p.st.Logger.Fatalf("marker object %q does not exist", refName) 294 // TODO(radu): maybe list references for the object. 295 } 296 return nil, errors.Errorf("marker object %q does not exist", refName) 297 } 298 return nil, errors.Wrapf(err, "checking marker object %q", refName) 299 } 300 } 301 objName := remoteObjectName(meta) 302 reader, size, err := meta.Remote.Storage.ReadObject(ctx, objName) 303 if err != nil { 304 if opts.MustExist && meta.Remote.Storage.IsNotExistError(err) { 305 p.st.Logger.Fatalf("object %q does not exist", objName) 306 // TODO(radu): maybe list references for the object. 307 } 308 return nil, err 309 } 310 return p.newRemoteReadable(reader, size, meta.DiskFileNum), nil 311 } 312 313 func (p *provider) remoteSize(meta objstorage.ObjectMetadata) (int64, error) { 314 if err := p.remoteCheckInitialized(); err != nil { 315 return 0, err 316 } 317 objName := remoteObjectName(meta) 318 return meta.Remote.Storage.Size(objName) 319 } 320 321 // sharedUnref implements object "removal" with the remote backend. The ref 322 // marker object is removed and the backing object is removed only if there are 323 // no other ref markers. 324 func (p *provider) sharedUnref(meta objstorage.ObjectMetadata) error { 325 if meta.Remote.CleanupMethod == objstorage.SharedNoCleanup { 326 // Never delete objects in this mode. 327 return nil 328 } 329 if p.isProtected(meta.DiskFileNum) { 330 // TODO(radu): we need a mechanism to unref the object when it becomes 331 // unprotected. 332 return nil 333 } 334 335 refName := p.sharedObjectRefName(meta) 336 // Tolerate a not-exists error. 337 if err := meta.Remote.Storage.Delete(refName); err != nil && !meta.Remote.Storage.IsNotExistError(err) { 338 return err 339 } 340 otherRefs, err := meta.Remote.Storage.List(sharedObjectRefPrefix(meta), "" /* delimiter */) 341 if err != nil { 342 return err 343 } 344 if len(otherRefs) == 0 { 345 objName := remoteObjectName(meta) 346 if err := meta.Remote.Storage.Delete(objName); err != nil && !meta.Remote.Storage.IsNotExistError(err) { 347 return err 348 } 349 } 350 return nil 351 } 352 353 // ensureStorageLocked populates the remote.Storage object for the given 354 // locator, if necessary. p.mu must be held. 355 func (p *provider) ensureStorageLocked(locator remote.Locator) (remote.Storage, error) { 356 if p.mu.remote.storageObjects == nil { 357 p.mu.remote.storageObjects = make(map[remote.Locator]remote.Storage) 358 } 359 if res, ok := p.mu.remote.storageObjects[locator]; ok { 360 return res, nil 361 } 362 res, err := p.st.Remote.StorageFactory.CreateStorage(locator) 363 if err != nil { 364 return nil, err 365 } 366 367 p.mu.remote.storageObjects[locator] = res 368 return res, nil 369 } 370 371 // ensureStorage populates the remote.Storage object for the given locator, if necessary. 372 func (p *provider) ensureStorage(locator remote.Locator) (remote.Storage, error) { 373 p.mu.Lock() 374 defer p.mu.Unlock() 375 return p.ensureStorageLocked(locator) 376 }