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  }