github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/valuefile/file_value_store.go (about) 1 // Copyright 2021 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 valuefile 16 17 import ( 18 "context" 19 "sort" 20 "sync" 21 22 "github.com/dolthub/dolt/go/store/chunks" 23 "github.com/dolthub/dolt/go/store/hash" 24 "github.com/dolthub/dolt/go/store/types" 25 ) 26 27 var _ chunks.ChunkStore = (*FileValueStore)(nil) 28 var _ types.ValueReadWriter = (*FileValueStore)(nil) 29 30 // FileValueStore implements a trivial in memory chunks.ChunkStore and types.ValueReadWriter in order to allow easy 31 // serialization / deserialization of noms data to and from a file 32 type FileValueStore struct { 33 nbf *types.NomsBinFormat 34 35 valLock *sync.Mutex 36 values map[hash.Hash]types.Value 37 38 rootHash hash.Hash 39 chunkLock *sync.Mutex 40 chunks map[hash.Hash][]byte 41 } 42 43 // NewFileValueStore creates a new FileValueStore 44 func NewFileValueStore(nbf *types.NomsBinFormat) (*FileValueStore, error) { 45 return &FileValueStore{ 46 nbf: nbf, 47 valLock: &sync.Mutex{}, 48 values: make(map[hash.Hash]types.Value), 49 chunkLock: &sync.Mutex{}, 50 chunks: make(map[hash.Hash][]byte), 51 }, nil 52 } 53 54 // Gets the NomsBinaryFormat for the Store 55 func (f *FileValueStore) Format() *types.NomsBinFormat { 56 return f.nbf 57 } 58 59 // ReadValue reads a value from the store 60 func (f *FileValueStore) ReadValue(ctx context.Context, h hash.Hash) (types.Value, error) { 61 f.valLock.Lock() 62 defer f.valLock.Unlock() 63 64 v := f.values[h] 65 return v, nil 66 } 67 68 // ReadManyValues reads and decodes Values indicated by |hashes| from lvs and returns the found Values in the same order. 69 // Any non-present Values will be represented by nil. 70 func (f *FileValueStore) ReadManyValues(ctx context.Context, hashes hash.HashSlice) (types.ValueSlice, error) { 71 f.valLock.Lock() 72 defer f.valLock.Unlock() 73 74 vals := make(types.ValueSlice, len(hashes)) 75 for i, h := range hashes { 76 vals[i] = f.values[h] 77 } 78 79 return vals, nil 80 } 81 82 // WriteValue adds a value to the store 83 func (f *FileValueStore) WriteValue(ctx context.Context, v types.Value) (types.Ref, error) { 84 f.valLock.Lock() 85 defer f.valLock.Unlock() 86 87 h, err := v.Hash(f.nbf) 88 89 if err != nil { 90 return types.Ref{}, err 91 } 92 93 _, ok := f.values[h] 94 95 if !ok { 96 f.values[h] = v 97 98 c, err := types.EncodeValue(v, f.nbf) 99 100 if err != nil { 101 return types.Ref{}, err 102 } 103 104 err = f.Put(ctx, c) 105 106 if err != nil { 107 return types.Ref{}, err 108 } 109 } 110 111 return types.NewRef(v, f.nbf) 112 } 113 114 // Get gets a chunk by it's hash 115 func (f *FileValueStore) Get(ctx context.Context, h hash.Hash) (chunks.Chunk, error) { 116 f.chunkLock.Lock() 117 defer f.chunkLock.Unlock() 118 119 data, ok := f.chunks[h] 120 121 if !ok { 122 return chunks.EmptyChunk, nil 123 } else { 124 return chunks.NewChunkWithHash(h, data), nil 125 } 126 } 127 128 // GetMany gets chunks by their hashes. Chunks that are found are written to the channel. 129 func (f *FileValueStore) GetMany(ctx context.Context, hashes hash.HashSet, found func(*chunks.Chunk)) error { 130 f.chunkLock.Lock() 131 defer f.chunkLock.Unlock() 132 133 for h := range hashes { 134 data, ok := f.chunks[h] 135 136 if ok { 137 ch := chunks.NewChunkWithHash(h, data) 138 found(&ch) 139 } 140 } 141 142 return nil 143 } 144 145 // Has returns true if a chunk is present in the store, false if not 146 func (f *FileValueStore) Has(ctx context.Context, h hash.Hash) (bool, error) { 147 f.chunkLock.Lock() 148 defer f.chunkLock.Unlock() 149 150 _, ok := f.chunks[h] 151 return ok, nil 152 } 153 154 // HasMany returns the set of hashes that are absent from the store 155 func (f *FileValueStore) HasMany(ctx context.Context, hashes hash.HashSet) (absent hash.HashSet, err error) { 156 f.chunkLock.Lock() 157 defer f.chunkLock.Unlock() 158 159 absent = make(hash.HashSet, len(hashes)) 160 for h := range hashes { 161 _, ok := f.chunks[h] 162 163 if !ok { 164 absent[h] = struct{}{} 165 } 166 } 167 168 return absent, nil 169 } 170 171 // Put puts a chunk inton the store 172 func (f *FileValueStore) Put(ctx context.Context, c chunks.Chunk) error { 173 f.chunkLock.Lock() 174 defer f.chunkLock.Unlock() 175 176 f.chunks[c.Hash()] = c.Data() 177 return nil 178 } 179 180 // Version returns the nbf version string 181 func (f *FileValueStore) Version() string { 182 return f.nbf.VersionString() 183 } 184 185 // Rebase brings this ChunkStore into sync with the persistent storage's current root. Has no impact here 186 func (f *FileValueStore) Rebase(ctx context.Context) error { 187 f.chunkLock.Lock() 188 defer f.chunkLock.Unlock() 189 return nil 190 } 191 192 // Root returns the root hash 193 func (f *FileValueStore) Root(ctx context.Context) (hash.Hash, error) { 194 f.chunkLock.Lock() 195 defer f.chunkLock.Unlock() 196 return f.rootHash, nil 197 } 198 199 // Commit sets the root hash 200 func (f *FileValueStore) Commit(ctx context.Context, current, last hash.Hash) (bool, error) { 201 f.chunkLock.Lock() 202 defer f.chunkLock.Unlock() 203 204 if f.rootHash == last { 205 f.rootHash = current 206 return true, nil 207 } 208 209 return false, nil 210 } 211 212 // Stats doesn't do anything 213 func (f *FileValueStore) Stats() interface{} { 214 return nil 215 } 216 217 // StatsSummary doesn't do anything 218 func (f *FileValueStore) StatsSummary() string { 219 return "" 220 } 221 222 // Close doesn't do anything 223 func (f *FileValueStore) Close() error { 224 f.chunkLock.Lock() 225 defer f.chunkLock.Unlock() 226 227 return nil 228 } 229 230 func (f *FileValueStore) numChunks() int { 231 f.chunkLock.Lock() 232 defer f.chunkLock.Unlock() 233 234 return len(f.chunks) 235 } 236 237 func (f *FileValueStore) iterChunks(cb func(ch chunks.Chunk) error) error { 238 f.chunkLock.Lock() 239 defer f.chunkLock.Unlock() 240 241 hashes := make(hash.HashSlice, 0, len(f.chunks)) 242 for h := range f.chunks { 243 hashes = append(hashes, h) 244 } 245 246 sort.Slice(hashes, func(i, j int) bool { 247 return hashes[i].Less(hashes[j]) 248 }) 249 250 for _, h := range hashes { 251 data := f.chunks[h] 252 err := cb(chunks.NewChunkWithHash(h, data)) 253 254 if err != nil { 255 return err 256 } 257 } 258 259 return nil 260 }