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  }