github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/prolly/tree/node_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 tree
    16  
    17  import (
    18  	"context"
    19  	"sync"
    20  
    21  	"github.com/dolthub/dolt/go/store/prolly/message"
    22  
    23  	"github.com/dolthub/dolt/go/store/chunks"
    24  	"github.com/dolthub/dolt/go/store/hash"
    25  	"github.com/dolthub/dolt/go/store/pool"
    26  	"github.com/dolthub/dolt/go/store/types"
    27  )
    28  
    29  const (
    30  	cacheSize = 256 * 1024 * 1024
    31  )
    32  
    33  // NodeStore reads and writes prolly tree Nodes.
    34  type NodeStore interface {
    35  	// Read reads a prolly tree Node from the store.
    36  	Read(ctx context.Context, ref hash.Hash) (Node, error)
    37  
    38  	// ReadMany reads many prolly tree Nodes from the store.
    39  	ReadMany(ctx context.Context, refs hash.HashSlice) ([]Node, error)
    40  
    41  	// Write writes a prolly tree Node to the store.
    42  	Write(ctx context.Context, nd Node) (hash.Hash, error)
    43  
    44  	// Pool returns a buffer pool.
    45  	Pool() pool.BuffPool
    46  
    47  	// Format returns the types.NomsBinFormat of this NodeStore.
    48  	Format() *types.NomsBinFormat
    49  
    50  	BlobBuilder() *BlobBuilder
    51  }
    52  
    53  type nodeStore struct {
    54  	store chunks.ChunkStore
    55  	cache nodeCache
    56  	bp    pool.BuffPool
    57  	bbp   *sync.Pool
    58  }
    59  
    60  var _ NodeStore = nodeStore{}
    61  
    62  var sharedCache = newChunkCache(cacheSize)
    63  
    64  var sharedPool = pool.NewBuffPool()
    65  
    66  var blobBuilderPool = sync.Pool{
    67  	New: func() any {
    68  		return mustNewBlobBuilder(DefaultFixedChunkLength)
    69  	},
    70  }
    71  
    72  // NewNodeStore makes a new NodeStore.
    73  func NewNodeStore(cs chunks.ChunkStore) NodeStore {
    74  	return nodeStore{
    75  		store: cs,
    76  		cache: sharedCache,
    77  		bp:    sharedPool,
    78  		bbp:   &blobBuilderPool,
    79  	}
    80  }
    81  
    82  // Read implements NodeStore.
    83  func (ns nodeStore) Read(ctx context.Context, ref hash.Hash) (Node, error) {
    84  	n, ok := ns.cache.get(ref)
    85  	if ok {
    86  		return n, nil
    87  	}
    88  
    89  	c, err := ns.store.Get(ctx, ref)
    90  	if err != nil {
    91  		return Node{}, err
    92  	}
    93  	assertTrue(c.Size() > 0, "empty chunk returned from ChunkStore")
    94  
    95  	n, err = NodeFromBytes(c.Data())
    96  	if err != nil {
    97  		return Node{}, err
    98  	}
    99  	ns.cache.insert(ref, n)
   100  
   101  	return n, nil
   102  }
   103  
   104  // ReadMany implements NodeStore.
   105  func (ns nodeStore) ReadMany(ctx context.Context, addrs hash.HashSlice) ([]Node, error) {
   106  	found := make(map[hash.Hash]Node)
   107  	gets := hash.HashSet{}
   108  
   109  	for _, r := range addrs {
   110  		n, ok := ns.cache.get(r)
   111  		if ok {
   112  			found[r] = n
   113  		} else {
   114  			gets.Insert(r)
   115  		}
   116  	}
   117  
   118  	var nerr error
   119  	mu := new(sync.Mutex)
   120  	err := ns.store.GetMany(ctx, gets, func(ctx context.Context, chunk *chunks.Chunk) {
   121  		n, err := NodeFromBytes(chunk.Data())
   122  		if err != nil {
   123  			nerr = err
   124  		}
   125  		mu.Lock()
   126  		found[chunk.Hash()] = n
   127  		mu.Unlock()
   128  	})
   129  	if err == nil {
   130  		err = nerr
   131  	}
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	var ok bool
   137  	nodes := make([]Node, len(addrs))
   138  	for i, addr := range addrs {
   139  		nodes[i], ok = found[addr]
   140  		if ok {
   141  			ns.cache.insert(addr, nodes[i])
   142  		}
   143  	}
   144  	return nodes, nil
   145  }
   146  
   147  // Write implements NodeStore.
   148  func (ns nodeStore) Write(ctx context.Context, nd Node) (hash.Hash, error) {
   149  	c := chunks.NewChunk(nd.bytes())
   150  	assertTrue(c.Size() > 0, "cannot write empty chunk to ChunkStore")
   151  
   152  	getAddrs := func(ch chunks.Chunk) chunks.GetAddrsCb {
   153  		return func(ctx context.Context, addrs hash.HashSet, exists chunks.PendingRefExists) (err error) {
   154  			err = message.WalkAddresses(ctx, ch.Data(), func(ctx context.Context, a hash.Hash) error {
   155  				if !exists(a) {
   156  					addrs.Insert(a)
   157  				}
   158  				return nil
   159  			})
   160  			return
   161  		}
   162  	}
   163  
   164  	if err := ns.store.Put(ctx, c, getAddrs); err != nil {
   165  		return hash.Hash{}, err
   166  	}
   167  	ns.cache.insert(c.Hash(), nd)
   168  	return c.Hash(), nil
   169  }
   170  
   171  // Pool implements NodeStore.
   172  func (ns nodeStore) Pool() pool.BuffPool {
   173  	return ns.bp
   174  }
   175  
   176  // BlobBuilder implements NodeStore.
   177  func (ns nodeStore) BlobBuilder() *BlobBuilder {
   178  	bb := ns.bbp.Get().(*BlobBuilder)
   179  	if bb.ns == nil {
   180  		bb.SetNodeStore(ns)
   181  	}
   182  	return bb
   183  }
   184  
   185  func (ns nodeStore) Format() *types.NomsBinFormat {
   186  	nbf, err := types.GetFormatForVersionString(ns.store.Version())
   187  	if err != nil {
   188  		panic(err)
   189  	}
   190  	return nbf
   191  }