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  }