github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/chunks/memory_store.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  // This file incorporates work covered by the following copyright and
    16  // permission notice:
    17  //
    18  // Copyright 2016 Attic Labs, Inc. All rights reserved.
    19  // Licensed under the Apache License, version 2.0:
    20  // http://www.apache.org/licenses/LICENSE-2.0
    21  
    22  package chunks
    23  
    24  import (
    25  	"context"
    26  	"errors"
    27  	"fmt"
    28  	"sync"
    29  
    30  	"github.com/dolthub/dolt/go/store/constants"
    31  	"github.com/dolthub/dolt/go/store/d"
    32  	"github.com/dolthub/dolt/go/store/hash"
    33  )
    34  
    35  // MemoryStorage provides a "persistent" storage layer to back multiple
    36  // MemoryStoreViews. A MemoryStorage instance holds the ground truth for the
    37  // root and set of chunks that are visible to all MemoryStoreViews vended by
    38  // NewView(), allowing them to implement the transaction-style semantics that
    39  // ChunkStore requires.
    40  type MemoryStorage struct {
    41  	data     map[hash.Hash]Chunk
    42  	rootHash hash.Hash
    43  	mu       sync.RWMutex
    44  	version  string
    45  }
    46  
    47  // NewView vends a MemoryStoreView backed by this MemoryStorage. It's
    48  // initialized with the currently "persisted" root.
    49  func (ms *MemoryStorage) NewView() ChunkStore {
    50  	version := ms.version
    51  	if version == "" {
    52  		version = constants.FormatLD1String
    53  	}
    54  	v := &MemoryStoreView{storage: ms, rootHash: ms.rootHash, version: version}
    55  	v.gcCond = sync.NewCond(&v.mu)
    56  
    57  	return v
    58  }
    59  
    60  // NewViewWithFormat makes a MemoryStoreView with a specific NomsBinFormat.
    61  func (ms *MemoryStorage) NewViewWithFormat(nbf string) ChunkStore {
    62  	v := &MemoryStoreView{storage: ms, rootHash: ms.rootHash, version: nbf}
    63  	v.gcCond = sync.NewCond(&v.mu)
    64  	return v
    65  }
    66  
    67  // NewViewWithDefaultFormat vends a MemoryStoreView backed by this MemoryStorage. It's
    68  // initialized with the currently "persisted" root. Uses the default format.
    69  func (ms *MemoryStorage) NewViewWithDefaultFormat() ChunkStore {
    70  	v := &MemoryStoreView{storage: ms, rootHash: ms.rootHash, version: constants.FormatDefaultString}
    71  	v.gcCond = sync.NewCond(&v.mu)
    72  	return v
    73  }
    74  
    75  // Get retrieves the Chunk with the Hash h, returning EmptyChunk if it's not
    76  // present.
    77  func (ms *MemoryStorage) Get(ctx context.Context, h hash.Hash) (Chunk, error) {
    78  	if err := ctx.Err(); err != nil {
    79  		return Chunk{}, err
    80  	}
    81  	ms.mu.RLock()
    82  	defer ms.mu.RUnlock()
    83  	if c, ok := ms.data[h]; ok {
    84  		return c, nil
    85  	}
    86  	return EmptyChunk, nil
    87  }
    88  
    89  // Has returns true if the Chunk with the Hash h is present in ms.data, false
    90  // if not.
    91  func (ms *MemoryStorage) Has(ctx context.Context, r hash.Hash) (bool, error) {
    92  	ms.mu.RLock()
    93  	defer ms.mu.RUnlock()
    94  	_, ok := ms.data[r]
    95  	return ok, nil
    96  }
    97  
    98  // Len returns the number of Chunks in ms.data.
    99  func (ms *MemoryStorage) Len() int {
   100  	ms.mu.RLock()
   101  	defer ms.mu.RUnlock()
   102  	return len(ms.data)
   103  }
   104  
   105  // Root returns the currently "persisted" root hash of this in-memory store.
   106  func (ms *MemoryStorage) Root(ctx context.Context) hash.Hash {
   107  	ms.mu.RLock()
   108  	defer ms.mu.RUnlock()
   109  	return ms.rootHash
   110  }
   111  
   112  // Update checks the "persisted" root against last and, iff it matches,
   113  // updates the root to current, adds all of novel to ms.data, and returns
   114  // true. Otherwise returns false.
   115  func (ms *MemoryStorage) Update(current, last hash.Hash, novel map[hash.Hash]Chunk) bool {
   116  	ms.mu.Lock()
   117  	defer ms.mu.Unlock()
   118  	if last != ms.rootHash {
   119  		return false
   120  	}
   121  	if ms.data == nil {
   122  		ms.data = map[hash.Hash]Chunk{}
   123  	}
   124  	for h, c := range novel {
   125  		ms.data[h] = c
   126  	}
   127  	ms.rootHash = current
   128  	return true
   129  }
   130  
   131  // MemoryStoreView is an in-memory implementation of store.ChunkStore. Useful
   132  // mainly for tests.
   133  // The proper way to get one:
   134  // storage := &MemoryStorage{}
   135  // ms := storage.NewView()
   136  type MemoryStoreView struct {
   137  	pending     map[hash.Hash]Chunk
   138  	pendingRefs hash.HashSet
   139  	rootHash    hash.Hash
   140  
   141  	mu         sync.RWMutex
   142  	isGC       bool
   143  	gcCond     *sync.Cond
   144  	keeperFunc func(hash.Hash) bool
   145  
   146  	version string
   147  
   148  	storage *MemoryStorage
   149  }
   150  
   151  var _ ChunkStore = &MemoryStoreView{}
   152  var _ ChunkStoreGarbageCollector = &MemoryStoreView{}
   153  
   154  func (ms *MemoryStoreView) Get(ctx context.Context, h hash.Hash) (Chunk, error) {
   155  	ms.mu.RLock()
   156  	defer ms.mu.RUnlock()
   157  	if c, ok := ms.pending[h]; ok {
   158  		return c, nil
   159  	}
   160  	return ms.storage.Get(ctx, h)
   161  }
   162  
   163  func (ms *MemoryStoreView) GetMany(ctx context.Context, hashes hash.HashSet, found func(context.Context, *Chunk)) error {
   164  	for h := range hashes {
   165  		c, err := ms.Get(ctx, h)
   166  
   167  		if err != nil {
   168  			return err
   169  		}
   170  
   171  		if !c.IsEmpty() {
   172  			found(ctx, &c)
   173  		}
   174  	}
   175  
   176  	return nil
   177  }
   178  
   179  func (ms *MemoryStoreView) Has(ctx context.Context, h hash.Hash) (bool, error) {
   180  	ms.mu.RLock()
   181  	defer ms.mu.RUnlock()
   182  	if _, ok := ms.pending[h]; ok {
   183  		return true, nil
   184  	}
   185  	return ms.storage.Has(ctx, h)
   186  }
   187  
   188  func (ms *MemoryStoreView) HasMany(ctx context.Context, hashes hash.HashSet) (hash.HashSet, error) {
   189  	absent := hash.HashSet{}
   190  	for h := range hashes {
   191  		exists, err := ms.Has(ctx, h)
   192  		if err != nil {
   193  			return nil, err
   194  		} else if !exists {
   195  			absent.Insert(h)
   196  		}
   197  	}
   198  	return absent, nil
   199  }
   200  
   201  func (ms *MemoryStoreView) Version() string {
   202  	return ms.version
   203  }
   204  
   205  func (ms *MemoryStoreView) AccessMode() ExclusiveAccessMode {
   206  	return ExclusiveAccessMode_Shared
   207  }
   208  
   209  func (ms *MemoryStoreView) errorIfDangling(ctx context.Context, addrs hash.HashSet) error {
   210  	absent := hash.NewHashSet()
   211  	for h := range addrs {
   212  		if _, ok := ms.pending[h]; ok {
   213  			continue
   214  		}
   215  		ok, err := ms.storage.Has(ctx, h)
   216  		if err != nil {
   217  			return err
   218  		} else if !ok {
   219  			absent.Insert(h)
   220  		}
   221  	}
   222  	if absent.Size() != 0 {
   223  		return fmt.Errorf("Found dangling references to %s", absent.String())
   224  	}
   225  	return nil
   226  }
   227  
   228  func (ms *MemoryStoreView) Put(ctx context.Context, c Chunk, getAddrs GetAddrsCurry) error {
   229  	if err := ctx.Err(); err != nil {
   230  		return err
   231  	}
   232  
   233  	addrs := hash.NewHashSet()
   234  	err := getAddrs(c)(ctx, addrs, NoopPendingRefExists)
   235  	if err != nil {
   236  		return err
   237  	}
   238  
   239  	ms.mu.Lock()
   240  	defer ms.mu.Unlock()
   241  
   242  	if ms.keeperFunc != nil {
   243  		if ms.keeperFunc(c.Hash()) {
   244  			ms.waitForGC()
   245  		}
   246  	}
   247  
   248  	if ms.pendingRefs == nil {
   249  		ms.pendingRefs = addrs
   250  	} else {
   251  		ms.pendingRefs.InsertAll(addrs)
   252  	}
   253  
   254  	if ms.pending == nil {
   255  		ms.pending = map[hash.Hash]Chunk{}
   256  	}
   257  	ms.pending[c.Hash()] = c
   258  
   259  	return nil
   260  }
   261  
   262  func (ms *MemoryStoreView) Len() int {
   263  	ms.mu.RLock()
   264  	defer ms.mu.RUnlock()
   265  	return len(ms.pending) + ms.storage.Len()
   266  }
   267  
   268  func (ms *MemoryStoreView) Rebase(ctx context.Context) error {
   269  	ms.mu.Lock()
   270  	defer ms.mu.Unlock()
   271  	ms.rootHash = ms.storage.Root(ctx)
   272  
   273  	return nil
   274  }
   275  
   276  func (ms *MemoryStoreView) Root(ctx context.Context) (hash.Hash, error) {
   277  	ms.mu.RLock()
   278  	defer ms.mu.RUnlock()
   279  	return ms.rootHash, nil
   280  }
   281  
   282  func (ms *MemoryStoreView) waitForGC() {
   283  	for ms.isGC {
   284  		ms.gcCond.Wait()
   285  	}
   286  }
   287  
   288  func (ms *MemoryStoreView) transitionToGC(keeperFunc func(hash.Hash) bool) error {
   289  	ms.mu.Lock()
   290  	defer ms.mu.Unlock()
   291  	if ms.isGC == true {
   292  		return errors.New("gc already in progress")
   293  	}
   294  	ms.isGC = true
   295  	ms.keeperFunc = keeperFunc
   296  	ms.gcCond.Broadcast()
   297  	return nil
   298  }
   299  
   300  func (ms *MemoryStoreView) transitionToNoGC() {
   301  	ms.mu.Lock()
   302  	defer ms.mu.Unlock()
   303  	if !ms.isGC {
   304  		panic("attempt to toggle GC to false when GC is not true")
   305  	}
   306  	ms.isGC = false
   307  	ms.keeperFunc = nil
   308  	ms.gcCond.Broadcast()
   309  }
   310  
   311  func (ms *MemoryStoreView) Commit(ctx context.Context, current, last hash.Hash) (bool, error) {
   312  	ms.mu.Lock()
   313  	defer ms.mu.Unlock()
   314  
   315  	if ms.keeperFunc != nil {
   316  		if ms.keeperFunc(current) {
   317  			ms.waitForGC()
   318  		}
   319  	}
   320  
   321  	if last != ms.rootHash {
   322  		return false, nil
   323  	}
   324  
   325  	if err := ms.errorIfDangling(ctx, ms.pendingRefs); err != nil {
   326  		return false, err
   327  	}
   328  
   329  	success := ms.storage.Update(current, last, ms.pending)
   330  	if success {
   331  		ms.pending = nil
   332  		ms.pendingRefs = nil
   333  	}
   334  	ms.rootHash = ms.storage.Root(ctx)
   335  	return success, nil
   336  }
   337  
   338  func (ms *MemoryStoreView) BeginGC(keeper func(hash.Hash) bool) error {
   339  	return ms.transitionToGC(keeper)
   340  }
   341  
   342  func (ms *MemoryStoreView) EndGC() {
   343  	ms.transitionToNoGC()
   344  }
   345  
   346  func (ms *MemoryStoreView) MarkAndSweepChunks(ctx context.Context, hashes <-chan []hash.Hash, dest ChunkStore) error {
   347  	if dest != ms {
   348  		panic("unsupported")
   349  	}
   350  
   351  	ms.mu.Lock()
   352  	if !ms.isGC {
   353  		panic("MarkAndSweepChunks called without BeginGC")
   354  	}
   355  	ms.mu.Unlock()
   356  
   357  	keepers := make(map[hash.Hash]Chunk, ms.storage.Len())
   358  
   359  LOOP:
   360  	for {
   361  		select {
   362  		case hs, ok := <-hashes:
   363  			if !ok {
   364  				break LOOP
   365  			}
   366  			for _, h := range hs {
   367  				c, err := ms.Get(ctx, h)
   368  				if err != nil {
   369  					return err
   370  				}
   371  				keepers[h] = c
   372  			}
   373  		case <-ctx.Done():
   374  			return ctx.Err()
   375  		}
   376  	}
   377  
   378  	ms.mu.Lock()
   379  	defer ms.mu.Unlock()
   380  	ms.storage = &MemoryStorage{rootHash: ms.rootHash, data: keepers}
   381  	ms.pending = map[hash.Hash]Chunk{}
   382  	return nil
   383  }
   384  
   385  func (ms *MemoryStoreView) Stats() interface{} {
   386  	return nil
   387  }
   388  
   389  func (ms *MemoryStoreView) StatsSummary() string {
   390  	return "Unsupported"
   391  }
   392  
   393  func (ms *MemoryStoreView) PersistGhostHashes(ctx context.Context, refs hash.HashSet) error {
   394  	panic("not implemented")
   395  }
   396  
   397  func (ms *MemoryStoreView) Close() error {
   398  	return nil
   399  }
   400  
   401  type memoryStoreFactory struct {
   402  	stores map[string]*MemoryStorage
   403  	mu     *sync.Mutex
   404  }
   405  
   406  func NewMemoryStoreFactory() *memoryStoreFactory {
   407  	return &memoryStoreFactory{map[string]*MemoryStorage{}, &sync.Mutex{}}
   408  }
   409  
   410  func (f *memoryStoreFactory) CreateStoreFromCache(ctx context.Context, ns string) ChunkStore {
   411  	return f.CreateStore(ctx, ns)
   412  }
   413  
   414  func (f *memoryStoreFactory) CreateStore(ctx context.Context, ns string) ChunkStore {
   415  	f.mu.Lock()
   416  	defer f.mu.Unlock()
   417  
   418  	if f.stores == nil {
   419  		d.Panic("Cannot use memoryStoreFactory after Shutter().")
   420  	}
   421  	if ms, present := f.stores[ns]; present {
   422  		return ms.NewView()
   423  	}
   424  	f.stores[ns] = &MemoryStorage{}
   425  	return f.stores[ns].NewView()
   426  }
   427  
   428  func (f *memoryStoreFactory) Shutter() {
   429  	f.stores = nil
   430  }