github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/nbs/bs_manifest.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package nbs 16 17 import ( 18 "bytes" 19 "context" 20 "errors" 21 22 "github.com/dolthub/dolt/go/store/blobstore" 23 "github.com/dolthub/dolt/go/store/chunks" 24 "github.com/dolthub/dolt/go/store/hash" 25 ) 26 27 const ( 28 manifestFile = "manifest" 29 ) 30 31 type blobstoreManifest struct { 32 bs blobstore.Blobstore 33 } 34 35 func (bsm blobstoreManifest) Name() string { 36 return bsm.bs.Path() 37 } 38 39 func manifestVersionAndContents(ctx context.Context, bs blobstore.Blobstore) (string, manifestContents, error) { 40 reader, ver, err := bs.Get(ctx, manifestFile, blobstore.AllRange) 41 42 if err != nil { 43 return "", manifestContents{}, err 44 } 45 46 defer reader.Close() 47 contents, err := parseManifest(reader) 48 49 if err != nil { 50 return "", manifestContents{}, err 51 } 52 53 return ver, contents, nil 54 } 55 56 // ParseIfExists looks for a manifest in the specified blobstore. If one exists 57 // will return true and the contents, else false and nil 58 func (bsm blobstoreManifest) ParseIfExists(ctx context.Context, stats *Stats, readHook func() error) (bool, manifestContents, error) { 59 if readHook != nil { 60 panic("Read hooks not supported") 61 } 62 63 _, contents, err := manifestVersionAndContents(ctx, bsm.bs) 64 65 if err != nil { 66 if blobstore.IsNotFoundError(err) { 67 return false, contents, nil 68 } 69 70 // io error 71 return true, contents, err 72 } 73 74 return true, contents, nil 75 } 76 77 // Update updates the contents of the manifest in the blobstore 78 func (bsm blobstoreManifest) Update(ctx context.Context, lastLock hash.Hash, newContents manifestContents, stats *Stats, writeHook func() error) (manifestContents, error) { 79 checker := func(upstream, contents manifestContents) error { 80 if contents.gcGen != upstream.gcGen { 81 return chunks.ErrGCGenerationExpired 82 } 83 return nil 84 } 85 86 return updateBSWithChecker(ctx, bsm.bs, checker, lastLock, newContents, writeHook) 87 } 88 89 func (bsm blobstoreManifest) UpdateGCGen(ctx context.Context, lastLock hash.Hash, newContents manifestContents, stats *Stats, writeHook func() error) (manifestContents, error) { 90 checker := func(upstream, contents manifestContents) error { 91 if contents.gcGen == upstream.gcGen { 92 return errors.New("UpdateGCGen() must update the garbage collection generation") 93 } 94 95 if contents.root != upstream.root { 96 return errors.New("UpdateGCGen() cannot update the root") 97 } 98 return nil 99 } 100 101 return updateBSWithChecker(ctx, bsm.bs, checker, lastLock, newContents, writeHook) 102 } 103 104 func updateBSWithChecker(ctx context.Context, bs blobstore.Blobstore, validate manifestChecker, lastLock hash.Hash, newContents manifestContents, writeHook func() error) (mc manifestContents, err error) { 105 if writeHook != nil { 106 panic("Write hooks not supported") 107 } 108 109 ver, contents, err := manifestVersionAndContents(ctx, bs) 110 111 if err != nil && !blobstore.IsNotFoundError(err) { 112 return manifestContents{}, err 113 } 114 115 // this is where we assert that gcGen is correct 116 err = validate(contents, newContents) 117 if err != nil { 118 return manifestContents{}, err 119 } 120 121 if contents.lock == lastLock { 122 buffer := bytes.NewBuffer(make([]byte, 64*1024)[:0]) 123 err := writeManifest(buffer, newContents) 124 125 if err != nil { 126 return manifestContents{}, err 127 } 128 129 _, err = bs.CheckAndPut(ctx, ver, manifestFile, int64(buffer.Len()), buffer) 130 131 if err != nil { 132 if !blobstore.IsCheckAndPutError(err) { 133 return manifestContents{}, err 134 } 135 } else { 136 return newContents, nil 137 } 138 } 139 140 return contents, nil 141 }