github.com/dolthub/go-mysql-server@v0.18.0/sql/memory.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 "os" 19 "runtime" 20 "strconv" 21 "sync" 22 23 errors "gopkg.in/src-d/go-errors.v1" 24 ) 25 26 // Disposable objects can erase all their content when they're no longer in use. 27 // Expressions and Nodes that implement Disposable will have Dispose called on them as a final stage of query 28 // execution. This can be used to clean up cached memory that wouldn't get caught via the normal garbage collection 29 // process. 30 type Disposable interface { 31 // Dispose the contents. 32 Dispose() 33 } 34 35 func Dispose(i interface{}) { 36 if d, ok := i.(Disposable); ok { 37 d.Dispose() 38 } 39 } 40 41 // Freeable objects can free their memory. 42 type Freeable interface { 43 // Free the memory. 44 Free() 45 } 46 47 // KeyValueCache is a cache of key value pairs. 48 type KeyValueCache interface { 49 // Put a new value in the cache. 50 Put(uint64, interface{}) error 51 // Get the value with the given key. An error is returned if the specified key does not exist. 52 Get(uint64) (interface{}, error) 53 // Size returns the number of elements in the cache. 54 Size() int 55 } 56 57 // RowsCache is a cache of rows. 58 type RowsCache interface { 59 // Add a new row to the cache. If there is no memory available, it will try to 60 // free some memory. If after that there is still no memory available, it 61 // will return an error and erase all the content of the cache. 62 Add(Row) error 63 // Get returns all rows. 64 Get() []Row 65 } 66 67 // Rows2Cache is a cache of Row2s. 68 type Rows2Cache interface { 69 RowsCache 70 // Add2 a new row to the cache. If there is no memory available, it will try to 71 // free some memory. If after that there is still no memory available, it 72 // will return an error and erase all the content of the cache. 73 Add2(Row2) error 74 // Get2 gets all rows. 75 Get2() []Row2 76 } 77 78 // ErrNoMemoryAvailable is returned when there is no more available memory. 79 var ErrNoMemoryAvailable = errors.NewKind("no memory available") 80 81 const maxMemoryKey = "MAX_MEMORY" 82 83 const ( 84 b = 1 85 kib = 1024 * b 86 mib = 1024 * kib 87 ) 88 89 var maxMemory = func() uint64 { 90 val := os.Getenv(maxMemoryKey) 91 var v uint64 92 if val != "" { 93 var err error 94 v, err = strconv.ParseUint(val, 10, 64) 95 if err != nil { 96 panic("MAX_MEMORY environment variable must be a number, but got: " + val) 97 } 98 } 99 100 return v * uint64(mib) 101 }() 102 103 // Reporter is a component that gives information about the memory usage. 104 type Reporter interface { 105 // MaxMemory returns the maximum number of memory allowed in bytes. 106 MaxMemory() uint64 107 // UsedMemory returns the memory in use in bytes. 108 UsedMemory() uint64 109 } 110 111 // ProcessMemory is a reporter for the memory used by the process and the 112 // maximum amount of memory allowed controlled by the MAX_MEMORY environment 113 // variable. 114 var ProcessMemory Reporter = new(processReporter) 115 116 type processReporter struct{} 117 118 func (processReporter) UsedMemory() uint64 { 119 var s runtime.MemStats 120 runtime.ReadMemStats(&s) 121 return s.HeapInuse + s.StackInuse 122 } 123 124 func (processReporter) MaxMemory() uint64 { return maxMemory } 125 126 // HasAvailableMemory reports whether more memory is available to the program if 127 // it hasn't reached the max memory limit. 128 func HasAvailableMemory(r Reporter) bool { 129 maxMemory := r.MaxMemory() 130 if maxMemory == 0 { 131 return true 132 } 133 134 return r.UsedMemory() < maxMemory 135 } 136 137 // MemoryManager is in charge of keeping track and managing all the components that operate 138 // in memory. There should only be one instance of a memory manager running at the 139 // same time in each process. 140 type MemoryManager struct { 141 mu sync.RWMutex 142 reporter Reporter 143 caches map[uint64]Disposable 144 token uint64 145 } 146 147 // NewMemoryManager creates a new manager with the given memory reporter. If nil is given, 148 // then the Process reporter will be used by default. 149 func NewMemoryManager(r Reporter) *MemoryManager { 150 if r == nil { 151 r = ProcessMemory 152 } 153 154 return &MemoryManager{ 155 reporter: r, 156 caches: make(map[uint64]Disposable), 157 } 158 } 159 160 // HasAvailable reports whether the memory manager has any available memory. 161 func (m *MemoryManager) HasAvailable() bool { 162 return HasAvailableMemory(m.reporter) 163 } 164 165 // DisposeFunc is a function to completely erase a cache and remove it from the manager. 166 type DisposeFunc func() 167 168 // NewLRUCache returns an empty LRU cache and a function to dispose it when it's 169 // no longer needed. 170 func (m *MemoryManager) NewLRUCache(size uint) (KeyValueCache, DisposeFunc) { 171 c := newLRUCache(m, m.reporter, size) 172 pos := m.addCache(c) 173 return c, func() { 174 c.Dispose() 175 m.removeCache(pos) 176 } 177 } 178 179 // NewHistoryCache returns an empty history cache and a function to dispose it when it's 180 // no longer needed. 181 func (m *MemoryManager) NewHistoryCache() (KeyValueCache, DisposeFunc) { 182 c := newHistoryCache(m, m.reporter) 183 pos := m.addCache(c) 184 return c, func() { 185 c.Dispose() 186 m.removeCache(pos) 187 } 188 } 189 190 // NewRowsCache returns an empty rows cache and a function to dispose it when it's 191 // no longer needed. 192 func (m *MemoryManager) NewRowsCache() (RowsCache, DisposeFunc) { 193 c := newRowsCache(m, m.reporter) 194 pos := m.addCache(c) 195 return c, func() { 196 c.Dispose() 197 m.removeCache(pos) 198 } 199 } 200 201 // NewRowsCache returns an empty rows cache and a function to dispose it when it's 202 // no longer needed. 203 func (m *MemoryManager) NewRows2Cache() (Rows2Cache, DisposeFunc) { 204 c := newRowsCache(m, m.reporter) 205 pos := m.addCache(c) 206 return c, func() { 207 c.Dispose() 208 m.removeCache(pos) 209 } 210 } 211 212 func (m *MemoryManager) addCache(c Disposable) (pos uint64) { 213 m.mu.Lock() 214 defer m.mu.Unlock() 215 m.token++ 216 m.caches[m.token] = c 217 return m.token 218 } 219 220 func (m *MemoryManager) removeCache(pos uint64) { 221 m.mu.Lock() 222 defer m.mu.Unlock() 223 delete(m.caches, pos) 224 225 if len(m.caches) == 0 { 226 m.token = 0 227 } 228 } 229 230 // Free the memory of all freeable caches. 231 func (m *MemoryManager) Free() { 232 m.mu.RLock() 233 defer m.mu.RUnlock() 234 235 for _, c := range m.caches { 236 if f, ok := c.(Freeable); ok { 237 f.Free() 238 } 239 } 240 } 241 242 func (m *MemoryManager) NumCaches() int { 243 m.mu.RLock() 244 defer m.mu.RUnlock() 245 return len(m.caches) 246 }