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 }