Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: hashicorp/golang-lru
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.5.4
Choose a base ref
...
head repository: hashicorp/golang-lru
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.6.0
Choose a head ref
  • 9 commits
  • 14 files changed
  • 6 contributors

Commits on May 11, 2020

  1. Code quality improvements (#67)

    * add golangci-lint configuration, fix discovered issues
    
    * add GitHub Actions test and build and linter run
    paskal authored May 11, 2020

    Unverified

    This user has not yet uploaded their public signing key.
    Copy the full SHA
    eb52994 View commit details

Commits on Jan 4, 2021

  1. rework lru's external registered callback invocation to avoid concurr… (

    #80)
    
    * rework lru's external registered callback invocation to avoid concurrency issue such as possible deadlock
    
    * fix lint issues
    yigongliu-concur authored Jan 4, 2021

    Unverified

    This user has not yet uploaded their public signing key.
    Copy the full SHA
    80c9821 View commit details

Commits on Oct 11, 2022

  1. Copy the full SHA
    003b81b View commit details
  2. Copy the full SHA
    d4900dc View commit details

Commits on Oct 21, 2022

  1. Update MPL 2.0 LICENSE (#110)

    Co-authored-by: hashicorp-copywrite[bot] <noreply@hashicorp.com>
    hashicorp-copywrite[bot] and hashicorp-copywrite[bot] authored Oct 21, 2022
    Copy the full SHA
    6da3f98 View commit details

Commits on Oct 29, 2022

  1. Update github workflow

    jefferai committed Oct 29, 2022
    Copy the full SHA
    fa11c6b View commit details
  2. Copy the full SHA
    f2408b3 View commit details
  3. Copy the full SHA
    bc1608d View commit details

Commits on Oct 31, 2022

  1. Remove deprecated linters

    jefferai committed Oct 31, 2022
    Copy the full SHA
    bdf35e3 View commit details
Showing with 205 additions and 47 deletions.
  1. +32 −0 .github/workflows/ci.yml
  2. +30 −0 .golangci.yml
  3. +1 −2 2q.go
  4. +5 −6 2q_test.go
  5. +2 −0 LICENSE
  6. +1 −1 README.md
  7. +0 −1 arc.go
  8. +5 −5 arc_test.go
  9. +100 −19 lru.go
  10. +5 −6 lru_test.go
  11. +3 −3 simplelru/lru.go
  12. +3 −2 simplelru/lru_interface.go
  13. +2 −2 simplelru/lru_test.go
  14. +16 −0 testing.go
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: build

on:
push:
branches:
tags:
pull_request:

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: set up go 1.19
uses: actions/setup-go@v1
with:
go-version: 1.19
id: go

- name: checkout
uses: actions/checkout@v2

- name: build and test
run: |
go test -timeout=60s -race
go build -race
- name: install golangci-lint
run: curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $GITHUB_WORKSPACE v1.50.1

- name: run golangci-lint
run: $GITHUB_WORKSPACE/golangci-lint run --out-format=github-actions
30 changes: 30 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
linters:
enable:
- megacheck
- revive
- govet
- unconvert
- megacheck
- gas
- gocyclo
- dupl
- misspell
- unparam
- unused
- typecheck
- ineffassign
- stylecheck
- exportloopref
- gocritic
- nakedret
- gosimple
- prealloc
fast: false
disable-all: true

issues:
exclude-rules:
- path: _test\.go
linters:
- dupl
exclude-use-default: false
3 changes: 1 addition & 2 deletions 2q.go
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ func New2Q(size int) (*TwoQueueCache, error) {

// New2QParams creates a new TwoQueueCache using the provided
// parameter values.
func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCache, error) {
func New2QParams(size int, recentRatio, ghostRatio float64) (*TwoQueueCache, error) {
if size <= 0 {
return nil, fmt.Errorf("invalid size")
}
@@ -138,7 +138,6 @@ func (c *TwoQueueCache) Add(key, value interface{}) {
// Add to the recently seen list
c.ensureSpace(false)
c.recent.Add(key, value)
return
}

// ensureSpace is used to ensure we have space in the cache
11 changes: 5 additions & 6 deletions 2q_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package lru

import (
"math/rand"
"testing"
)

@@ -13,7 +12,7 @@ func Benchmark2Q_Rand(b *testing.B) {

trace := make([]int64, b.N*2)
for i := 0; i < b.N*2; i++ {
trace[i] = rand.Int63() % 32768
trace[i] = getRand(b) % 32768
}

b.ResetTimer()
@@ -43,9 +42,9 @@ func Benchmark2Q_Freq(b *testing.B) {
trace := make([]int64, b.N*2)
for i := 0; i < b.N*2; i++ {
if i%2 == 0 {
trace[i] = rand.Int63() % 16384
trace[i] = getRand(b) % 16384
} else {
trace[i] = rand.Int63() % 32768
trace[i] = getRand(b) % 32768
}
}

@@ -75,8 +74,8 @@ func Test2Q_RandomOps(t *testing.T) {

n := 200000
for i := 0; i < n; i++ {
key := rand.Int63() % 512
r := rand.Int63()
key := getRand(t) % 512
r := getRand(t)
switch r % 3 {
case 0:
l.Add(key, key)
2 changes: 2 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
Copyright (c) 2014 HashiCorp, Inc.

Mozilla Public License, version 2.0

1. Definitions
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ thread safe LRU cache. It is based on the cache in Groupcache.
Documentation
=============

Full docs are available on [Godoc](http://godoc.org/github.com/hashicorp/golang-lru)
Full docs are available on [Godoc](https://pkg.go.dev/github.com/hashicorp/golang-lru)

Example
=======
1 change: 0 additions & 1 deletion arc.go
Original file line number Diff line number Diff line change
@@ -173,7 +173,6 @@ func (c *ARCCache) Add(key, value interface{}) {

// Add to the recently seen list
c.t1.Add(key, value)
return
}

// replace is used to adaptively evict from either T1 or T2
10 changes: 5 additions & 5 deletions arc_test.go
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ func BenchmarkARC_Rand(b *testing.B) {

trace := make([]int64, b.N*2)
for i := 0; i < b.N*2; i++ {
trace[i] = rand.Int63() % 32768
trace[i] = getRand(b) % 32768
}

b.ResetTimer()
@@ -48,9 +48,9 @@ func BenchmarkARC_Freq(b *testing.B) {
trace := make([]int64, b.N*2)
for i := 0; i < b.N*2; i++ {
if i%2 == 0 {
trace[i] = rand.Int63() % 16384
trace[i] = getRand(b) % 16384
} else {
trace[i] = rand.Int63() % 32768
trace[i] = getRand(b) % 32768
}
}

@@ -80,8 +80,8 @@ func TestARC_RandomOps(t *testing.T) {

n := 200000
for i := 0; i < n; i++ {
key := rand.Int63() % 512
r := rand.Int63()
key := getRand(t) % 512
r := getRand(t)
switch r % 3 {
case 0:
l.Add(key, key)
119 changes: 100 additions & 19 deletions lru.go
Original file line number Diff line number Diff line change
@@ -6,10 +6,17 @@ import (
"github.com/hashicorp/golang-lru/simplelru"
)

const (
// DefaultEvictedBufferSize defines the default buffer size to store evicted key/val
DefaultEvictedBufferSize = 16
)

// Cache is a thread-safe fixed size LRU cache.
type Cache struct {
lru simplelru.LRUCache
lock sync.RWMutex
lru *simplelru.LRU
evictedKeys, evictedVals []interface{}
onEvictedCB func(k, v interface{})
lock sync.RWMutex
}

// New creates an LRU of the given size.
@@ -19,30 +26,63 @@ func New(size int) (*Cache, error) {

// NewWithEvict constructs a fixed size cache with the given eviction
// callback.
func NewWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) {
lru, err := simplelru.NewLRU(size, simplelru.EvictCallback(onEvicted))
if err != nil {
return nil, err
func NewWithEvict(size int, onEvicted func(key, value interface{})) (c *Cache, err error) {
// create a cache with default settings
c = &Cache{
onEvictedCB: onEvicted,
}
c := &Cache{
lru: lru,
if onEvicted != nil {
c.initEvictBuffers()
onEvicted = c.onEvicted
}
return c, nil
c.lru, err = simplelru.NewLRU(size, onEvicted)
return
}

func (c *Cache) initEvictBuffers() {
c.evictedKeys = make([]interface{}, 0, DefaultEvictedBufferSize)
c.evictedVals = make([]interface{}, 0, DefaultEvictedBufferSize)
}

// onEvicted save evicted key/val and sent in externally registered callback
// outside of critical section
func (c *Cache) onEvicted(k, v interface{}) {
c.evictedKeys = append(c.evictedKeys, k)
c.evictedVals = append(c.evictedVals, v)
}

// Purge is used to completely clear the cache.
func (c *Cache) Purge() {
var ks, vs []interface{}
c.lock.Lock()
c.lru.Purge()
if c.onEvictedCB != nil && len(c.evictedKeys) > 0 {
ks, vs = c.evictedKeys, c.evictedVals
c.initEvictBuffers()
}
c.lock.Unlock()
// invoke callback outside of critical section
if c.onEvictedCB != nil {
for i := 0; i < len(ks); i++ {
c.onEvictedCB(ks[i], vs[i])
}
}
}

// Add adds a value to the cache. Returns true if an eviction occurred.
func (c *Cache) Add(key, value interface{}) (evicted bool) {
var k, v interface{}
c.lock.Lock()
evicted = c.lru.Add(key, value)
if c.onEvictedCB != nil && evicted {
k, v = c.evictedKeys[0], c.evictedVals[0]
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
}
c.lock.Unlock()
return evicted
if c.onEvictedCB != nil && evicted {
c.onEvictedCB(k, v)
}
return
}

// Get looks up a key's value from the cache.
@@ -75,61 +115,102 @@ func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) {
// recent-ness or deleting it for being stale, and if not, adds the value.
// Returns whether found and whether an eviction occurred.
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) {
var k, v interface{}
c.lock.Lock()
defer c.lock.Unlock()

if c.lru.Contains(key) {
c.lock.Unlock()
return true, false
}
evicted = c.lru.Add(key, value)
if c.onEvictedCB != nil && evicted {
k, v = c.evictedKeys[0], c.evictedVals[0]
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
}
c.lock.Unlock()
if c.onEvictedCB != nil && evicted {
c.onEvictedCB(k, v)
}
return false, evicted
}

// PeekOrAdd checks if a key is in the cache without updating the
// recent-ness or deleting it for being stale, and if not, adds the value.
// Returns whether found and whether an eviction occurred.
func (c *Cache) PeekOrAdd(key, value interface{}) (previous interface{}, ok, evicted bool) {
var k, v interface{}
c.lock.Lock()
defer c.lock.Unlock()

previous, ok = c.lru.Peek(key)
if ok {
c.lock.Unlock()
return previous, true, false
}

evicted = c.lru.Add(key, value)
if c.onEvictedCB != nil && evicted {
k, v = c.evictedKeys[0], c.evictedVals[0]
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
}
c.lock.Unlock()
if c.onEvictedCB != nil && evicted {
c.onEvictedCB(k, v)
}
return nil, false, evicted
}

// Remove removes the provided key from the cache.
func (c *Cache) Remove(key interface{}) (present bool) {
var k, v interface{}
c.lock.Lock()
present = c.lru.Remove(key)
if c.onEvictedCB != nil && present {
k, v = c.evictedKeys[0], c.evictedVals[0]
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
}
c.lock.Unlock()
if c.onEvictedCB != nil && present {
c.onEvictedCB(k, v)
}
return
}

// Resize changes the cache size.
func (c *Cache) Resize(size int) (evicted int) {
var ks, vs []interface{}
c.lock.Lock()
evicted = c.lru.Resize(size)
if c.onEvictedCB != nil && evicted > 0 {
ks, vs = c.evictedKeys, c.evictedVals
c.initEvictBuffers()
}
c.lock.Unlock()
if c.onEvictedCB != nil && evicted > 0 {
for i := 0; i < len(ks); i++ {
c.onEvictedCB(ks[i], vs[i])
}
}
return evicted
}

// RemoveOldest removes the oldest item from the cache.
func (c *Cache) RemoveOldest() (key interface{}, value interface{}, ok bool) {
func (c *Cache) RemoveOldest() (key, value interface{}, ok bool) {
var k, v interface{}
c.lock.Lock()
key, value, ok = c.lru.RemoveOldest()
if c.onEvictedCB != nil && ok {
k, v = c.evictedKeys[0], c.evictedVals[0]
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
}
c.lock.Unlock()
if c.onEvictedCB != nil && ok {
c.onEvictedCB(k, v)
}
return
}

// GetOldest returns the oldest entry
func (c *Cache) GetOldest() (key interface{}, value interface{}, ok bool) {
c.lock.Lock()
func (c *Cache) GetOldest() (key, value interface{}, ok bool) {
c.lock.RLock()
key, value, ok = c.lru.GetOldest()
c.lock.Unlock()
c.lock.RUnlock()
return
}

Loading