github.com/dolthub/go-mysql-server@v0.18.0/sql/cache.go (about) 1 // Copyright 2020-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 sql 16 17 import ( 18 "fmt" 19 "runtime" 20 21 "github.com/cespare/xxhash/v2" 22 23 lru "github.com/hashicorp/golang-lru" 24 ) 25 26 // HashOf returns a hash of the given value to be used as key in a cache. 27 func HashOf(v Row) (uint64, error) { 28 hash := xxhash.New() 29 for i, x := range v { 30 if i > 0 { 31 // separate each value in the row with a nil byte 32 if _, err := hash.Write([]byte{0}); err != nil { 33 return 0, err 34 } 35 } 36 37 // TODO: probably much faster to do this with a type switch 38 // TODO: we don't have the type info necessary to appropriately encode the value of a string with a non-standard 39 // collation, which means that two strings that differ only in their collations will hash to the same value. 40 // See rowexec/grouping_key() 41 if _, err := hash.Write([]byte(fmt.Sprintf("%v,", x))); err != nil { 42 return 0, err 43 } 44 } 45 return hash.Sum64(), nil 46 } 47 48 // ErrKeyNotFound is returned when the key could not be found in the cache. 49 var ErrKeyNotFound = fmt.Errorf("memory: key not found in cache") 50 51 type lruCache struct { 52 memory Freeable 53 reporter Reporter 54 size int 55 cache *lru.Cache 56 } 57 58 func (l *lruCache) Size() int { 59 return l.size 60 } 61 62 func newLRUCache(memory Freeable, r Reporter, size uint) *lruCache { 63 lru, _ := lru.New(int(size)) 64 return &lruCache{memory, r, int(size), lru} 65 } 66 67 func (l *lruCache) Put(k uint64, v interface{}) error { 68 if releaseMemoryIfNeeded(l.reporter, l.Free, l.memory.Free) { 69 l.cache.Add(k, v) 70 } 71 return nil 72 } 73 74 func (l *lruCache) Get(k uint64) (interface{}, error) { 75 v, ok := l.cache.Get(k) 76 if !ok { 77 return nil, ErrKeyNotFound 78 } 79 80 return v, nil 81 } 82 83 func (l *lruCache) Free() { 84 l.cache, _ = lru.New(l.size) 85 } 86 87 func (l *lruCache) Dispose() { 88 l.memory = nil 89 l.cache = nil 90 } 91 92 type rowsCache struct { 93 memory Freeable 94 reporter Reporter 95 rows []Row 96 rows2 []Row2 97 } 98 99 func newRowsCache(memory Freeable, r Reporter) *rowsCache { 100 return &rowsCache{memory: memory, reporter: r} 101 } 102 103 func (c *rowsCache) Add(row Row) error { 104 if !releaseMemoryIfNeeded(c.reporter, c.memory.Free) { 105 return ErrNoMemoryAvailable.New() 106 } 107 108 c.rows = append(c.rows, row) 109 return nil 110 } 111 112 func (c *rowsCache) Get() []Row { return c.rows } 113 114 func (c *rowsCache) Add2(row2 Row2) error { 115 if !releaseMemoryIfNeeded(c.reporter, c.memory.Free) { 116 return ErrNoMemoryAvailable.New() 117 } 118 119 c.rows2 = append(c.rows2, row2) 120 return nil 121 } 122 123 func (c *rowsCache) Get2() []Row2 { 124 return c.rows2 125 } 126 127 func (c *rowsCache) Dispose() { 128 c.memory = nil 129 c.rows = nil 130 } 131 132 // mapCache is a simple in-memory implementation of a cache 133 type mapCache struct { 134 cache map[uint64]interface{} 135 } 136 137 func (m mapCache) Put(u uint64, i interface{}) error { 138 m.cache[u] = i 139 return nil 140 } 141 142 func (m mapCache) Get(u uint64) (interface{}, error) { 143 v, ok := m.cache[u] 144 if !ok { 145 return nil, ErrKeyNotFound 146 } 147 return v, nil 148 } 149 150 func (m mapCache) Size() int { 151 return len(m.cache) 152 } 153 154 func NewMapCache() mapCache { 155 return mapCache{ 156 cache: make(map[uint64]interface{}), 157 } 158 } 159 160 type historyCache struct { 161 memory Freeable 162 reporter Reporter 163 cache map[uint64]interface{} 164 } 165 166 func (h *historyCache) Size() int { 167 return len(h.cache) 168 } 169 170 func newHistoryCache(memory Freeable, r Reporter) *historyCache { 171 return &historyCache{memory, r, make(map[uint64]interface{})} 172 } 173 174 func (h *historyCache) Put(k uint64, v interface{}) error { 175 if !releaseMemoryIfNeeded(h.reporter, h.memory.Free) { 176 return ErrNoMemoryAvailable.New() 177 } 178 h.cache[k] = v 179 return nil 180 } 181 182 func (h *historyCache) Get(k uint64) (interface{}, error) { 183 v, ok := h.cache[k] 184 if !ok { 185 return nil, ErrKeyNotFound 186 } 187 return v, nil 188 } 189 190 func (h *historyCache) Dispose() { 191 h.memory = nil 192 h.cache = nil 193 } 194 195 // releasesMemoryIfNeeded releases memory if needed using the following steps 196 // until there is available memory. It returns whether or not there was 197 // available memory after all the steps. 198 func releaseMemoryIfNeeded(r Reporter, steps ...func()) bool { 199 for _, s := range steps { 200 if HasAvailableMemory(r) { 201 return true 202 } 203 204 s() 205 runtime.GC() 206 } 207 208 return HasAvailableMemory(r) 209 }