github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/prolly/tree/node_cache.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 "fmt" 19 "sync" 20 21 "github.com/dolthub/dolt/go/store/hash" 22 ) 23 24 const ( 25 numStripes = 32 26 stripeMask byte = 0b00011111 27 ) 28 29 func newChunkCache(maxSize int) (c nodeCache) { 30 sz := maxSize / numStripes 31 for i := range c.stripes { 32 c.stripes[i] = newStripe(sz) 33 } 34 return 35 } 36 37 type nodeCache struct { 38 stripes [numStripes]*stripe 39 } 40 41 func (c nodeCache) get(addr hash.Hash) (Node, bool) { 42 s := c.stripes[addr[0]&stripeMask] 43 return s.get(addr) 44 } 45 46 func (c nodeCache) insert(addr hash.Hash, node Node) { 47 s := c.stripes[addr[0]&stripeMask] 48 s.insert(addr, node) 49 } 50 51 type centry struct { 52 a hash.Hash 53 n Node 54 i int 55 prev *centry 56 next *centry 57 } 58 59 type stripe struct { 60 mu *sync.Mutex 61 chunks map[hash.Hash]*centry 62 head *centry 63 sz int 64 maxSz int 65 rev int 66 } 67 68 func newStripe(maxSize int) *stripe { 69 return &stripe{ 70 &sync.Mutex{}, 71 make(map[hash.Hash]*centry), 72 nil, 73 0, 74 maxSize, 75 0, 76 } 77 } 78 79 func removeFromList(e *centry) { 80 e.prev.next = e.next 81 e.next.prev = e.prev 82 e.prev = e 83 e.next = e 84 } 85 86 func (s *stripe) moveToFront(e *centry) { 87 e.i = s.rev 88 s.rev++ 89 if s.head == e { 90 return 91 } 92 if s.head != nil { 93 removeFromList(e) 94 e.next = s.head 95 e.prev = s.head.prev 96 s.head.prev = e 97 e.prev.next = e 98 } 99 s.head = e 100 } 101 102 func (s *stripe) get(h hash.Hash) (Node, bool) { 103 s.mu.Lock() 104 defer s.mu.Unlock() 105 if e, ok := s.chunks[h]; ok { 106 s.moveToFront(e) 107 return e.n, true 108 } else { 109 return Node{}, false 110 } 111 } 112 113 func (s *stripe) insert(addr hash.Hash, node Node) { 114 s.mu.Lock() 115 defer s.mu.Unlock() 116 117 if e, ok := s.chunks[addr]; !ok { 118 e = ¢ry{addr, node, 0, nil, nil} 119 e.next = e 120 e.prev = e 121 s.moveToFront(e) 122 s.chunks[addr] = e 123 s.sz += node.Size() 124 s.shrinkToMaxSz() 125 } else { 126 s.moveToFront(e) 127 } 128 } 129 130 func (s *stripe) shrinkToMaxSz() { 131 for s.sz > s.maxSz { 132 if s.head != nil { 133 t := s.head.prev 134 removeFromList(t) 135 if t == s.head { 136 s.head = nil 137 } 138 delete(s.chunks, t.a) 139 s.sz -= t.n.Size() 140 } else { 141 panic("cache is empty but cache Size is > than max Size") 142 } 143 } 144 } 145 146 func (s *stripe) sanityCheck() { 147 if s.head != nil { 148 p := s.head.next 149 i := 1 150 sz := s.head.n.Size() 151 lasti := s.head.i 152 for p != s.head { 153 i++ 154 sz += p.n.Size() 155 if p.i >= lasti { 156 panic("encountered lru list entry with higher rev later in the list.") 157 } 158 p = p.next 159 } 160 if i != len(s.chunks) { 161 panic(fmt.Sprintf("cache lru list has different Size than cache.chunks. %d vs %d", i, len(s.chunks))) 162 } 163 if sz != s.sz { 164 panic("entries reachable from lru list have different Size than cache.sz.") 165 } 166 j := 1 167 p = s.head.prev 168 for p != s.head { 169 j++ 170 p = p.prev 171 } 172 if j != i { 173 panic("length of list backwards is not equal to length of list forward") 174 } 175 } else { 176 if len(s.chunks) != 0 { 177 panic("lru list is empty but s.chunks is not") 178 } 179 if s.sz != 0 { 180 panic("lru list is empty but s.sz is not 0") 181 } 182 } 183 }