Files
SugarDB/internal/eviction/lfu.go

113 lines
2.8 KiB
Go

// Copyright 2024 Kelvin Clement Mwinuka
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package eviction
import (
"container/heap"
"slices"
"time"
)
type EntryLFU struct {
key string // The key, matching the key in the store
count int // The number of times this key has been accessed
addedTime int64 // The time this entry was added to the cache in unix milliseconds
index int // The index of the entry in the heap
}
type CacheLFU struct {
keys map[string]bool
entries []*EntryLFU
}
func NewCacheLFU() CacheLFU {
cache := CacheLFU{
keys: make(map[string]bool),
entries: make([]*EntryLFU, 0),
}
heap.Init(&cache)
return cache
}
func (cache *CacheLFU) Len() int {
return len(cache.entries)
}
func (cache *CacheLFU) Less(i, j int) bool {
// If 2 entries have the same count, swap using addedTime
if cache.entries[i].count == cache.entries[j].count {
return cache.entries[i].addedTime > cache.entries[j].addedTime
}
// Otherwise, swap using count
return cache.entries[i].count < cache.entries[j].count
}
func (cache *CacheLFU) Swap(i, j int) {
cache.entries[i], cache.entries[j] = cache.entries[j], cache.entries[i]
cache.entries[i].index = i
cache.entries[j].index = j
}
func (cache *CacheLFU) Push(key any) {
n := len(cache.entries)
cache.entries = append(cache.entries, &EntryLFU{
key: key.(string),
count: 1,
addedTime: time.Now().UnixMilli(),
index: n,
})
cache.keys[key.(string)] = true
}
func (cache *CacheLFU) Pop() any {
old := cache.entries
n := len(old)
entry := old[n-1]
old[n-1] = nil
entry.index = -1
cache.entries = old[0 : n-1]
delete(cache.keys, entry.key)
return entry.key
}
func (cache *CacheLFU) Update(key string) {
// If the key is not contained in the cache, push it.
if !cache.contains(key) {
heap.Push(cache, key)
return
}
// Get the item with key
entryIdx := slices.IndexFunc(cache.entries, func(e *EntryLFU) bool {
return e.key == key
})
entry := cache.entries[entryIdx]
entry.count += 1
heap.Fix(cache, entryIdx)
}
func (cache *CacheLFU) Delete(key string) {
entryIdx := slices.IndexFunc(cache.entries, func(entry *EntryLFU) bool {
return entry.key == key
})
if entryIdx > -1 {
heap.Remove(cache, cache.entries[entryIdx].index)
}
}
func (cache *CacheLFU) contains(key string) bool {
_, ok := cache.keys[key]
return ok
}